Vue3-10-Vue3用到了一些es的语法讲-02

70 阅读4分钟
继续上一讲解
/**
    * 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 会调用

    }
})