这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
先从面向对象讲起,本瓜认为:面向对象编程,它的最大能力就是:复用!
咱常说,面向对象三大特点,封装、继承、多态。
这三个特点,以“继承”为核心。封装成类,是为了继承,继承之后再各自发展(重写),可理解为多态。所以,根本目的是为了继承,即“复用“!
如果你用 JavaScript 面向对象的能力来编程的话,能想到的,也只供使用的就是:基于原型。
因为这门语言设计就是这样,我们之前也提过:JavaScript的语言设计主要受到了Self(一种基于原型的编程语言)和 Scheme(一门函数式编程语言)的影响;
它复用的能力就是来自原型!
好了,有这个认知基础,我们再看原型继承。
原型链继承
原型继承最直接的一种实现就是:原型链继承
ECMA-262 把原型链定义为 ECMAScript 的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。
我们来看看原型链继承的代码实现:
- 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']
- 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 继承真的很奇怪。。。并不是面向对象语言,又要通过原型链去模拟面向对象,真的很多小坑的点需要去注意。(哈哈哈,想想还是函数式好,清晰)
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。