1.以复制方式实现的继承
1.1浅拷贝
基本类型的复制
1 var parent = { 2 lanage: "chinese" 3 } 4 5 var child = { 6 name: "xxx", 7 age: 12 8 } 9 10 function extend(parent, child) { 11 var child = child || {}; 12 for (const propertype in parent) { 13 child[propertype] = parent[propertype]; 14 } 15 } 16 17 extend(parent, child); 18 19 console.log(child);//{ name: ‘xxx‘, age: 12, lanage: ‘chinese‘ }
以上代码中,通过一个extend()函数,将父对象parent的属性遍历赋给子对象child,从而实现继承。
但是这种字面量复制的方式存在巨大的缺陷,当父对象有引用类型的属性时,通过这么复制的方式,就像上一节中的var b = a一样,只会将a对象的引用赋给b对象,结果两者是指向同一个对象内存的,继承出来的子对象的属性是同一个,显然是不能满足需求的,(基本类型之间画等号是值的复制,引用类型之间画等号是引用的复制)所以就需要另谋出路。
1.2.深拷贝
利用递归加类型判断复制引用类型
1 var parent = { 2 lanage: "chinese", 3 address: { 4 home: "gansu", 5 office: "xian" 6 }, 7 schools: ["xiaoxue", "zhongxue", "daxue"] 8 } 9 10 var child = { 11 name: "xxx", 12 age: 12 13 } 14 15 function extendDeeply(parent, child) { 16 var child = child || {}; 17 for (const propertype in parent) { 18 //首先判断父对象的当前属性是不是引用类型 19 if (typeof parent[propertype] === "object") { 20 //再判断该引用类型是不是数组,是则给子对象初始化为空数组,否则初始化为空对象 21 child[propertype] = Array.isArray(parent[propertype]) ? [] : {}; 22 //递归调用自己,将父对象的属性复制到子对象 23 extendDeeply(parent[propertype], child[propertype]); 24 } else { 25 //基本类型直接复制 26 child[propertype] = parent[propertype]; 27 } 28 } 29 } 30 31 extendDeeply(parent, child); 32 console.log(child); 33 child.schools[0] = "qinghua"; //改变子对象的属性值 34 child.address.home = "tianjin"; 35 36 console.log(child.schools[0]); //qinghua 37 console.log(parent.schools[0]); //xiaoxue 38 console.log(child.address.home); //tianjin 39 console.log(parent.address.home); //gansu
可见此时子对象的改变并不会影响到父对象,父子彻底决裂。这便是深拷贝实现的继承。
注意:for in 是可以遍历数组的,结果为数组元素下标。
2.以构造函数实现继承
1 function Parent() { 2 this.name = "name"; 3 this.age = 12; 4 } 5 6 function Child() { 7 Parent.call(this); 8 this.language = "chinese"; 9 } 10 11 var child = new Child(); 12 console.log(child);//{ name: ‘name‘, age: 12, language: ‘chinese‘ } 13 child.name = "swk"; 14 console.log(new Parent().name);//name
如上所示,通过在子类构造函数中调用父类构造函数,将父类属性继承到子类对象上,new出来的每一个子类对象都是独立的实例,这种方式更符合面向对象的思想。
3.以原型方式实现继承
1 var person = {name: "pname"}; 2 3 function myCreate(person) { 4 var ins; 5 function F() { }; 6 F.prototype = person; 7 ins = new F(); 8 return ins; 9 } 10 11 var c = new myCreate(person); 12 console.log(c.name); //pname 13 console.log(c); //{}
如上所示,将一个构造函数的原型替换为父对象,然后返回该构造函数的实例,如此一来创建的子对象就是F的实例,并且其原型为父对象,所以c.name可以被查找到,但子对象c本身是没有属性的。这种直接重写prototype原型对象的弊端在于会断掉原本的原型链,即没有了继承自Object的方法,和给原型对象prototype上添加属性是不同的。
4.JS原型链
盗一波大神的经典图
从该图可以看出两点
4.1从原型prototype层面:
- 不论是 new Foo()自定义构造函数,new Object()系统构造函数,还是对象字面量创建出来的对象,只要是对象,其原型最终都指向Object.prototype对象,自定义的多绕了一步,就像Java里说的,Object.prototype是一切JS对象的根对象。
- 函数的原型都指向Function.prototype,而在JS中,函数也是对象的一种,所以符合第一条。
4.2从构造器constructor层面
- 首先对象的构造器肯定是指向他的构造函数,f1, f2指向Foo(), o1, o2指向Object()。
- 而构造函数Foo()和Object()的constructor属性最终指向函数构造器Function()。
原文地址:https://www.cnblogs.com/jixiaohua/p/10514239.html
时间: 2024-11-25 05:51:41