深入解析JavaScript原型链与继承机制

作者:代码拾荒

时间:2026-10-27

浏览:615

本文深入解析JavaScript中对象与prototype的关系,阐述原型链继承的实现原理。通过代码示例分析new操作符的执行步骤,探讨直接赋值prototype导致的constructor指向错误问题,并介绍使用Object.getPrototypeOf()区分父类与实例原型的最佳实践,帮助开发者理清继承关系中的矛盾与解决方案。

文章目录

JavaScript原型与对象基础

在JavaScript中,对象通常由一个指向prototype的指针以及自身的属性列表构成。JavaScript在创建对象时遵循写时复制的理念。只有构造函数具备prototype属性,而原型链继承的本质是创建一个新的指针,使其指向该构造函数的prototype属性。prototype属性之所以特殊,是由JavaScript读取属性时的遍历机制所决定的,本质上它仍是一个普通指针。常见的内置构造函数包括:1.Object、2.Function、3.Array、4.Date、5.String。

接下来通过具体示例进行说明。

具体代码实现如下:

<script>
//每个function都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数
//注意Person.constructor 不等于 Person.prototype.constructor. Function实例自带constructor属性
function Person(name) {
this.name = name;
    };  
Person.prototype.getName = function() {
return this.name;
    };  
var p = new Person("ZhangSan");
console.log(Person.prototype.constructor === Person); // true
console.log(p.constructor === Person); // true ,这是因为p本身不包含constructor属性,所以这里其实调用的是Person.prototype.constructor
</script>

原型链继承的实现与矛盾

我们的目标有两个: 1. 表明Person继承自Animal 2. 表明p2是Person的实例

我们需要修改prototype属性的指向,使Person能够获取Animal中prototype属性包含的方法,即实现Person对Animal的继承(类比“人是野兽”)。

具体代码如下:

<script>
function Person(name) {
this.name = name;
    };  
Person.prototype.getName = function() {
return this.name;
    };  
var p1 = new Person("ZhangSan");
console.log(p.constructor === Person); // true
console.log(Person.prototype.constructor === Person); // true
function Animal(){ } 
 Person.prototype = new Animal();//之所以不采用Person.prototype = Animal.prototype,是因为new 还有其他功能,最后总结。 
 var p2 = new Person("ZhangSan"); 
//(p2 -> Person.prototype -> Animal.prototype, 所以p2.constructor其实就是Animal.prototype.constructor)
 console.log(p2.constructor === Person); // 输出为false ,但我们的本意是要这里为true的,表明p2是Person的实例。此时目的1达到了,目的2没达到。 
</script>

解决constructor指向与继承关系的冲突

如果进行如下修正: Person.prototype = new Animal(); Person.prototype.constructor = Person; 此时p2.constructor指向正确,即Person,表明p2是Person类的实例,但引发新问题:目的2达成,目的1(继承关系)未达成。目的1和目的2在此时相互矛盾,因为prototype同时表达了两个含义: 1. 表示父类是谁 2. 作为自己实例的原型来复制 因此,不能直接使用prototype属性来表示父类是谁,而应使用getPrototypeOf()方法来确认父类身份。

具体代码示例如下:

Person.prototype = new Animal();
Person.prototype.constructor = Person;
var p2 = new Person("ZhangSan");
p2.constructor //显示 function Person() {}
Object.getPrototypeOf(Person.prototype).constructor //显示 function Animal() {}

这样就将这两个概念分离开了。

new操作符执行过程总结

最后进行总结: 当执行代码var p = new Person()时,new操作符执行了以下操作:

创建一个空白对象

创建一个指向Person.prototype的指针

将这个对象通过this关键字传递到构造函数中并执行构造函数。

如果采用Person.prototype = Animal.prototype来表示Person继承自Animal,instanceof方法同样会显示p也是Animal的实例,返回true。之所以不采用此方法,主要基于以下两个原因:

1. new 创建了一个新对象,这样就避免了设置Person.prototype.constructor = Person 的时候也会导致Animal.prototype.constructor的值变为Person,而是动态给这个新创建的对象一个constructor实例属性,这样实例上的属性constructor就覆盖了Animal.prototype.constructor,这样Person.prototype.constructor和Animal.prototype.contructor就分开了。

2. Animal自身的this对象的属性没办法传递给Person

通过使用 hasOwnProperty()方法,什么时候访问的是实例属性,什么时候访问的是原型属性就 一清二楚了。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章