本文深入解析JavaScript中的原型与原型链机制。首先对比工厂方法与构造器方法两种对象创建方式,详细剖析new关键字的执行步骤及this指向。随后阐述原型的定义、constructor属性关联以及__proto__与[[Prototype]]的关系,最后通过属性查找规则完整揭示原型链在继承中的作用。
JavaScript对象创建基础
正如某些面向对象语言所具备的功能,在JavaScript中我们有时需要创建一个包含公共函数与属性的父类,以此减少代码重复、实现类型检查并构建更清晰的代码结构。在JavaScript中,继承机制是通过原型链来实现的。在深入理解JavaScript的继承与原型链之前,首先需要掌握JavaScript中对象的创建方式。
在JavaScript中创建对象
JavaScript中对象的创建方式主要有两种:工厂方法(Factory Functions)和构造器方法(Constructor Functions)。
工厂方法
工厂方法在编程领域是一种非类或非构造器的返回对象的方法。在JavaScript中,任何返回不使用new关键词创建对象的方法都被称为工厂方法。
工厂方法与构造器方法对比
function person(firstName, lastName, age) {
const person = {};
person.firstName = firstName;
person.lastName = lastName;
person.age = age;
return person;
}
构造器方法
构造器方法与工厂方法的区别主要在于用例和命名规范。在命名规范上,构造器方法的名称首字母需要大写,且必须通过new关键词来调用构造器方法以生成实例。生成后的实例可以通过instanceof关键词进行检查。
function Person(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
new的行为
当在工厂方法和构造器方法上同时使用new关键词创建对象时,工厂方法创建出的对象的__proto__属性指向Object.prototype,而构造器方法创建出的对象的__proto__属性则指向其自身的Xxx.prototype。
new关键字的执行机制
const mike = new person('mike', 'grand', 23);
mike.__proto__ // Object.prototype
const jack = new Person('jack', 'grand', 23);
jack.__proto__ // Person.protytype这里的prototype指向Person的Prototype Object
jack.__proto__.__proto__ // Object.prototype
new关键词在后台为构造器方法执行了以下几步
- 在构造器方法内部创建一个新对象,并将其赋值给this。
- 设置对象的[[Prototype]]和__proto__为原型的构造函数,这一步使得新对象的构造函数在构造新对象时被添加到原型链上。
- 如果该方法内部没有返回object、function或array类型的结果,则返回this。
- 如果该方法内部没有返回任何值,则返回this。
下面是一个展示new关键词在JavaScript引擎中执行效果的伪代码,注释部分用于示范new关键词添加语句的伪代码。
function Person(firstName, lastName, age) {
// this = {};
// this.__proto__ = Person.prototype;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
// return this;
}
由于构造器方法上没有显式返回值,后台创建的this将被返回;而工厂方法内部由于返回了对象,因此后台不再添加return this,导致自然返回的内容不一致。
如果没有在构造器方法前使用new关键词,而是直接调用执行构造器方法,其仅作为一个普通方法来执行,而非作为构造器。
const bob = Person('bob', 'grand', 23);
bob // undefined. 因为这里Person当作方法直接调用了且没有返回值
window.firstName // bob. 函数内的this将指向全局作用域,导致意外操作
继承与原型链
原型
原型(Prototype)可以视为JavaScript方法的一个属性。每次在JavaScript代码中创建方法时,JavaScript引擎会自动添加一个名为prototype的属性。这个prototype属性是一个对象(即原型对象),该对象默认包含一个constructor属性,指向原方法对象。任何添加到prototype的属性和方法都位于这个对象中,该类的所有实例共享这个原型对象。实例对象的__proto__属性指向这个对象,方法的prototype属性也指向这个对象。
在ECMAScript标准中,object.[[Prototype]]是访问原型的方法,但在ECMAScript 2015中,建议使用Object.getPrototypeOf()和Object.setPrototypeOf()来替代。等价的__proto__是多数浏览器使用的事实上的、但非标准的实现。
原型的定义与访问
function Person(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
Person.prototype === Person.prototype.constructor.prototype // 指向Person的原型对象
Person.prototype.constructor === Person // 指向Person方法对象
let bob = new Person("Bob", "Ross", 21);
Person.prototype === bob.__proto__; // true
let alex = new Person("Alex", "Wang", 21);
Person.prototype === alex.__proto__; // true
alex.__proto__ === bob.__proto__; // true
原型链
首先我们需要了解对象的查找机制。当我们访问一个对象的属性时,JavaScript引擎会首先查找该对象本身是否包含对应属性;如果没有,则去对象的原型中查找;如果仍未找到,则继续去对象的原型对象的原型对象中查找,直至查询到对象的__proto__为null时停止。

原型链的查找规则
const obj = {};
console.log(obj); // [object Object] obj的toString()方法从Object的原型中查找到并使用
function Person(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
// 在Person.prototype上定义了toString覆写了Object.prototype上的toString
Person.prototype.toString = function() {
return `${this.firstName} It Is`;
}
}
let bob = new Person("Bob", "Ross", 21);
let alex = new Person("Alex", "Wang", 21);
console.log(bob); // Bob It Is
console.log(alex); // Alex It Is
以上就是详解JavaScript原型与原型链的详细内容,更多关于JavaScript原型与原型链的资料请关注本站其它相关文章!