JS进阶:原型链与继承

学习自:JS忍者秘籍

在JavaScript中,可通过原型实现继承。

原型是定义属性和功能的一种便捷方式,对象可以访问原型上的属性和功能。原型类似于经典的面向对象语言中的类(class)。JavaScript使用new操作符,通过构造函数初始化新对象,但是没有真正的类定义。

每个对象都含有原型的引用,当查找属性时,若对象本身不具有该属性,则会查找原型上是否有该属性。

内置的方法Object.setPrototypeOf需要传入两个对象作为参数,并将第二个对象设置为第一个对象的原型。

每个对象都可以有一个原型,每个对象的原型也可以拥有一个原型,以此类推,形成一个原型链。查找特定属性将会被委托在整个原型链上,只有当没有更多的原型可以进行查找时,才会停止查找。

  • 每个函数都有一个原型对象
  • 每一个函数的原型都具有一个constructor属性,该属性指向函数本身。
  • constructor对象的原型设置为新创建的对象的原型。


对象ninja2创建完成时,对象ninja2的原型被设置为Ninja的原型。即,我们将一个函数作为构造函数使用时,构造器的原型对象将被设置为函数的原型

1
2
3
4
5
6
函数{
原型属性:原型对象{
constructor:函数本身
其他属性
}
}

通过使用constructor属性,我们可以访问创建该对象时所用的函数。这个特性可以用于类型校验

由于constructor属性仅仅是原始构造函数的引用,因此我们可以使用该属性创建新的对象

instanceof检查操作符右边的函数的原型是否存在于操作符左边的对象的原型链上

实例属性

当把函数作为构造函数,通过操作符new进行调用时,它的上下文被定义为新的对象实例。

在构造函数内部,关键字this指向新创建的对象,所以在构造器内添加的属性直接在新的实例上。

如果实例中可以查找到的属性,将不会查找原型。所以可以对属性进行重写

如果我们需要私有对象,在构造函数内指定方法是唯一的解决方案。

更改原型

对象与函数原型之间的引用关系是在对象创建时建立的。新创建的对象将引用新的原型

继承

使用原型实现继承

创建原型链最佳技术方案是一个对象的原型直接是另一个对象的实例

SubClass.prototype = new SuperClass();

Object.defineProperty

constructor属性被改写了,在类型检测中可能会出现问题。

当访问ninja对象的constructor属性时,由于ninja对象本身没有constructor属性,会查询原型即person对象。person对象本身也没有constructor属性,因此继续查找person对象的原型对象Person.prototype。Person.prototype具有constructor属性,指向Person函数。

如果想调整属性的配置信息,我们可以使用内置的Object.defineProperty方法,传入3个参数:属性所在的对象、属性名和属性描述对象。

属性描述(property descriptor):
● configurable——如果设为true,则可以修改或删除属性。如果设为false,则不允许修改。
● enumerable——如果设为true,则可在for-in循环对象属性时出现(我们很快会介绍for-in循环)。
● value——指定属性的值,默认为undefined。
● writable——如果设为true,则可通过赋值语句修改属性值。
● get——定义getter函数,当访问属性时发生调用,不能与value与writable同时使用。
● set——定义setter函数,当对属性赋值时发生调用,也不能与value与writable同时使用。


如果遍历Ninja.prototype对象,可确保不会访问到constructor属性。

用class和extends实现继承

ES6引入新的关键字class,但其底层的实现仍然是基于原型继承,class只是语法糖。

静态方法是类级别的方法

实现继承

注意转换