原型链是 JavaScript 中一个重要的概念,它是对象之间继承机制的基础。通过原型链,JavaScript 实现了基于对象的继承。理解原型链有助于深入理解 JavaScript 的继承、对象构造、属性查找等机制,也是常见面试考点之一。
一、原型链的概念
每个 JavaScript 对象都有一个内部属性叫 [[Prototype]]
(可以通过 __proto__
访问),它指向另一个对象,这个对象就是该对象的原型。如果原型对象本身也有原型,那么就会形成一条原型链,直到原型链的终点是 null
。在查找对象的属性时,如果对象本身没有这个属性,JavaScript 会沿着原型链向上查找,直到找到该属性或到达 null
为止。
关键点:
- 每个对象都有一个原型:对象的原型通过
[[Prototype]]
属性(或__proto__
)连接到另一个对象。 - 构造函数的
prototype
:每个构造函数(函数本身是对象)都有一个prototype
属性,指向该构造函数创建的对象的原型。 - 对象属性查找顺序:当访问对象的属性时,JavaScript 会首先在该对象本身上查找,如果找不到,则沿着原型链向上查找,直到找到属性或到达
null
。 - 终点是
null
:原型链的最后一个对象的__proto__
指向null
,即原型链的终点。
二、原型链示例
javascript
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
const alice = new Person('Alice');
alice.sayHello(); // 输出: Hello, my name is Alice
console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
在这个例子中:
alice
是通过Person
构造函数创建的对象,它的原型指向Person.prototype
。Person.prototype
具有sayHello
方法,alice
通过原型链可以访问该方法。Person.prototype
本身的原型是Object.prototype
,它是所有对象的原型,原型链的终点是null
。
三、原型链的结构
- 对象实例:每个对象实例都有一个隐式属性
__proto__
,指向该对象的原型。 - 构造函数的
prototype
属性:构造函数拥有一个prototype
属性,指向其原型对象,这个原型对象将成为由该构造函数创建的实例的原型。
原型链的图示:
alice.__proto__ -> Person.prototype -> Object.prototype -> null
四、常见的原型链相关面试题
1. 解释 JavaScript 中的原型链是什么?
回答要点:
- 原型链是对象的继承机制。每个对象都有一个
__proto__
指向其原型,原型链会沿着__proto__
一直向上查找,直到null
。 - 当访问对象的属性时,JavaScript 会首先在对象本身查找,如果找不到,则沿着原型链依次向上查找。
2. 如何访问对象的原型?
回答要点:
- 使用
__proto__
来访问对象的原型(不推荐在生产环境中使用,属于非标准)。 - 使用
Object.getPrototypeOf(obj)
获取对象的原型,这是标准方法。
javascript
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
3. 构造函数与原型链的关系是什么?
回答要点:
- 构造函数创建对象时,生成的对象会继承构造函数的
prototype
属性。 - 构造函数的
prototype
属性指向一个对象,这个对象包含该构造函数实例共享的方法或属性。 - 实例对象的
__proto__
指向构造函数的prototype
。
javascript
function Person() {}
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
4. 原型链如何影响属性查找?
回答要点:
- 当访问对象的属性时,JavaScript 会首先检查对象自身的属性,如果没有找到,则沿着原型链向上查找,直到找到该属性或到达
null
为止。 - 如果在原型链上找到了该属性,它将返回该属性的值。
- 如果在整个原型链上都没有找到该属性,返回
undefined
。
javascript
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello');
};
const alice = new Person('Alice');
console.log(alice.name); // "Alice"(在对象自身上找到)
alice.sayHello(); // "Hello"(在原型链上找到)
5. 什么是 Object.prototype
?
回答要点:
Object.prototype
是所有对象的原型,所有通过构造函数创建的对象都继承自Object.prototype
。- 原型链的终点是
Object.prototype
,Object.prototype.__proto__ === null
。
javascript
console.log(Object.prototype.__proto__ === null); // true
6. 什么是 prototype
和 __proto__
?它们之间的区别是什么?
回答要点:
prototype
:是构造函数的属性,指向一个对象,该对象将作为由该构造函数创建的实例的原型。__proto__
:是对象的属性,指向创建该对象的构造函数的prototype
,它表示对象的原型。
javascript
function Person() {}
const p = new Person();
console.log(Person.prototype); // 构造函数的 prototype
console.log(p.__proto__); // 实例对象的 __proto__,指向 Person.prototype
7. 如何创建一个没有原型的对象?
回答要点:
- 可以使用
Object.create(null)
来创建一个没有原型的对象。此对象不继承Object.prototype
的属性和方法。
javascript
const obj = Object.create(null);
console.log(Object.getPrototypeOf(obj)); // null
8. 如何判断一个属性是对象自身的还是从原型继承的?
回答要点:
- 可以使用
Object.hasOwnProperty()
方法来判断一个属性是否为对象自身的属性,而非从原型链继承的属性。
javascript
const obj = { name: 'Alice' };
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.hasOwnProperty('toString')); // false(继承自 Object.prototype)
9. 什么是 instanceof
运算符,它是如何工作的?
回答要点:
instanceof
用来判断一个对象是否是某个构造函数的实例。instanceof
通过检查对象的原型链,判断对象的__proto__
是否在构造函数的prototype
链上。
javascript
function Person() {}
const p = new Person();
console.log(p instanceof Person); // true
console.log(p instanceof Object); // true(因为 p 的原型链上有 Object.prototype)
10. 如何在 JavaScript 中实现继承?
回答要点:
- 使用构造函数和
prototype
来实现继承。 - 使用
Object.create()
来实现继承。
原型继承:
javascript
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + ' makes a sound.');
};
function Dog(name) {
Animal.call(this, name); // 继承属性
}
Dog.prototype = Object.create(Animal.prototype); // 继承方法
Dog.prototype.constructor = Dog;
const dog = new Dog('Rex');
dog.speak(); // "Rex makes a sound."
总结
原型链是 JavaScript 中对象继承的基础,它通过每个对象的 __proto__
属性指向原型对象,从而实现属性和方法的继承。了解原型链的结构及其工作机制是深入理解 JavaScript 继承和面向对象编程的关键。常见面试题围绕如何访问原型、属性查找机制、prototype
和 __proto__
的区别以及继承机制等展开。在实际
手写instanceof
js
const instanceof_ = (A, B) => {
// 前置判断(A 是对象 B 是函数),不符合直接访问false
if (typeof A !== 'object' || A === null || typeof B !== 'function') {
return false;
};
// 取构造函数创建实例的原型
const prototype = B.prototype;
// 取对象实例的原型
let __proto__ = A.__proto__;
while (true) {
// 假设找到顶端了,返回false
if (__proto__ === null) {
return false;
};
// 假设相等,返回true
if (prototype === __proto__) {
return true;
};
// 一直顺着__proto__往上找
__proto__ = __proto__.__proto__;
}
}
解释
js
A instanceof B
- A 是一个实例对象
- B 是一个构造器函数 在javascript中只有函数才有prototype属性