家都知道的,JavaScript这门语言在ES6出来之前是没有类(class)这个概念的。
所以JavaScript中的类都是通过原型链来实现的。
既然能实现类,那同样也就可以在JavaScript中实现面向对象的继承了。(喜欢看书的朋友,可以去看一下《JavaScript高级程序设计》第三版6.3小节)。
我们使用继承主要是为了能实现代码的抽象和代码的复用,在应用层实现功能的封装。
在JavaScript中,继承的实现比较复杂,坑也很多,什么属性继承、原型继承、call/aplly继承、原型链继承、对象继承、构造函数继承、组合继承、类继承... 十几种,看着都头晕,对于小白来说,看多了都不知道用哪个好了。
而且每一种都细讲需要花很多时间,所以今天这里大致梳理常用的两种给大家学习,一起来看看吧!
JavaScript 中的继承并不是明确规定的,而是通过模仿实现的。
今天我们来讲两种,一个是原型链实现继承、第二个借用构造函数
先看一下父类
父类
实现方式:子类的原型指向父类的实例
子类:
代码测试
测试
这种方式会存在的问题:
1.引用类型的对象会被子类的所有实例共享(问题1解决)
2.无法在创建子类的实例时,给父类的构造函数传递参数(问题2解决)
实现方式:在子类的构造函数中利用call(或者apply)方法执行父类构造函数(问题2解决),将执行对象设为子类的this,相当于把父类构造函数中的成员拷贝了一份到子类(问题1解决)
子类
子类
测试代码如下:
测试代码
存在的问题:
3.父类原型中定义的属性无法被继承
以上两种方法都是有存在问题的,所以我们现在就出现了第三种方法来实现,你知道第三种方式是怎么实现的吗?欢迎在评论区留言哦!
我们下次继续聊继承的其他方法。
其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。因为构造函数只是一个函数,所以可使 Parent 构造函数 成为 Children 的方法,然后调用它。Children 就会收到 Parent 的构造函数中定义的属性和方法。例如,用下面的方式定义 Parent 和 Children:
原理:就是把 Parent 构造函数放到 Children 构造函数里面执行一次。那为什么不直接执行,非要转个弯把 Parent 赋值给 Children 的 method 属性再执行呢? 这跟 this 的指向有关,在函数内 this 是指向 window 的。当将 Parent 赋值给 Children 的 method 时, this 就指向了 Children 类的实例。
众所周知,JavaScript 是一门基于原型的语言,在 JavaScript 中 prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制:
注意:调用 Parent 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。
这个方法是与对象冒充方法最相似的方法,因为它也是通过改变了 this 的指向而实现继承:
apply 方法本人就不举列了,它和 call 方法的区别在于它的第二个参数必须是数组。
对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。如何选择呢?答案很简单,两者都用。 在 JavaScript 中创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制:
Object.create 方法会使用指定的原型对象及其属性去创建一个新的对象:
@ 当执行 Children.prototype=Object.create(Parent.prototype) 这个语句后,Children 的 constructor 就被改变为 Parent ,因此需要将 Children.prototype.constructor 重 新指定为 Children 自身。
这个是 ES6 的语法糖,下面看下es6实现继承的方法:
上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。子类Children的构造函数之中的super(),代表调用父类Parent的构造函数。这是必须的,否则 JavaScript 引擎会报错。
注意,super虽然代表了父类Parent的构造函数,但是返回的是子类Children的实例,即super内部的this指的是Children,因此super()在这里相当于Parent.prototype.constructor.call(this)。
创: 前端二牛
借用构造函数继承解决了原型链数据共享和无法向超类型传递参数的问题,但自身的缺陷是不能使用超类型原型中定义的方法。组合继承是将原型链继承和借用构造函数继承组合到一起,从而发挥二者之长的一种继承模式,也被称作伪经典继承。背后的思路是使用原型链实现原型属性和方法的继承,使用构造函数来实现实例属性的继承。这样既通过在原型上定义方法实现了函数的复用,又能保证每个实例都有它自己的属性。
下面来看一个例子:
在这个例子中首先声明了一个 Human函数,然后使用原型模式定义了 sayName方法。声明了 Person函数,通过构造函数模式继承了 Human,但是这只能继承 Human中声明的属性,也就是实例属性,没办法继承原型属性和方法,于是紧接着将 Person的原型赋值为 Human的一个实例,通过原型链实现原型属性和方法的继承,然后添加了一个子类型特有的方法 sayName,这就是组合继承。测试使用,结果既可以通过构造函数向超类型传递参数,也没有 colors被共享的问题,同时还可以使用超类型原型上定义的方法 sayName,可以说非常完美。
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JavaScript中最常用的继承模式。而且,因为使用了原型链, instanceOf和 isPrototypeOf也能够识别基于组合继承创建的对象。
*请认真填写需求信息,我们会在24小时内与您取得联系。