JavaScript 的灵魂:原型与原型链

72 阅读2分钟

在JavaScript中,我们可以通过构造函数(Constructor)来创建一个新的对象。

构造函数其实就是一个普通的函数,但当我们通过 new 关键字来调用它时,它就会生成一个新的对象。

这个新的对象会有一个隐藏的属性 __proto__,这个属性就指向了构造函数的 prototype 属性,也就是原型对象。

举个例子,我们创建一个构造函数 Person,然后通过 new Person() 来创建一个新的对象 person

function Person() {}

let person = new Person();

在这个例子中,person 这个对象有一个隐藏的属性 __proto__,它指向了 Person 构造函数的 prototype属性。

Person.prototype 就是 person 的原型对象。

console.log(person.__proto__ === Person.prototype); // 输出:true

这就是原型。

接下来我们来说说原型链(prototype chain)。

原型链的概念是建立在原型的基础上的。在 JavaScript 中,每个对象都有一个原型,原型对象也是对象,所以它也有自己的原型。

这样一层层向上追溯,就形成了一条链状结构,我们称之为原型链。

举个例子,我们刚才创建的 person 对象,它的原型是 Person.prototype,那么 Person.prototype 的原型是什么呢?

其实它的原型是 Object.prototype,因为所有的构造函数(包括 Person)都是 Object 构造函数的实例。

console.log(Person.prototype.__proto__ === Object.prototype); // 输出:true

那么 Object.prototype 的原型是什么呢?它的原型是 nullnull 没有原型,所以这条原型链在此结束。

这就是原型链。

在 JavaScript 中,当我们试图访问一个对象的属性时,如果这个对象自身没有这个属性,那么 JavaScript 就会沿着原型链向上查找,直到找到这个属性或者达到 Object.prototype

如果 Object.prototype 也没有这个属性,那么返回 undefined

显式原型和隐式原型

显式原型

在 JavaScript 中,每个函数都有一个 prototype 属性,这个属性就是我们所说的显式原型。

这个 prototype 属性是一个对象,它包含了由该函数构造出来的所有实例共享的属性和方法。也就是说,当我们创建一个新的对象时,这个对象会自动获取 prototype 对象的所有属性和方法。

function Person() {}

Person.prototype.sayHello = function() {
  console.log('Hello!');
};

let person1 = new Person();
let person2 = new Person();

person1.sayHello(); // 输出:Hello!
person2.sayHello(); // 输出:Hello!

在这个例子中,Person.prototype 就是 Person 函数的显式原型。

sayHello 方法是定义在 Person.prototype 上的,所以所有 Person 的实例都能访问到这个方法。

隐式原型

在 JavaScript 中,每个对象都有一个 __proto__ 属性,这个属性就是我们所说的隐式原型。这个 __proto__ 属性指向了创建这个对象的函数的 prototype 属性。也就是说,__proto__ 就是这个对象的原型。

在上面的例子中,person1.__proto__person2.__proto__ 就是 person1person2 的隐式原型。它们都指向了 Person.prototype

这就是显式原型和隐式原型的区别。