在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 的原型是什么呢?它的原型是 null。null 没有原型,所以这条原型链在此结束。
这就是原型链。
在 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__ 就是 person1 和 person2 的隐式原型。它们都指向了 Person.prototype。
这就是显式原型和隐式原型的区别。