1. 数据劫持(数据代理)
1.1 数据劫持(数据代理):
- 在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果
1.2 数据劫持(数据代理)的2种方式:
- ① Object.defineProperty()
- ② proxy
1.3 Object.defineProperty(obj,key,{属性描述符}) ---> ES5 ---> 只能代理对象的某个属性
-
作用:给对象定义新属性或修改对象的现有属性,并返回对象
-
参数:
-
参数1:obj,操作的对象
-
参数2:key,属性名
-
参数3:{ },属性描述符。可配置项包括:
value:50;设置属性值。默认值undefinedconfigurable:true;属性能否被删除。默认是falsewritable:true;是否能设置属性值。默认是falseenumerable:true;属性是否能遍历。默认是false- 以上4个属性不能和getter,setter一起使用
- get ---> 属性的 getter 函数,获取会调用此函数。默认值undefined
- set ---> 属性的 setter 函数,修改会调用此函数。默认值undefined
-
-
返回值:返回修改后的对象
1.4 new Proxy(target,handler) ---> ES6 ---> 代理整个对象
- 作用:代理整个对象
- 参数:
- 参数1:target,被代理的对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
- 参数2:handler,一个通常以函数作为属性的对象,各属性中的函数定义了代理的行为
【补充】数据劫持
2. Object.prototype.hasOwnProperty() 过滤原型上的属性和方法
3. 闭包
3.1 什么是闭包
-
函数嵌套函数,内部函数可以使用外部函数的参数和变量,并且参数和变量不会被js的垃圾回收机制(gc)回收。
function fn(){ var num = 10; return num; } fn(); console.log(num); // 报错,num未定义 // 报错的原因: // 1. 作用范围超出。函数内部的局部变量,不能超出作用范围访问 // 2. 函数执行完毕之后,函数内部的局部变量被销毁 // 解决问题:闭包 function fn1(){ var num = 20; return function(){ num++; return num; } } var a = fn1(); console.log(a()); // 21 console.log(a()); // 22 var b = fn1(); console.log(b()); // 21
3.2 闭包的优点
- ① 让一个变量长期驻扎在内存当中不被释放
- ② 避免变量全局污染
3.3 闭包的缺点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
- 闭包会在父函数外部,改变父函数内部变量的值。
3.4 闭包的应用
- 实现缓存
- 存储值与避免变量全局污染
- 函数的柯里化
- 节流和防抖
3.5 防抖debounce
防抖:任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行
3.6 节流throttle
节流:指定时间间隔内只会执行一次任务
3.7 函数的柯里化(了解)
4. 递归
4.1 递归
函数自己调用自己,要有临界点
4.2 递归的特点
循环能做的事,递归都能做。但是性能不太好,不推荐使用
4.3 递归的应用
- ① 快速排序
- ② nodejs磁盘文件的遍历
- ③ 管理系统的权限菜单栏
- ④ 对象的深拷贝
5. 拷贝
仅存在于引用类型中。
5.1 2种拷贝:深拷贝和浅拷贝
- 浅拷贝:拷贝的对象(包括子对象),除了第一层对象没有任何关联,有可能第二层对象存在关联。只有第一层引用地址不共享,第二层引用地址共享。
- 深拷贝:拷贝的对象(包括子对象),与原来的对象没有任何关联。所有的引用地址都不共享。
5.2 浅拷贝的方式
5.2.1 数组的浅拷贝:
- ①
arr.slice(0) - ②
arr.concat([]) - ③ 扩展运算符
[...arr] - ④ 使用第三方js库lodash
_.clone(arr)
5.2.2 对象的浅拷贝:
- ①
Object.assign({},obj) - ② 扩展运算符
{...obj} - ③ 使用第三方js库lodash
_.clone(obj)
5.2 深拷贝的方式
-
①
JSON.stringify()+JSON.parse()缺点:会丢失方法
-
② 使用第三方js库lodash
_.cloneDeep(obj) -
③ 利用递归手写深拷贝
function cloneDeep(a){ // 先判断是数组还是对象,再创建新的数组或者对象 let b = Array.isArray(a) ? [] : {}; for(let key in a){ if(typeof a[key] == 'object'){ b[key] = cloneDeep(a[key]); }else{ b[key] = a[key]; } } return b; }
【补充:区分数组和对象的一些方法】
1. Array.isArray() ---> 可靠
let obj = {};
let arr = [];
console.log(Array.isArray(obj)); // false
console.log(Array.isArray(arr)); // true
2. instanceof ---> 不太可靠
原理:通过原型链查找是否有该类型的原型
let obj = {};
let arr = [];
console.log(obj instanceof Object); // true
console.log(arr instanceof Object); // true
console.log(obj instanceof Array); // false
console.log(arr instanceof Array); // true
3. Object.prototype.toString.call() ---> 最可靠
let obj = {};
let arr = [];
console.log(Object.prototype.toString.call(obj)); // [object Object]
console.log(Object.prototype.toString.call(arr)); // [object Array]
// 封装一个判断引用类型具体类型的方法
function checkType(o){
let str1 = Object.prototype.toString.call(o);
let str2 = str1.split(" ")[1];
return str2.slice(0,str2.length-1);
}
6. promise
【知识补充1】
- 回调函数:一个函数作为另一个函数的参数,在另一个函数内部被执行和传递参数。
- 回调函数优点:① 解决异步; ② 扩展函数的功能。
- 缺点:容易造成回调地狱,不方便维护和理解
【知识补充2】
- 同步:按顺序一步一步执行,容易造成阻塞,js是单线程。阻塞:第一步未完成,就不能进行第二步,等待的过程就是阻塞
- 异步:和同步相反
【知识补充3】解决异步的发展史:
- ① 回调函数
- ② 函数生成器 ---> ES5 (已淘汰)
- ③ promise ---> ES6
- ④ promise + async + await ---> ES7
6.1 Promise
是一个构造函数/类,需要被实例化,是一个微任务。
-
① promise在实例化的时候需要传递一个参数,这个参数是一个函数。
-
② new Promise(function(){}) 本身是同步的
-
③ 语法:
let p1 = new Promise((resolve,reject)=>{})【注】resolve,reject都是函数
6.2 promise的3个状态
- ① pending 等待
- ② fulfilled 完成
- ③ rejected 拒绝
- 顺序不可逆,只能pending --> fulfilled,或者pending --> rejected
6.3 promise的3个原型方法
6.3.1 then()
- ① 作用:可以接收resolve和reject的结果
- ② 参数:2个函数,第1个函数接收resolve的结果,第2个函数接收reject的结果。如果第2个函数不写,后面要调用catch()方法捕获reject的结果。
- ③ 返回值:返回一个promise对象
6.3.2 catch()
- ① 作用:捕获reject的结果
- ② 参数:1个函数,捕获reject的结果。
- ③ 返回值:返回一个promise对象
6.3.3 finally()
-
① 作用:执行resolve或reject后,都会执行finally
-
② 参数:无
-
③ 返回值:返回一个promise对象
function getData(){ return new Promise(function(resolve,reject){ resolve('resolve被执行了'); // resolve执行reject就不会执行 reject('reject被执行了'); // reject执行resolve就不会执行 }) } getData().then(function(res){ console.log(res); }).catch(function(res){ console.log(res); }).finally(console.log('无论成功或者失败都会执行finally'))
6.4 promise的4个静态方法
6.4.1 Promise.all() 并发执行
- ① 作用:将多个promise包装成一个promise,并发执行,返回所有的结果
- ② 参数:参数是一个数组,数组成员是promise对象
- ③ 返回值:返回一个promise对象
6.4.2 Promise.race() 返回先完成的结果
- ① 作用:将多个promise包装成一个promise,谁先完成返回谁
- ② 参数:参数是一个数组,数组成员是promise对象
- ③ 返回值:返回一个promise对象
6.4.3 Promise.resolve()
作用:返回状态为fulfilled的promise对象
6.4.4 Promise.reject()
作用:返回状态为rejected的promise对象
6.5 promise解决异步(ES6)
// 1. 回调函数解决异步
function getData(callback){
setTimeout(()=>{
var o = {id:1,name:"小A"};
callback(o);
},5000)
}
getData(function(res){console.log(res)}); // {id: 1, name: '小A'}
// 如果要多次调用,容易造成回调地狱,不方便维护和理解
getData(function(res){
getData(function(res){
getData(function(res){
console.log(res)
})
})
})
// 2. promise解决异步:用promise把异步包裹起来
function getData(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var o = {id:1,name:"小A"};
resolve(o);
},5000)
})
}
getData().then((res)=>{console.log(res)},(res)=>{console.log(res)}); // {id: 1, name: '小A'}
// 如果要多次调用,代码冗余
getData().then(function(res){
return getData();
}).then(function(res){
return getData();
}).then(function(res){
console.log(res);
})
6.6 promise + async + await 解决异步(ES7)
-
await 必须放在
异步函数中。函数前加async就是异步函数 -
await 后面必须是一个
promise对象
【注】onsubmit事件不能和 async await 使用
function getData(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var o = {id:1,name:"小A"};
resolve(o);
},5000)
})
}
async function getA(){
var res = await getData();
}