本文深入解析JavaScript原型链的核心机制,详细拆解new操作符的三步执行过程,重点阐述__proto__在属性查找中的关键作用。通过代码示例演示原型链的构建与属性访问路径,揭示prototype作为辅助角色与__proto__作为本质连接的关系,帮助开发者彻底理解对象继承原理。
new操作符的执行步骤解析
首先观察以下代码片段:
代码如下:
<script type="text/javascript">
var Person = function () { };
var p = new Person();
</script> 这段代码非常基础,但我们需要深入探究new关键字具体执行了哪些操作。可以将new的过程分解为以下三个步骤:
<1> var p={}; 即初始化创建一个空对象p。
<2> p.__proto__=Person.prototype; 将实例的__proto__指向构造函数的prototype。
<3> Person.call(p); 即调用构造函数初始化p,这一步也可以理解为对p进行初始化。
其中的关键步骤是第二步,我们可以通过代码来验证其正确性:
代码如下:
<script type="text/javascript">
var Person = function () { };
var p = new Person();
alert(p.__proto__ === Person.prototype);
</script> 执行这段代码会返回true,证实了步骤2的描述是正确的。
__proto__与原型链的基本概念
那么__proto__究竟是什么?这里简要说明:每个对象内部都会初始化一个名为__proto__的属性。当访问对象的某个属性时,如果该对象自身不存在此属性,引擎就会去__proto__指向的对象中查找。而__proto__指向的对象又拥有自己的__proto__,如此层层递进查找,便形成了我们常说的原型链。
按照ECMAScript标准,__proto__属于非标准私有属性,不应当对外公开。然而,Firefox浏览器引擎将其暴露为公共属性,允许外部访问和设置。
原型链属性查找机制示例
概念阐述清楚后,让我们通过以下代码进行实践:
代码如下:
<script type="text/javascript">
var Person = function () { };
Person.prototype.Say = function () {
alert("Person say");
}
var p = new Person();
p.Say();
</script> 这段代码十分简单,相信大家都写过类似的代码。现在我们来分析为什么实例p能够访问Person构造函数原型上的Say方法。
首先,通过var p=new Person()可知p.__proto__=Person.prototype。当我们调用p.Say()时,引擎首先在p自身查找Say属性,未找到后,便沿着__proto__指向Person.prototype进行查找。由于我们在前文定义了Person.prototype.Say=function(){},因此成功找到了该方法。
复杂原型链推导与总结
接下来,我们看一个更复杂的继承场景:
代码如下:
<script type="text/javascript">
var Person = function () { };
Person.prototype.Say = function () {
alert("Person say");
}
Person.prototype.Salary = 50000;
var Programmer = function () { };
Programmer.prototype = new Person();
Programmer.prototype.WriteCode = function () {
alert("programmer writes code");
};
Programmer.prototype.Salary = 500;
var p = new Programmer();
p.Say();
p.WriteCode();
alert(p.Salary);
</script> 我们来逐步推导其原型链结构:
由var p=new Programmer()可知p.__proto__=Programmer.prototype;。
而在前文中我们指定了Programmer.prototype=new Person();。我们可以将其拆解为:var p1=new Person(); Programmer.prototype=p1; 此时:
p1.__proto__=Person.prototype;
Programmer.prototype.__proto__=Person.prototype;
结合前文得到的p.__proto__=Programmer.prototype,可以推导出p.__proto__.__proto__=Person.prototype。
理清关系后,我们来看上述代码的执行结果。调用p.Say()时,由于p自身没有Say属性,引擎前往p.__proto__(即Programmer.prototype,也就是p1)查找,发现p1中也没有Say,于是继续前往p.__proto__.__proto__(即Person.prototype)查找,最终找到了alert(“Person say”)的方法。
其他属性的查找逻辑也是同样的道理。
这就是原型链的实现原理。
最后需要明确的是,prototype实际上只是一个辅助概念,它在原型链构建中主要起到桥梁作用,特别是在new操作时具有特定价值。而原型链的本质连接,其实在于__proto__!