JavaScript 是一门面向对象的语言,而对象是 JavaScript 中的基本数据类型之一。每个对象都有一组属性和方法,这些属性和方法可以被其他对象继承和使用。JavaScript 中的对象继承是通过原型链来实现的。
JavaScript 中的每个对象都有一个内部属性 __proto__,该属性指向该对象的原型对象。原型对象也是一个对象,它也有自己的原型对象,以此类推,最终所有对象的原型对象都指向一个通用的根对象 Object.prototype。
当我们在一个对象上访问一个属性或方法时,JavaScript 引擎会首先在该对象本身查找该属性或方法,如果找不到则会到该对象的原型对象中查找,如果还找不到则继续在原型对象的原型对象中查找,以此类推,直到找到该属性或方法或者到达 Object.prototype。这种查找属性或方法的方式就称为原型链。
举个例子,假设我们有一个对象 obj,其原型对象为 protoObj,而 protoObj 的原型对象为 rootObj。当我们访问 obj 上的属性时,如果在 obj 中找不到该属性,则会去 protoObj 中查找,如果 protoObj 中也没有该属性,则会去 rootObj 中查找,以此类推,直到找到该属性或到达 Object.prototype。
在 JavaScript 中,可以通过两种方式来实现原型链:
我们可以使用构造函数来创建对象,并且在该构造函数的 prototype 属性中定义原型对象。当我们使用 new 关键字创建对象时,该对象就会继承该构造函数的原型对象。
例如,我们可以创建一个 Animal 构造函数和一个 Cat 构造函数,并且让 Cat 的原型对象指向 Animal 的实例对象:
function Animal() {}
Animal.prototype.sayHi = function() {console.log('Hi from Animal');
};function Cat() {}
Cat.prototype = new Animal();
Cat.prototype.sayHi = function() {console.log('Hi from Cat');
};var cat = new Cat();
cat.sayHi(); // 输出 "Hi from Cat"
在这个例子中,当我们在 cat 上调用 sayHi 方法时,JavaScript 引擎首先在 cat 中查找该方法,如果找不到则在 Cat.prototype 中查找,因为 Cat.prototype 指向 Animal 的实例对象,所以会在 Animal.prototype 中查找,最终找到该方法并执行。
我们可以使用 Object.create() 方法来创建一个新对象,并且指定该对象的原型对象。这个方法可以用于任何对象,不必使用构造函数。
例如,我们可以创建一个 Animal 对象和一个 Cat 对象,并且让 Cat 对象的原型对象指向 Animal 对象:
var animal = {sayHi: function() {console.log('Hi from Animal');}
};var cat = Object.create(animal);
cat.sayHi = function() {console.log('Hi from Cat');
};cat.sayHi(); // 输出 "Hi from Cat"
在这个例子中,当我们在 cat 上调用 sayHi 方法时,JavaScript 引擎首先在 cat 中查找该方法,如果找不到则在 cat 的原型对象中查找,即 animal,最终找到该方法并执行。
使用原型链实现对象继承有以下几个优点:
使用原型链实现对象继承也存在一些缺点:
JavaScript 原型链是 JavaScript 面向对象编程中一个重要的概念,也是实现对象继承的一种方式。理解原型链可以让我们更好地理解 JavaScript 中的对象和继承机制。虽然原型链存在一些缺点,但它仍然是 JavaScript 中最重要的面向对象编程的概念之一,值得我们深入学习和掌握。
实例的 __proto__ 属性等于其构造函数的 prototype 属性。
所有构造函数都是Function的实例,所有原型对象除了Object.prototype都是Object的实例。
原型链的尽头是null,也就是Object.prototype.__proto__ === null
