- 探讨原型
- 使用函数作为构造器
- 使用原型扩展对象
- 避免常见的问题
- 使用继承创建类
function Ninja() {
this.swung = true;
}
const ninja1 = new Ninja();
Ninja.prototype.swingSword = function() {
return this.swung;
};
console.log(ninja1.swingSword()); // true
Ninja.prototype = {
pierce: function() {
return true;
}
};
console.log(ninja1.swingSword()); // true
const ninja2 = new Ninja();
console.log(ninja2.pierce()); // true
console.log(ninja2.swingSword); // undefined
检查实例的类型与它的 constructor
function Ninja() {}
const ninja = new Ninja();
console.log(typeof ninja === 'object');
console.log(ninja instanceof Ninja);
console.log(ninja.constructor === Ninja);
function Person(){}
Person.prototype.dance = funtion(){};
function Ninja(){}
Ninja.prototype = {
dance: Person.prototype.dance; // 复制 Person 的原型方法 dance 到 Ninja 的原型上
};
const ninja = new Ninja();
console.log(ninja instanceof Ninja);
console.log(ninja instanceof Person);
console.log(ninja instanceof Object);
由于函数原型是对象类型,因此有多种复制功能(如属性或方法)可以实现继承的方法。这不是真正的继承——仅仅是复制。
function Person() {}
Person.prototype.dance = function() {};
function Ninja() {}
Ninja.prototype = new Person();
const ninja = new Ninja();
ninja instanceof Ninja;
ninja instanceof Person;
ninja instanceof Object;
typeof ninja.dance === 'function';
直接使用 Person 的原型对象作为 Ninja 的原型,如
Ninja.prototype = Person.prototype
。这样做会导致在 Person 原型上所发生的所有变化都被同步到 Ninja 原型上(Person 原型与 Ninja 原型是同一个对象),一定会有不良的副作用。
ninja.constructor === Person; // true
解决 constructor 属性的问题
function Person() {}
Person.prototype.dance = function() {};
function Ninja() {}
Ninja.prototype = new Person();
Object.defineProperty(Ninja.prototype, 'constructor', {
enumerable: false,
value: Ninja,
writable: true
});
var ninja = new Ninja();
console.log(ninja.constructor === Ninja);
for (let prop in Ninja.prototype) {
console.log(prop === 'dance');
}
检查操作符右边的函数的原型是否存在于操作符左边的对象的原型链上。
虽然 ES6 引入关键字 class,但是底层仍然是基于原型的实现。class 只是语法糖,使得在 JavaScript 模拟类的代码更为简洁。
class Ninja {
constructor(name, level) {
this.name = name;
this.level = level;
}
swingSword() {
return true;
}
static compare(ninja1, ninja2) {
return ninja1.level - ninja2.level;
}
}
var ninja1 = new Ninja('Yoshi', 4);
var ninja2 = new Ninja('Hattori', 3);
console.log(!('compare' in ninja1) && !('compare' in ninja2));
console.log(Ninja.compare(ninja1, ninja2) > 0);
console.log(!('swingSword' in Ninja));
- JavaScript 对象是属性名与属性值的集合。
- JavaScript 使用原型。
- 每个对象上都具有原型的引用,搜索指定的属性时,如果对象本身不存在该属性,则可以代理到原型上进行搜索。对象的原型也可以具有原型,以此类推,形成原型链。
- 可以通过
Object.setPrototypeOf
方法定义对象的原型。 - 原型与构造函数密切相关。每个函数都具有原型属性,该函数创建的对象的原型,就是函数本身。
- 函数原型对象具有
constructor
属性,该属性指向函数本身。该函数创建的全部对象均访问该属性,constructor
属性还可用于判断对象是否是由指定的函数创建的。 在JavaScript中,几乎所有的内容在运行时都会发生变化,包括对象的原型和函 数的原型。 - 如果我们希望 Ninja 构造函数创建的实例都可以“继承”(更准确地说,可以访问)Person 构造函数的属性,那么,将 Ninja 构造函数的原型设置为 Person 类的实例。
- 在 JavaScript 中,原型具有属性(如
configurable
、enumerable
、writable
)。这些属性可通过内置的Object.defineProperty
方法进行定义。 - JavaScript ES6 引入关键字
class
,使得我们可以更方便地实现模拟类。在底层仍然是使用原型实现的。 - 使用
extends
可以更优雅地实现继承。