继续上一讲解
/**
* WeakMap key 接受的是对象,只能是个对象,弱引用不是引用类型的也法用。
* ... 代表的是浅拷贝
* let o1 = {name:1};
* let o2 = {ag3:2};
* let o3 = {...o1, ...o2} 默认只展开对象的第一层 等价于Object.assign,对于深层次的只是拷贝了个地址
* JSON.stringify({a:/\d/, b:undefined}) 转完了就丢掉了,对象里面放个函数也不行
* 用函数深度遍历
* 1.递归
* 2.栈
* 深拷贝
* function deepClone(obj, hash = new WeakMap()){ // 如果里面有就直接返回 hash 如果有直接返回防止循环引用的问题
* if(hash.has(obj)) return hash.get(obj); // 返回上次拷贝的结果 不在递归了
* if(obj == null) return obj;
* if(obj instanceof RegExp) return new RegExp(obj); // 这东西为什么重新new了一个相当于创建了一个新的对象
* if(obj instanceof Date) return new Date(obj);
* if(typeof obj != "object") return obj;
* if(typeof obj == "function") return obj;
* // ....
* // 剩下的就是数组和对象
* // ([]).constructor = Array 可以直接 new ([]).constructor 就new 出来一个空的数组
* // ({}).constructor = Object 同理可以直接new ({}).constructor 就new出来一个空的对象
* // 很巧妙昂不用判断你是数组还是对象直接就new 一个不管你是啥
* let res = new obj.constructor(); // 这个可以当拷贝后的结果,没参数可以直接new obj.constructor
* hash.set(obj, res); // 把复制的直接过用对象关联起来res这玩意是个引用类型放在哪里都一样!!
* for(let i in obj){
* if(obj.hasOwnProperty(i)){ // 不希望拷贝原型链上的方法
* res[i] = deepClone(obj[i], hash)
* }
* return res;
* }
* // 上面这个例子如何处理循环引用?
* let obj1 = {a:1}
* obj1.b = {}
* obj1.b.a = obj1.b
*
* deepClone(obj1) 栈溢出了
* // 如果拷贝过的对象不需要再次拷贝 WeakMap存储一下
* [1,[2,3,[4]]] 展开为一维数组
Array.prototype.myFlat = function(){
// 结束条件
let R = new this.constructor;
for(let i = 0; i < this.length; i++){
if(Array.isArray(this[i])){
R = [...R, ...this[i].myFlat()];
} else {
R[i] = this[i];
}
}
return R
}
*/
reduce
// 收敛函数 可以把一个数组转换为其他格式
// 执行过程
// 0如果不写默认就是1用的数组的第一项, 如果数组是空的这里必须要有默认值!! ([]) 这里的0是不能省略的 ([1]) 0就可以不写了
([1,2,3,4]).redice((prevVal, currentVal, idx, ary)=>{
},0)
let r = ([1]).reduce(()=>{})
console.log(r) // 1 只有一个值的时候返回时数组第一个值,注意默认值也不能有
// reduce 方法使用的前提是前提必须是数组不能为空,如果只有一个值就返回当前值。
let r = ([1,2,3,4]).reduce((p, c, idx, arry)=>{
console.log(p, c); // p是上一次累加的结果, c就是数组当前的元素 如果函数后面的那个参数没传 第一次【p:1,c:2】 第二次[p:3, c:3]
return p+c;
})
r是最终的结果
// 这玩意除了干个累加还能干个什么?
// 手动实现reduce
Array.prototype.myReduce = (cb=()=>{}, prev) => {
for(let i = 0; i<this.length;i++){
// 如果不传prev
if(!prev){
prev = cb(this[i], this[i+1], i+1, this)
i++;
} else {
prev = cb(prev, this[i], i, this)
}
}
return prev
}
compose 中间件 组合函数把多个函数组合起来
function sum(a,b){
return a+b;
}
function len(str) {
return str.length;
}
function addPrefix(str) {
return str + '--'
}
// 有了这三个函数如何组合起来(compose)?
// 简单写法 addPrefix(len(sum('a', 'b')))
// compose 就是把他们组合在一起。
// 组合成一个最终的函数
let finall = compose(sum, len, addPrefix);
// 调用最终的函数
finall('a', 'b');
function compose(...fns) {
return (...ag) => {
let tmpRes = ag;
for(let i = fns.length-1;i>-1;i--) {
tmpRes = fns[i](...tmpRes);
}
}
}
// reduce版本
// reduceRight从右向左能
function compose(...fns) {
return (...args) => {
const first = fns.shift()(...args);
return fns.reduce((p, v, idx, ay)=>{
return v(p)
},first)
}
}
// reduce第二版本
// 这个最后玩完了相当于a(b(c())) // 仔细考虑考虑
// sum 延后了一层一层的嵌套起来了
// 调用最外边会先从嵌套的开始执行
// 从外到里包裹, 从里到外执行 包的时候是先包外边执行的时候是先执行里面
// reduce可以做收敛函数最终转换为一个结果
// 和科里化很像第一次是N个后面是一个个的。
function compose(...fns) {
return fns.reduce((a, b)=>{ // a是len b是sum
return (...args) => { // args 是 1 2
return a(b(...args)); // 这就是下一次的a
}
})
}
r = compose(len, sum)
r(1,2)
// 精简完了
const compose = (...fns) => fns.reduce((a,b) => (...args)=>a(b(...args)))
defineProperty Vue2中用的 特点 给本来的属性可以用此方法来定义,并且可以把值转换get set
let _val = '';
let s = {};
// 使用defineProperty的时候需要定义一个第三方参数供给给set和get方法使用,需要一个中间变量
Object.defineProperty(s, 'a', {
value: 'admin',
configurable: true, // 是不是可以被删除 delete s.a 如果设置了就能被删除 true代表可以被delete删除 默认不能被删除
enumerable: true, // 如果想可以枚举就设置为true,遍历对象可以被遍历 默认不能被枚举
// 默认不能被修改 s['a'] = '123' 默认不能被修改
writeable: true, // 就可以被修改了
// 扩展value的功能
get(){
// return 'ok' // s['a'] 返回就是ok
return _val;
}
set(val){
// s['a'] = 1 就走到了set方法
_value = val;
}
// 如果设置了get 和 set 就不能设置 writeable
// 加上 set get {a:[Getter/Setter]}
})
s['b'] = 'admin'
这两种方式的区别
上面那个可以加一些描述器
console.log(s); // 没有'a' 默认不可以枚举 但是可以用 s['a'] 取得
// 这个东西有什么缺陷?
// 如何把对象全部的属性编程getter setter?
// 需要遍历对象所有的属性给他们加上这层代理,用defineProperty把所有的属性重新定义所以性能垃圾。
// 把对象所有属性都 Object.defineProperty(s, 'a', { 这么来一下就拉稀了
// 如果是数组很大采用这种形式就芭比Q了
// 如果对象内部嵌套了对象 需要递归处理
相对于 Object.defineProperty ES6 有了 Proxy
// 不用改写元对象
// 但是兼容性差劲
xxx = new Proxy(s, { // 没有对obj的属性重写直接全对象代理,不需要递归
// 只要操作属性就会通过这两个
get(){ // xxx.xx
}
set(){ // xxx.xx = xx
}
has(){ // xx in xxx
}
deleteProperty(){ // 删除属性的时候
}
ownKeys(){ // getOwnPropertyNames 会调用
}
})