原型
我们来仔细看看以下这段代码。
let empty = {};
console.log(empty.toString);
// → function toString(){…}
console.log(empty.toString());
// → [object Object]
我从一个空对象中取出了一个属性。 好神奇!
实际上并非如此。我只是掩盖了一些 JavaScript 对象的内部工作细节罢了。每个对象除了拥有自己的属性外,都包含一个原型(prototype)。原型是另一个对象,是对象的一个属性来源。当开发人员访问一个对象不包含的属性时,就会从对象原型中搜索属性,接着是原型的原型,依此类推。
那么空对象的原型是什么呢?是Object.prototype
,它是所有对象中原型的父原型。
console.log(Object.getPrototypeOf({}) ==
Object.prototype);
// → true
console.log(Object.getPrototypeOf(Object.prototype));
// → null
正如你的猜测,Object.getPrototypeOf
返回一个对象的原型。
JavaScript 对象原型的关系是一种树形结构,整个树形结构的根部就是Object.prototype
。Object.prototype
提供了一些可以在所有对象中使用的方法。比如说,toString
方法可以将一个对象转换成其字符串表示形式。
许多对象并不直接将Object.prototype
作为其原型,而会使用另一个原型对象,用于提供一系列不同的默认属性。函数继承自Function.prototype
,而数组继承自Array.prototype
。
console.log(Object.getPrototypeOf(Math.max) ==
Function.prototype);
// → true
console.log(Object.getPrototypeOf([]) ==
Array.prototype);
// → true
对于这样的原型对象来说,其自身也包含了一个原型对象,通常情况下是Object.prototype
,所以说,这些原型对象可以间接提供toString
这样的方法。
你可以使用Object.create
来创建一个具有特定原型的对象。
let protoRabbit = {
speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
};
let killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEEE!");
// → The killer rabbit says 'SKREEEE!'
像对象表达式中的speak(line)
这样的属性是定义方法的简写。 它创建了一个名为speak
的属性,并向其提供函数作为它的值。
原型对象protoRabbit
是一个容器,用于包含所有兔子对象的公有属性。每个独立的兔子对象(比如killerRabbit
)可以包含其自身属性(比如本例中的type
属性),也可以派生其原型对象中公有的属性。