一、理解原型:
在JavaScript中,可通过原型实现继承。
原型的概念很简单。每个对象都含有原型的引用,当查找属性时,若对象本身不具有该属性,则会查找原型上是否有该属性。可以假想一下,你正在和一组人共同玩一个游戏,游戏规则为:主持人提问,如果你知道答案,则可直接回答;如果你不知道答案,则可以询问你的下一个人。就是那么简单。
在JavaScript中,对象的原型属性是内置属性(使用标记[[prototype]]),无法直接访问。相反,内置的方法Object.setPrototypeOf需要传入两个对象作为参数,并将第二个对象设置为第一个对象的原型。
每个对象都可以有一个原型,每个对象的原型也可以拥有一个原型,以此类推,形成一个原型链。查找特定属性将会被委托在整个原型链上,只有当没有更多的原型可以进行查找时,才会停止查找。
二、对象构造器与原型:
通过操作符new,应用于构造函数之前,触发创建一个新对象分配。
每个函数都有一个原型对象,该原型对象将被自动设置为通过该函数创建对象的原型。
当函数创建完成之后, 立即就获得了一个原型对象, 我们可以对该原型对象进行扩展。
· 每一个函数都具有一个原型对象。
· 每一个函数的原型都具有一个constructor属性,该属性指向函数本身。
· constructor对象的原型设置为新创建的对象的原型。
我们创建的每一个函数都具有一个新的原型对象。
最初的原型对象只有一个属性,即constructor属性。该属性指向函数本身。
当我们将函数作为构造器进行调用时,新构造出来的对象的原型被设置为构造函数的原型的引用。
function Person() {}
Person.prototype.dance = function() {};
function Ninja() {}
Ninja.prototype = new Person();
Object.defineProperty(Ninja.prototype, "constructor", {
enumerable: false,
value: Ninja,
writable: true //定义一个新的不可枚举的constructor属性,属性值为Ninja
});
function Parent(name, actions) {
this.name = name;
this.actions = actions;
}
Parent.prototype.eat = function () {
console.log(`${this.name} - eat`);
}
function Child(id) {
Parent.apply(this, Array.from(arguments).slice(1));
this.id = id;
}
let TempFunction = function () {}
TempFunction.prototype = Parent.prototype;
Child.prototype = new TempFunction();
Child.prototype.constructor = Child;
四、类:
使用类实现继承:
class Person {
constructor(name) {
this.name = name;
}
dance() {
return true;
}
}
class Ninja extends Person { //使用关键字extends实现继承
constructor(name, weapon) {
super(name); //使用关键字super调用基类构造函数
this.weapon = weapon;
}
wieldWeapon() {
return true;
}
}
五、原型链查找顺序:
1.实例对象自身
2.实例对象的构造函数
3.实例对象的原型,也就是构造函数的原型对象
4.实例对象的原型的原型,也就是构造函数的原型对象的原型
六、小结:
·JavaScript对象是属性名与属性值的集合。
·JavaScript使用原型。
·每个对象上都具有原型的引用, 搜索指定的属性时, 如果对象本身不存在该属性, 则可以代理到原型上进行搜索。 对象的原型也可以具有原型, 以此类推, 形成原型链。
·可以通过Object.setPrototypeOf方法定义对象的原型。
·原型与构造函数密切相关。 每个函数都具有prototype属性, 该函数创建的对象的原型, 就是函数的原型。
·函数原型对象具有constructor属性, 该属性指向函数本身。 该函数创建的全部对象均访问该属性, constructor属性还可用于判断对象是否是由指定的函数创建的。
·在JavaScript中, 几乎所有的内容在运行时都会发生变化, 包括对象的原型和函数的原型。
·如果我们希望Ninja构造函数创建的实例都可以“继承”( 更准确地说, 可以访问) Person构造函数的属性, 那么, 将Ninja构造函数的原型设置为Person类的实例。
·在JavaScript中, 原型具有属性( 如configurable、 enumerable、writable) 。 这些属性可通过内置的Object.defineProperty方法进行定义。
·JavaScript ES6引入关键字class, 使得我们可以更方便地实现模拟类。 在底层仍然是使用原型实现的。
·使用extends可以更优雅地实现继承。