JavaScript 设计模式核⼼原理与应⽤实践 | 小册免费学

176 阅读2分钟

整理今天的笔记:

  1. 原型链
  2. 对象的深度拷贝
原型链:

什么是原型链,这个一道经典的面试题目。如果让我回答我感觉好像又说不清。这箭头指来指去的有点头晕呀,今天又看到了关于原型链的知识讲解,索性把它弄清楚。

原型基础知识:
  • 在 JavaScript 中,每个构造函数都拥有一个prototype属性,它指向构造函数的原型对象,这个原型对象中有一个 constructor 属性指回构造函数;
  • 每个实例都有一个__proto__属性,当我们使用构造函数去创建实例时,实例的__proto__属性就会指向构造函数的原型对象。所有对象(除null) 都有__proto__属性。

image.png

代码:

function Foo(){

}
var f1=new Foo();
var f2=new Foo();

通过上面两句对着图,突然发现原型链就是查询原型对象的过程。当构造函数通过prototype查找,对象通过__proto__查找。(prototype/__proto__类似指针)

对象的深拷贝
//深度优先遍历
function deepClone(obj){
    if(typeof obj !=='object' || obj===null){
        return obj;
    }
    
    let newObj={};
    
    //通过constructor 属性指回构造函数 判断类型
    if(obj.constructor===Array){
        newObj=[];
    }
    
    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            newObj[key]=deepClone(obj[key]);
        }
    }
    return newObj;
}

后面来阅读了其他拷贝的方法,根据文章的写的稍微改了下,加了对数组的判断。

//广度优先遍历

function isObject(x){
    return Object.prototype.toString.call(x)==='[Object Object]';
}

function deepClone(obj){

    //去重
    const uniqueList = new WeakMap();

    let root={};

    //队列 初始化节点
    let queue=[{
        parent:root,
        key:undefined,
        data:obj
    }]

    while(queue.length){

        const node=queue.shift();
        const parent=node.parent;
        const key=node.key;
        const data=node.data;

        let res=parent;
        if(typeof key!=='undefined'){
            let obj={};
            if(data.constructor===Array){//判断类型 处理数组
                obj=[];
            }
            //保持引用关系
            res=parent[key]=obj;
        }

        if(uniqueList.has(data)){
            parent[key]=uniqueList.get(data);
            break;
        };
        uniqueList.set(data,res);

        for(let key in data){
            if(data.hasOwnProperty(key)){
                if (isObject(data[key]) {
                    queue.push({
                        parent:res,
                        key:key,
                        data:data[key]
                    })
                }else{
                    res[key]=data[key];
                }
               
            }

        }
    }

    return root;
}
总结:
  1. 深度优先的方式容易栈溢出,而广度优先遍历里面有个引用。就像作者说的有各自的边界问题;
  2. 了解JSON.parse(JSON.stringify()),方式深度拷贝也有栈溢出问题。
  3. 通过map对象,将拷贝过的数据存储起来。
  4. 后来查看资料可以使用 new WeakMap() 防止循环引用。