这里给大家分享我在网上总结出来的一些知识 ,希望对大家有所帮助
我们来看看原型链继承的代码实现:
function SuperType() {
this.property = true;
}
function SubType() {
this.subproperty = false;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
SubType.prototype = new SuperType(); // 对 SubType 得原型链重新指定 ,是原型链继承
let instance = new SubType();
console.log(instance.getSuperValue()); // true
还需要再额外说明查找关系吗??不懂得工友可见这篇 《歪理解?原型链中的函数和对象》
这里还是用代码展示下它们的指向关系吧:
上面例子中有 1 个对象 instance , 两个函数 ,SuperType 和 SubType 。函数是上帝 ,对象是基本物质 。继承来自两方面:1. 继承自祖先(遗产);2. 继承自上帝(天赋);
// 继承自祖先(遗产)
instance.__proto__ === SubType.prototype // true
SubType.prototype.__proto__ === SuperType.prototype // true
// 继承自上帝(天赋)
SuperType.__proto__ === Function.prototype // true
SubType.__proto__ === Function.prototype // true
SuperType.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true
当然 ,我们并不是来讲原型链的 。重点是:点出原型链继承的“问题 ”!!
它的主要问题出现在:原型中包含引用值的时候 ,原型中包含的引用值会在所有实例间共享 。
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {}
SubType.prototype = new SuperType() // 原型链继承
let s1 = new SubType()
let s2 = new SubType()
s1.colors.push("yellow")
console.log(s1.colors) // [red, blue, green, yellow]
console.log(s2.colors) // [red, blue, green, yellow]
colors 是个数组 ,引用值 ,当它共享给 SubType 的时候 ,用的是引用值 ,当我们实例化的时候 ,如果其中一个实力对它做出了修改,将会影响到其它实例的引用 。
其实 ,我们也知道 ,很少在业务代码中这样去写继承:SubType.prototype = new SuperType() ,原型链继承会造成复用的混乱 ,所以它基本不会被单独使用 。
构造函数继承
构造函数继承 ,也叫做:“盗用构造函数 ” ,“对象伪装 ”或“经典继承 ” 。
基本思路:在子类构造函数中用 apply()和 call()方法调用父类构造函数 。
上一小节的例子改造为:
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
SuperType.call(this) // 构造函数继承
}
let s1 = new SubType()
let s2 = new SubType()
s1.colors.push("yellow")
console.log(s1.colors) // [red, blue, green, yellow]
console.log(s2.colors) // [red, blue, green]
完美解决原型链继承的问题 ,但是它也有它的问题 ,也是使用构造函数模式自定义类型的问题 ,
即:必须在构造函数中定义方法(在原型上定义方法 ,子类是访问不到的) ,函数不能重用 。
function SuperType() {
}
function SubType() {
SuperType.call(this) // 构造函数继承
}
SuperType.prototype.fn = ()=>{}
let s1 = new SubType()
console.log(s1.fn) // undefined
function SuperType() {
this.fn=()=>{}
}
function SubType() {
SuperType.call(this) // 构造函数继承
}
let s1 = new SubType()
let s2 = new SubType()
console.log(s1.fn === s2.fn) // false
而这一点 ,在原型链继承中 ,又是可以的 。 。 。
function SuperType() {}
function SubType() {}
SuperType.prototype.fn = ()=>{}
SubType.prototype = new SuperType() // 原型链继承
let s1 = new SubType()
console.log(s1.fn) // ()=>{}
function SuperType() {
this.fn=()=>{}
}
function SubType() {}
SubType.prototype = new SuperType() // 原型链继承
let s1 = new SubType()
let s2 = new SubType()
console.log(s1.fn === s2.fn) // true
所以 ,综上 ,原型链继承和构造函数继承的 “毛病 ” 分别是:
原型链继承:所有继承的属性和方法都会在对象实例间共享,无法做到实例私有。
构造函数继承:子类不能访问父类原型上的方法 。
咱就是说 ,这东西怎么这么拧巴呢 。。 。
于是乎一个规避二者“毛病 ”的继承方式出现了:组合继承~~
组合继承
目前最流行的继承模式是组合继承!
思路是:使用原型链继承原型上的属性和方法 ,而通过构造函数继承实例属性 。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
function SubType(name, age){
SuperType.call(this, name) // 构造函数继承
this.age = age;
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
SubType.prototype = new SuperType() // 原型链继承
SubType.prototype.sayAge = function() {
console.log(this.age);
}
let s1 = new SubType("Nicholas", 29)
let s2= new SubType("Greg", 27)
s1.colors.push("yellow")
console.log(s1.colors) // [red, blue, green, yellow]
console.log(s2.colors) // [red, blue, green]
s1.sayName() // Nicholas
s2.sayName() // Greg
s1.sayAge() // 29
s2.sayAge() // 27
组合继承,总结起来就是 ,属性(特别是引用值)通过构造函数去继承 ,而公用的 、需要复用的方法用原型链去继承!!
说实话 ,JS 继承真的很奇怪 。 。 。并不是面向对象语言 ,又要通过原型链去模拟面向对象 ,真的很多小坑的点需要去注意 。(哈哈哈 ,想想还是函数式好 ,清晰)
本文转载于:
https://juejin.cn/post/7107779239281164301
如果对您有所帮助 ,欢迎您点个关注 ,我会定时更新技术文档 ,大家一起讨论学习 ,一起进步 。
声明:本站所有文章 ,如无特殊说明或标注,均为本站原创发布 。任何个人或组织 ,在未征得本站同意时 ,禁止复制 、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 。