整理今天的笔记:
- 原型链
- 对象的深度拷贝
原型链:
什么是原型链,这个一道经典的面试题目。如果让我回答我感觉好像又说不清。这箭头指来指去的有点头晕呀,今天又看到了关于原型链的知识讲解,索性把它弄清楚。
原型基础知识:
- 在 JavaScript 中,每个构造函数都拥有一个prototype属性,它指向构造函数的原型对象,这个原型对象中有一个 constructor 属性指回构造函数;
- 每个实例都有一个__proto__属性,当我们使用构造函数去创建实例时,实例的__proto__属性就会指向构造函数的原型对象。所有对象(除null) 都有__proto__属性。
代码:
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;
}
总结:
- 深度优先的方式容易栈溢出,而广度优先遍历里面有个引用。就像作者说的有各自的边界问题;
- 了解JSON.parse(JSON.stringify()),方式深度拷贝也有栈溢出问题。
- 通过map对象,将拷贝过的数据存储起来。
- 后来查看资料可以使用 new WeakMap() 防止循环引用。