JavaScript继承的几种实现
Posted Chris.Chen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript继承的几种实现相关的知识,希望对你有一定的参考价值。
0 什么是继承
继承就是获得存在对象已有的属性和方法的一种方式。
1 JS中继承的几种实现方法
属性拷贝
原型式继承
原型链继承
call/apply方法继承(借用构造函数)
组合式继承:借用构造函数 + 原型式继承
圣杯模式
深拷贝(递归)
2 继承的具体实现
2-0 属性拷贝
【实现方法】
遍历对象中的key进行赋值
【问题】
继承过来的引用类型在父子对象中是共享的,即对其修改会同时影响父子对象中的值。
【示例代码】
1 // 继承方式1:属性拷贝(遍历对象中的key进行赋值) 2 // 创建父对象 3 var superObj = { 4 name : ‘Xianxian‘, 5 age : 20, 6 friends : [‘Shuoshuo‘, ‘Srue‘ , ‘YanShuo‘ ], 7 showName : function(){ 8 console.log(this.name); 9 } 10 } 11 // 创建子对象 12 var subObj = {}; 13 14 for(var i in superObj){ 15 subObj[i] = superObj[i]; 16 } 17 18 subObj.friends.push(‘MyLove‘); 19 console.log(superObj); 20 console.log(subObj,subObj.friends);
2-1 原型式继承
【实现方法】
2) 只能继承父构造函数原型对象上的成员,不能继承父构造函数实例对象上的成员。
【样例代码】
1 // 继承方式2:原型式继承 2 // 创建父类构造函数 3 function SuperClass(name){ 4 this.name = name; 5 this.sayName = function(){ 6 console.log(this.name); 7 } 8 } 9 10 // 设置父类构造原型对象 11 SuperClass.prototype.age = 20; 12 SuperClass.prototype.friends = [‘Shuoshuo‘,‘Yanshuo‘]; 13 SuperClass.prototype.showAge = function(){ 14 console.log(this.age); 15 } 16 17 18 // 创建空的子类构造函数 19 function SubClass(){ 20 21 } 22 23 // 借用构造函数的原型对象继承 即子类.prototype = 父类.prototype 24 SubClass.prototype = SuperClass.prototype; 25 // 此时,子类构造函数的原型被覆盖,其构造函数指向父类,需要修正其值指向子构造函数,验证如下: 26 console.log(SubClass.prototype.constructor == SuperClass); // true 27 console.log(SuperClass.prototype.constructor == SuperClass); // true 28 // 修改如下,之后即完成继承 29 SubClass.prototype.constructor = SubClass; 30 var child = new SubClass(); 31 console.log(child.friends); 32 child.friends.push(‘Mylove‘); 33 child.age = 10; 34 var father = new SuperClass("ChrisChen"); 35 // child中继承的只有显示表示出的prototype部分的属性值,父构造函数中的属性不会被继承 36 // 即只能继承父构造函数原型对象上的成员,不能继承父构造函数实例对象上的成员 37 console.log(child); //没有name,sayName()这些属性 38 father.showAge(); 39 // 同样,该继承方式存在引用属性的成员共享问题 40 console.log(father.friends); // 会多出‘MyLove‘
2-2 原型链继承
【实现方式】
子构造函数.prototype = new 父构造函数();
同原型式继承,也要对子构造函数构造器进行修改(由于子构造器原型被覆盖),从而实现继承:
【存在问题】
继承过来的引用类型在父子对象中是共享的,即对其修改会同时影响父子对象中的值。
【总结】
【样例代码】
1 // 继承方式3:原型链继承 2 // 创建父构造函数 3 function SuperClass1(){ 4 this.name = ‘ChenQixian‘; 5 this.age = 20; 6 this.sayName = function(){ 7 console.log(this.name); 8 } 9 } 10 11 // 设置父构造函数的原型 12 SuperClass1.prototype.friends = [‘YanShuo‘ , ‘Sure!‘]; 13 SuperClass1.prototype.showAge = function(){ 14 console.log(this.age); 15 } 16 17 // 创建子构造函数 18 function SubClass1(){ 19 20 } 21 22 // 原型链继承方式:子构造函数.prototype = new 父构造函数() 23 SubClass1.prototype = new SuperClass1(); 24 // 同原型式继承,也要对子构造函数构造器进行修改(由于子构造器原型被覆盖),从而实现继承 25 SubClass1.prototype.constructor = SubClass; 26 // 不同于原型式继承,这里会将父构造函数实例对象中的属性也继承 27 var child = new SubClass1(); 28 console.log(child.name); // ChenQixian 29 console.log(child.friends); // ["YanShuo", "Sure!"] 30 child.sayName(); // ChenQixian 31 child.showAge(); // 20 32 // 同样存在父子对象中引用属性的共享问题 33 var father = new SuperClass1(); 34 console.log(father.friends); // ["YanShuo", "Sure!"] 35 child.friends.push(‘myLove‘); 36 console.log(father.friends); // ["YanShuo", "Sure!", "myLove"] 37 console.log(child.friends); // ["YanShuo", "Sure!", "myLove"]
2-3 借用构造函数
【实现方式】
在子构造函数中,使用call,apply函数:区别在于apply传参数数组,call传参数列表
【存在问题】
(1) 不能借用原型
(2) 每次构造多调一个函数,增加了函数调用
【总结】
【样例代码】
// 继承方式4:call(),apply()借用构造函数 // 创建父构造函数 function Person(name){ this.name = name; this.friends = [‘ShuoShuo‘,‘Sure‘]; this.sayName = function(){ console.log(this.name); } } // 创建子构造函数 function Student(name){ console.log(this); // 调用call借用Person构造函数改变this指向 Person.call(this , name); } // 可以给父构造函数传参(这里传递了name),同时引用成员不存在共享问题 var stu = new Student(‘ChenQixian‘); stu.sayName(); // ChenQixian var pcs = new Person(‘Person‘); console.log(pcs.friends); // ["ShuoShuo", "Sure"] stu.friends.push(‘My_Love‘); console.log(pcs.friends); // ["ShuoShuo", "Sure"] console.log(stu.friends); // ["ShuoShuo", "Sure", "My_Love"]
2-4 组合继承
【实现方式】
结合借用式继承和原型式继承的方式
在子构造函数里调用call
在设置原型继承,并调整构造器:
SubClass.prototype = SuperClass.prototype;
【存在问题】
仍存在引用成员共享问题。
【总结】
(1) 继承了父构造函数原型对象上的成员以及父构造函数上的属性
(2) 实现了向父构造对象的参数传递
【样例代码】
1 // 继承方式5:借用构造函数 + 原型式继承 2 // 创建父构造函数 3 function Person1(name , age){ 4 this.name = name; 5 this.age = age; 6 this.sayName = function(){ 7 console.log(this.name); 8 } 9 } 10 // 设置父构造函数的原型对象 11 Person1.prototype.showAge = function(){ 12 console.log(this.age); 13 } 14 Person1.prototype.friends = [‘ShuoYan‘,‘SureYan‘]; 15 // 创建子构造函数 16 function Student1(name , age){ 17 Person1.call(this , name , age); 18 } 19 // 设置原型式继承 20 Student1.prototype = Person1.prototype; 21 Student1.prototype.constructor = Student1; 22 // 验证如下 23 var stu1 = new Student1(‘ChenQixian‘ , 19); 24 stu1.sayName(); // ChenQixian 25 stu1.showAge(); // 19 26 var pcs = new Person1(‘Person‘ , 21); 27 console.log(pcs.friends); // ["ShuoYan", "SureYan"] 28 stu.friends.push(‘My_Love‘); 29 console.log(pcs.age); 30 console.log(pcs.friends); // ["ShuoYan", "SureYan", "My_Love"] 31 console.log(stu.friends); // ["ShuoYan", "SureYan", "My_Love"]
2-5 圣杯模式
【总结】
仅继承原型的属性
uber代表父类(super)
【样例代码】
1 // 继承方式6:圣杯模式 仅继承原型的属性 2 3 var inherit = (function(){ 4 var F = function(){}; 5 return function(Target , Origin){ 6 F.prototype = Origin.prototype; 7 Target.prototype = new F(); 8 Target.prototype.constructor = Target; 9 Target.prototype.uber = Origin.prototype; 10 } 11 })(); 12 13 function Animal(name , age){ 14 this.name = name; 15 this.age = age; 16 } 17 18 Animal.prototype.eat = function(){ 19 console.log(‘Animal eat.‘); 20 } 21 22 function Person2(){ 23 24 } 25 26 inherit(Person2 , Animal); 27 28 var p = new Person2(); 29 30 p.eat();
3 后记
继承是面向对象编程OOP里的一个重要概念,javascript是一个面向对象语言,因此有必要对JS里的继承机制有一个深度的了解。
继承里最常用的方式为借用构造函数+深拷贝的继承方式。在之后关于该方式,会对这篇笔记进行补充。
以上是关于JavaScript继承的几种实现的主要内容,如果未能解决你的问题,请参考以下文章