推荐答案
JavaScript 中,每个对象都有一个原型(prototype)属性,这个属性本身也是一个对象。当我们访问对象的属性时,如果对象本身没有这个属性,JavaScript 引擎会沿着对象的原型链向上查找,直到找到该属性或到达原型链的末端(null)。
原型 (Prototype):
- 每个函数(包括构造函数)都有一个
prototype属性,这个属性指向一个对象,这个对象通常被称为原型对象。 - 当使用
new关键字调用构造函数创建对象时,新创建的对象的内部属性[[Prototype]](可以通过__proto__或Object.getPrototypeOf()访问)会指向构造函数的prototype属性。 - 原型对象的作用是允许该构造函数创建的所有实例对象共享属性和方法。
原型链 (Prototype Chain):
- 原型链是指对象通过
[[Prototype]](__proto__)属性链接起来的一系列原型对象。 - 当访问对象的属性或方法时,如果对象自身没有,就会沿着原型链向上查找,直到找到或者到达原型链的末端(
null)。 - 原型链的末端通常是
Object.prototype,它的[[Prototype]]属性为null。 - 原型链是实现 JavaScript 继承的基础机制。
本题详细解读
1. 深入理解 Prototype
函数的 prototype 属性
在 JavaScript 中,函数也是对象。每个函数都有一个特殊的属性叫做 prototype。这个 prototype 属性是一个对象,默认情况下,它有一个 constructor 属性,指向该函数自身。
function Person(name) {
this.name = name;
}
console.log(Person.prototype); // 输出一个对象
console.log(Person.prototype.constructor === Person); // 输出 true原型对象的作用
当使用 new 关键字调用构造函数创建一个新的对象实例时,这个新实例的内部会有一个隐藏属性 [[Prototype]] (可以通过 __proto__ 或者 Object.getPrototypeOf() 访问),该属性指向构造函数的 prototype 属性。这意味着,通过构造函数创建的所有实例都会继承原型对象上的属性和方法。
// www.javascriptcn.com code example
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
let person1 = new Person("Alice");
let person2 = new Person("Bob");
person1.sayHello(); // 输出 "Hello, my name is Alice"
person2.sayHello(); // 输出 "Hello, my name is Bob"
console.log(person1.__proto__ === Person.prototype); // 输出 true
console.log(person2.__proto__ === Person.prototype); // 输出 true在这个例子中,person1 和 person2 实例都没有 sayHello 方法,但他们通过原型链都能够访问到 Person.prototype 上的 sayHello 方法。这就是原型继承。
2. 深入理解 Prototype Chain
原型链的形成
原型链是多个对象通过 [[Prototype]] 属性链接形成的链条。每个对象的 [[Prototype]] 属性指向其原型对象,而原型对象自身也可能有原型,依此类推。
// www.javascriptcn.com code example
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function(){
console.log("Animal eat");
}
function Dog(name, breed) {
Animal.call(this,name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function(){
console.log('Woof!');
}
let myDog = new Dog('Buddy', 'Golden Retriever');
console.log(myDog.__proto__);
console.log(myDog.__proto__.__proto__);
console.log(myDog.__proto__.__proto__.__proto__);
myDog.eat();//输出:Animal eat
myDog.bark();//输出:Woof!
console.log(myDog instanceof Dog);// 输出true
console.log(myDog instanceof Animal);// 输出true这里 myDog 实例的 [[Prototype]] 指向 Dog.prototype,而 Dog.prototype 的 [[Prototype]] 指向 Animal.prototype, Animal.prototype的[[Prototype]] 指向 Object.prototype。最终 Object.prototype的[[Prototype]] 指向 null,原型链到此结束。
属性查找机制
当我们尝试访问对象的属性时,JavaScript 引擎会按照以下步骤查找:
- 首先检查对象自身是否拥有该属性。
- 如果没有,检查对象的
[[Prototype]]指向的原型对象是否存在该属性。 - 如果原型对象仍然没有,继续查找原型对象的原型,依此类推,直到找到该属性或达到原型链的末端
null。 - 如果到原型链的末端仍未找到该属性,则返回
undefined。
这就是原型链的核心机制,使得 JavaScript 可以实现继承和代码复用。
Object.prototype 和 null
所有的 JavaScript 对象都继承自 Object.prototype,因此 Object.prototype 是原型链的最顶端。Object.prototype 本身的 [[Prototype]] 为 null,这意味着它不继承任何对象,它标志着原型链的结束。