开发中常用的JS知识点集锦
快捷导航菜单
1、对象的深拷贝(一级属性拷贝和多级属性嵌套拷贝)
ES6的Object.assign函数,和{...xxx}扩展运算符能够实现浅拷贝。JSON.parse(JSON.stringify(xxx))能够实现深拷贝,但是针对要拷贝的属性值如果是函数、时间、undefined、正则表达式以及对象的原型属性和方法都无能为力。因此深拷贝需要自定义函数:
function deepClone(sourceObj){
if (sourceObj instanceof RegExp) return new RegExp(sourceObj); //正则类型
if (sourceObj instanceof Date) return new Date(sourceObj); //时间类型
if (sourceObj === null || typeof sourceObj !== 'object'){ //基础类型
return sourceObj;
} //其他引用类型数据, 找出当前数据类型的构造函数,new一个当前类型的空对象
var obj = new sourceObj.constructor();
for (var key in sourceObj){
//属于自己的属性才递归深拷贝
if (sourceObj.hasOwnProperty(key)){
obj[key] = deepClone(sourceObj[key]);
}
}
return obj;
}
测试代码:
//测试
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.myInfo = function(){
console.log("my name is ", this.name, ", age is ", this.age);
} var obj = {
fn: Person, //函数
obj: new Person("王大锤", 18), //对象
time: new Date("2018-08-18 18:18"), //时间
reg: new RegExp('abc'), //正则
sign: undefined,
arr: ['etf', 99, true]
}
var obj2 = Object.assign({}, obj); //浅拷贝(ES6的Object.assign函数)
var obj3 = {...obj}; //浅拷贝(ES6扩展运算符)
//JSON.stringify深拷贝所有层级属性,但是针对属性值为函数,正则表达式,时间,undefined, 原型对象的方法和属性无能为力
var obj4 = JSON.parse(JSON.stringify(obj));
var obj5 = deepClone(obj); console.log("\n**************start*************")
console.log("obj: ", obj, "\n\nobj2: ", obj2, "\n\nobj3: ", obj3, "\n\nobj4: \n", obj4, "\n\nobj5: ", obj5); obj.obj.name = '罗小虎', obj.obj.age = 27;
obj.arr.push("玉娇龙");
console.log("............update.........")
console.log("obj: ", obj, "\n\nobj2: ", obj2, "\n\nobj3: ", obj3, "\n\nobj4: \n", obj4, "\n\nobj5: ", obj5);
测试结果截图对比一下,发现使用JSON.stringify深拷贝的对象,属性值为函数和undefined的属性直接过滤不见了,然后正则表达式变成了空对象{}, 时间变成了字符串等:
2、网络图片转成base64, 在线图片或文件点击下载(隐藏链接)
<div>
<div onclick="clickMeDownload()">点我下载</div> <script type="text/javascript">
/**
* 根据远程图片转成base64数据 (远程图片和当前页面不是同一域名时,需要进行web服务器配置,使其可以跨域下载)
* @param url 图片链接
* @param callback 回调函数
*/
function getBase64ByImgUrl(url, callback){
let canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function(){
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img,0,0); //获取base64数据
let base64 = canvas.toDataURL('image/png');
//回调
if (callback){
callback(base64);
}
canvas = null;
}
img.src = url;
} /**
* 把base64转成文件流
* @param base64 base64数据
* @param filename 自定义文件的名字
*/
function getFileByBase64(base64, filename){ let arr = base64.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n--){
u8arr[n] = bstr.charCodeAt(n);
} return new File([u8arr], filename, {type:mime});
} /**
* 测试例子:点击下载,隐藏下载链接
*/
function clickMeDownload(){ let imgUrl = 'https://img2018.cnblogs.com/blog/454511/201811/454511-20181114115022054-611805083.png'; getBase64ByImgUrl(imgUrl, function(base64){
console.log(base64); //创建a标签, 设置a标签的href属性和download属性
var aEle = document.createElement('a');
aEle.setAttribute('href', base64);
aEle.setAttribute('download', 'temp.png');
aEle.style.display = 'none'; //隐藏a标签
document.body.appendChild(aEle); //将a标签添加到body里
aEle.click(); //触发a标签点击事件 document.body.removeChild(aEle); //下载图片后,移除a标签 });
}
</script>
</div>
3、常用CSS样式记录
3.1 给定标签宽高,内容超出限制宽高后,以省略点展示,示例:
<div class="text-box">宽度500px, 高度50px,超出高度后后缀...展示。这俩人是现今世上,手段最高明的摸金校尉,都有万夫不挡之勇,神鬼莫测之机,兼有云长之忠,翼德之猛,子龙之勇,孔明之智,那面古镜一定就是他们从云南掏出来的。</div>
/* 6、超出限制宽高后省略点展示
多行后显示省略点,只应用于-webkit内核; 移动端浏览器基本都是WebKit内核的,所以该样式适用于移动端
*/
.text-box{
margin-top: 20px; width: 500px; height: 50px; border:1px dashed #999; color: #999; line-height: 25px;
overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
-webkit-line-clamp:; /* 这个是设置最多展示几行 */
-webkit-box-orient: vertical; word-break:break-all;
}
3.2 一个标签,配合before和after属性,设置出外圆边框,内三角形图标
<span class="video-icon"></span>
/* 5、视屏图标icon */
.video-icon { display: inline-block; width: 30px; height: 30px; border: 2px solid blue; border-radius: 30px; position: relative; cursor: pointer; }
.video-icon:after {
position: absolute; left: 12px; top: 7px; content: '';
border-width:8px 0 8px 10px; border-style:solid;
border-color:transparent transparent transparent blue;
}
3.3 单选切换:两个按钮,只能选中一个,左边按钮的左边和右边按钮的右边圆角
<div class="grade_box"><div class="grade_first grade_sel">一级分类</div><div class="grade_second">二级分类</div></div>
<script type="text/javascript">
var gradeList = (document.getElementsByClassName('grade_box')[0]).children; for (var i = 0; i < gradeList.length; i++){
var obj = gradeList[i]; obj.setAttribute('index', i); obj.onclick = function(){
var index = this.getAttribute('index'); console.log(this); if (index == 0){
gradeList[0].className = 'grade_first grade_sel';
gradeList[1].className = 'grade_second';
}
else{
gradeList[0].className = 'grade_first';
gradeList[1].className = 'grade_second grade_sel';
}
}
}
</script>
.grade_box { width: 300px; height: 40px; }
.grade_box div {
display: inline-block; height: 100%; width: 50%; line-height: 40px; text-align: center; background-color: #f4f4f4; color: #ff7800; border: 1px solid #ff7800; box-sizing: border-box; cursor: pointer;
}
.grade_box .grade_sel { background-color: #ff7800; color: #fff; }
.grade_box .grade_first { border-top-left-radius: 40px; border-bottom-left-radius: 40px; }
.grade_box .grade_second { border-top-right-radius: 40px; border-bottom-right-radius: 40px; }
3.4 按钮背景颜色渐变
<div class="bg_change_btn">4、按钮背景颜色渐变</div>
.bg_change_btn{
width: 200px; height: 50px; line-height: 50px; border-radius: 50px; margin-top: 30px; text-align: center;
background-image: linear-gradient(-45deg, orange 0%, yellow 100%), linear-gradient( green, black); cursor: pointer;
}
4、JS中某些数字加减乘除会出现多位小数点现象,精度丢失
举例:
console.log("0.1+0.2 = ", 0.1+0.2); //0.1+0.2 = 0.30000000000000004
console.log("0.1+0.7 = ", 0.1+0.7); //0.1+0.7 = 0.7999999999999999
console.log("0.2+0.7 = ", 0.2+0.7) //0.2+0.7 = 0.8999999999999999
console.log("1.1+0.3 = ", 1.1+0.3) //1.1+0.3 = 1.4000000000000001 console.log("1.7-0.1 = ", 1.7-1.1); //1.7-0.1 = 0.5999999999999999
console.log("1.7-1.2 = ", 1.7-1.3); //1.7-1.2 = 0.3999999999999999
console.log("1.7-1.4 = ", 1.7-1.4); //1.7-1.4 = 0.30000000000000004 console.log("1.10*100 =", 1.10*100); //1.10*100 = 110.00000000000001
console.log("1.11*100 =", 1.11*100); //1.11*100 = 111.00000000000001
console.log("1.12*100 =", 1.12*100); //1.12*100 = 112.00000000000001
console.log("1.13*100 =", 1.13*100); //1.13*100 = 112.99999999999999
console.log("1.14*100 =", 1.14*100); //1.14*100 = 113.99999999999999
console.log("1.15*100 =", 1.15*100); //1.15*100 = 114.99999999999999
console.log("1.16*100 =", 1.16*100); //1.16*100 = 115.99999999999999 console.log("0.7/0.1 = ", 0.7/0.1); //0.7/0.1 = 6.999999999999999
console.log("0.6/0.1 = ", 0.6/0.1); //0.6/0.1 = 5.999999999999999
解决方式1,使用Math.round函数处理,假设数字变量名为num, 格式为:Math.round(num*100) / 100:
console.log("0.1+0.2 = ", Math.round((0.1+0.2)*100)/100); //0.1+0.2 = 0.3
console.log("0.1+0.7 = ", Math.round((0.1+0.7)*100)/100); //0.1+0.7 = 0.8
console.log("0.2+0.7 = ", Math.round((0.2+0.7)*100)/100) //0.2+0.7 = 0.9
console.log("1.1+0.3 = ", Math.round((1.1+0.3)*100)/100) //1.1+0.3 = 1.4 console.log("1.7-0.1 = ", Math.round((1.7-0.1)*100)/100); //1.7-0.1 = 1.6
console.log("1.7-1.2 = ", Math.round((1.7-1.2)*100)/100); //1.7-1.2 = 0.5
console.log("1.7-1.4 = ", Math.round((1.7-1.4)*100)/100); //1.7-1.4 = 0.3 console.log("1.10*100 =", Math.round((1.10*100)*100)/100); //1.10*100 = 110
console.log("1.11*100 =", Math.round((1.11*100)*100)/100); //1.11*100 = 111
console.log("1.12*100 =", Math.round((1.12*100)*100)/100); //1.12*100 = 112
console.log("1.13*100 =", Math.round((1.13*100)*100)/100); //1.13*100 = 113
console.log("1.14*100 =", Math.round((1.14*100)*100)/100); //1.14*100 = 114
console.log("1.15*100 =", Math.round((1.15*100)*100)/100); //1.15*100 = 115
console.log("1.16*100 =", Math.round((1.16*100)*100)/100); //1.16*100 = 116 console.log("0.7/0.1 = ", Math.round((0.7/0.1)*100)/100); //0.7/0.1 = 7
console.log("0.6/0.1 = ", Math.round((0.6/0.1)*100)/100); //0.6/0.1 = 6
解决方式2:使用parseInt函数,假设数字变量名为num, 格式为:parseInt(num*100 + 0.1)/100
console.log("0.1+0.2 = ", parseInt((0.1+0.2)*100+0.1)/100); //0.1+0.2 = 0.3
console.log("0.1+0.7 = ", parseInt((0.1+0.7)*100+0.1)/100); //0.1+0.7 = 0.8
console.log("0.2+0.7 = ", parseInt((0.2+0.7)*100+0.1)/100) //0.2+0.7 = 0.9
console.log("1.1+0.3 = ", parseInt((1.1+0.3)*100+0.1)/100) //1.1+0.3 = 1.4 console.log("1.7-0.1 = ", parseInt((1.7-0.1)*100+0.1)/100); //1.7-0.1 = 1.6
console.log("1.7-1.2 = ", parseInt((1.7-1.2)*100+0.1)/100); //1.7-1.2 = 0.5
console.log("1.7-1.4 = ", parseInt((1.7-1.4)*100+0.1)/100); //1.7-1.4 = 0.3 console.log("1.10*100 =", parseInt((1.10*100)*100+0.1)/100); //1.10*100 = 110
console.log("1.11*100 =", parseInt((1.11*100)*100+0.1)/100); //1.11*100 = 111
console.log("1.12*100 =", parseInt((1.12*100)*100+0.1)/100); //1.12*100 = 112
console.log("1.13*100 =", parseInt((1.13*100)*100+0.1)/100); //1.13*100 = 113
console.log("1.14*100 =", parseInt((1.14*100)*100+0.1)/100); //1.14*100 = 114
console.log("1.15*100 =", parseInt((1.15*100)*100+0.1)/100); //1.15*100 = 115
console.log("1.16*100 =", parseInt((1.16*100)*100+0.1)/100); //1.16*100 = 116 console.log("0.7/0.1 = ", parseInt((0.7/0.1)*100+0.1)/100); //0.7/0.1 = 7
console.log("0.6/0.1 = ", parseInt((0.6/0.1)*100+0.1)/100); //0.6/0.1 = 6
5、call、apply和bind使用方法
call、apply、bind的存在都是为了可以改变函数运行时的“上下文”,也就是函数的this对象指向。使用格式为:
- call使用格式:函数.call(上下文, 参数1, 参数2, ...) == fn.call(obj, arg1, arg2, ....)
- apply使用格式(参数写在数组里):函数.apply(上下文, [参数1, 参数2, ...]) == fn.apply(obj, [arg1, arg2, ...])
- bind使用格式:函数.bind(上下文, 参数1, 参数2, ...)() == 函数.bind(上下文)(参数1, 参数2, ...) == 函数.bind(上下文, 参数1)(参数2, ...)
说明:这三个方法的第一个参数都是this要指向的对象,第二个及后面的参数就是要调用函数的参数。call和apply是立即调用,而bind方法会创建一个新函数(称为绑定函数),所以bind方法使用时还需要再调用一下。下面看一些例子:
var obj = {
name: "王大锤",
sayName: function(){
console.log("我是" + this.name);
}
}
var obj2 = { name: "罗小虎" } obj.sayName(); //我是王大锤
obj.sayName.call(obj2); //我是罗小虎
obj.sayName.apply(obj2); //我是罗小虎
obj.sayName.bind(obj2)(); //我是罗小虎
var obj = {
name: "王大锤",
sayName: function(age, sex){
console.log("我是" + this.name + ", 年龄" + age + ", 性别" + sex);
}
}
var obj2 = { name: "玉娇龙" } obj.sayName(27, "男"); //我是王大锤, 年龄27, 性别男
obj.sayName.call(obj2, 18, "女"); //我是玉娇龙, 年龄18, 性别女
obj.sayName.apply(obj2, [19, "女"]); //我是玉娇龙, 年龄19, 性别女
obj.sayName.bind(obj2)(20, "女"); //我是玉娇龙, 年龄20, 性别女
obj.sayName.bind(obj2, 21)("女"); //我是玉娇龙, 年龄21, 性别女
obj.sayName.bind(obj2, 22, "女")(); ////我是玉娇龙, 年龄22, 性别女
将伪数组转成数组,Object对象本来没有slice函数,但是Array有。然后可以通过call、apply、bind将Array的slice函数指向Object对象,让Object对象可以调用slice函数:
//将伪数组转成数组
var obj = { 0:99, 1: '王大锤', 2:true, 3: 0.27, 4: 'hello', length: 5 }
console.log( Array.prototype.slice.call(obj) ); //[99, "王大锤", true, 0.27, "hello"]
console.log( Array.prototype.slice.call(obj, 1, 3) ); //["王大锤", true]
console.log( Array.prototype.slice.apply(obj) ); //[99, "王大锤", true, 0.27, "hello"]
console.log( Array.prototype.slice.apply(obj, [0, 2]) ); //[99, "王大锤"]
console.log( Array.prototype.slice.bind(obj)() ); //[99, "王大锤", true, 0.27, "hello"]
console.log( Array.prototype.slice.bind(obj)(0, 3) ); //[99, "王大锤", true]
//获取数组中的最大值和最小值
console.log( Math.max.call(null, 11, 99, 22, 33) ); //
console.log( Math.min.call(null, 11, 99, 22, 33) ); //
console.log( Math.max.apply(null, [11, 99, 22, 33]) ); //
console.log( Math.min.apply(null, [11, 99, 22, 33]) ); //
console.log( Math.max.bind(null)(11, 99, 22, 33) ); //
console.log( Math.min.bind(null)(11, 99, 22, 33) ); //
6、定义类和继承的标准实现方式
6.1 定义类的方式有多种:工厂方式、构造函数方式、原型方式、混合的构造函数+原型方式、动态原型方法。目前使用最广泛的是混合的构造函数+原型方式。此外,动态原型方法也很留行,在功能上与混合的构造函数+原型方式等价。
混合的构造函数+原型方式(即用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(即方法))示例代码:
function Car(sColor,iDoors,iMpg) {
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.drivers = new Array("Mike","John");
} Car.prototype.showColor = function() {
alert(this.color);
}; var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",3,25); oCar1.drivers.push("Bill"); alert(oCar1.drivers); //Mike,John,Bill
alert(oCar2.drivers); //Mike,John
动态原型方法的基本想法和混合的构造函数+原型方式相同,即在构造函数内定义非函数属性,而函数属性则利用原型属性定义。示例代码:
function Car(sColor,iDoors,iMpg) {
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.drivers = new Array("Mike","John"); if (typeof Car._initialized == "undefined") {
Car.prototype.showColor = function() {
alert(this.color);
}; Car._initialized = true;
}
}
应用示例:
JavaScript的字符串是不可变的,即它们的值不能改变,请考虑下面的代码:
var str = "hello ";
str += "world";
实际上,这段代码在幕后执行的步骤如下:
- 创建存储 "hello" 的字符串。
- 创建存储 "world" 的字符串。
- 创建存储连接结果的字符串。
- 把str的当前内容复制到结果中。
- 把 "world" 复制到结果中。
- 更新str,使它指向结果。
每次完成字符串都会执行步骤2到6,使用这种操作非常消耗资源。如果重复这一过程几百次,甚至几千次,就会造成性能问题。解决办法使用Array对象存储字符串,然后用join()方法(参数是空字符串)创建最后的字符串,示例代码:
function StringBuffer () {
this._strings_ = new Array();
} StringBuffer.prototype.append = function(str) {
this._strings_.push(str);
}; StringBuffer.prototype.toString = function() {
return this._strings_.join("");
}; //测试演示
var buffer = new StringBuffer ();
buffer.append("hello ");
buffer.append("world"); console.log( buffer.toString() ); //hello world
6.2 JavaScript的继承机制实现也有多种方式:对象冒充、call//apply方法、原型链、混合方式。最好的方式是使用混合方式,我们知道创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法,示例代码:
function ClassA(sColor) {
this.color = sColor;
} ClassA.prototype.sayColor = function () {
alert(this.color);
}; function ClassB(sColor, sName) {
ClassA.call(this, sColor);
this.name = sName;
} ClassB.prototype = new ClassA();
ClassB.prototype.constructor = ClassB; //修正ClassB原型的构造函数
ClassB.prototype.sayName = function () { alert(this.name); }; //新增一个属于ClassB独有的方法
6.3 ES6新增加的创建类和继承的写法:
class ClassA{
constructor(sColor){
this.color = sColor;
} sayColor(){
console.log('color is ', this.color);
}
} //继承
class ClassB extends ClassA{
constructor(sColor, sName){
super(sColor); //调用父级
this.name = sName;
} //属于ClassB的方法
sayName(){
console.log(this.name, ' color is ', this.color);
}
}
开发中常用的JS知识点集锦的更多相关文章
- Hybrid App 应用开发中 9 个必备知识点复习(WebView / 调试 等)
前言 我们大前端团队内部 ?每周一练 的知识复习计划继续加油,本篇文章是 <Hybrid APP 混合应用专题> 主题的第二期和第三期的合集. 这一期共整理了 10 个问题,和相应的参考答 ...
- JAVA项目中常用的异常知识点总结
JAVA项目中常用的异常知识点总结 1. java.lang.nullpointerexception这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用 ...
- .net开发中常用的第三方组件
.net开发中常用的第三方组件 2013-05-09 09:33:32| 分类: dotnet |举报 |字号 订阅 下载LOFTER 我的照片书 | RSS.NET.dll RSS. ...
- iOS开发中关于UIImage的知识点总结
UIImage是iOS中层级比较高的一个用来加载和绘制图像的一个类,更底层的类还有 CGImage,以及iOS5.0以后新增加的CIImage.今天我们主要聊一聊UIImage的三个属性: image ...
- 依赖注入及AOP简述(十)——Web开发中常用Scope简介 .
1.2. Web开发中常用Scope简介 这里主要介绍基于Servlet的Web开发中常用的Scope. l 第一个比较常用的就是Application级Scope,通常我们会将一 ...
- spring注解开发中常用注解以及简单配置
一.spring注解开发中常用注解以及简单配置 1.为什么要用注解开发:spring的核心是Ioc容器和Aop,对于传统的Ioc编程来说我们需要在spring的配置文件中邪大量的bean来向sprin ...
- Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令
Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...
- 2019-2-20C#开发中常用加密解密方法解析
C#开发中常用加密解密方法解析 一.MD5加密算法 我想这是大家都常听过的算法,可能也用的比较多.那么什么是MD5算法呢?MD5全称是 message-digest algorithm 5[|ˈmes ...
- 开发中常用的es6知识
结合实际开发,开发中常用的es6的知识: 1.新增let和const命令: ES6 新增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效: cons ...
随机推荐
- codeforces-1136 (div2)
A.读到第i章,就有N - i + 1章还没读. #include <map> #include <set> #include <ctime> #include & ...
- Python3下POST请求HTTPS链接
Python 3.5.2 + Windows 7环境下 第一种:http.client方式 def http_client_post(): conn = http.client.HTTPSConnec ...
- AFM论文精读
深度学习在推荐系统的应用(二)中AFM的简单回顾 AFM模型(Attentional Factorization Machine) 模型原始论文 Attentional Factorization M ...
- CentOS7设置ssh服务以及端口修改
很多时候我们都是通过SSH 服务 来对 Linux 进行操作,而不是直接来操作Linux机器,包括对Linux服务器的操作,因此,设置SSH服务对于学习Linux来说属于必备技能(尤其是运维人员),关 ...
- dataTable 实战总结
后台项目中经常会用到 dataTable 进行表格的智能绘制:总结一下项目中经常用到的 dataTable 设置 1.dataTable 属性设置: autoWidth: true, // 是否自动计 ...
- 基于jeesite的cms系统(三):使用RESTful API在前端渲染数据
使用RESTful API可以更好的开发前后分离的应用,后面一节会介绍使用模版引擎Beetl开发后端渲染的应用. 一.配置Swagger(Api 接口文档) 1.使用系统自带 拷贝jeesite-mo ...
- DataTable某一列转换为分隔字符串
//把DataTable的某一列转化为逗号分隔字符串 private static string DataTableColumnSplit(DataTable dataTable,string str ...
- docker学习------registry可视化的实现
1.Harbor的发现 之前一直在使用registry,但docker pull下来的基础镜像并没用携带UI界面,所以查找了一波开源工具,发现了Harbor这一工具,下面对工具进行一个学习. 2.部 ...
- python整数与IP地址转换
python整数与IP地址转换 [转] 我们有时会将一个整数与IP地址进行互换,用python代码实现很简单将一个整数如2000000,变为一个IP地址的方式 >>> import ...
- Navicat连接阿里云服务器Linux下的Mysql
用Navicat连接阿里云ECS服务器上的MySQL数据库 今天用navtive连接阿里云服务器(Linux)的数据库时,老是连接不上,并且报10060错误,要通过以下两个步骤解决: 1.先进入l ...