深入学习JavaScript中的原型prototype(2)

JavaScript 中定义或实例化任何一个对象的时候,它都会被附加一个名为 __proto__ 的隐藏属性,原型链正是依靠这个属性才得以形成。但是千万别直接访问 __proto__ 属性,因为有些浏览器并不支持直接访问它。另外 __proto__ 和 对象的 prototype 属性也不是一回事,它们各自有各自的用途。

怎么理解呢?其实,当我们创建 myObject 函数时,实际上是创建了一个 Function 类型的对象:

console.log(typeof myObject); // function

这里要说明一下,Function 是 JavaScript 中预定义的一个对象,所以它也有自己预定义的属性(如 length 和 arguments)和方法(如 call 和 apply),当然也有 __proto__,以此实现原型链。也就是说,JavaScript 引擎内可能有类似如下的代码片段:

Function.prototype = { arguments: null, length: 0, call: function() { // secret code }, apply: function(){ // secret code }, ... };

事实上,JavaScript 引擎代码不可能这样简单,这里只是描述一下原型链是如何工作的。

我们定义了一个函数 myObject,它还有一个参数 name,但是并没有给它任何其它属性,例如 length 或者其它方法,如 call。那么下面这段代码为啥能正常执行呢?

console.log(myObject.length); // 结果:1,是参数的个数

这是因为我们定义 myObject 时,同时也给它定义了一个 __proto__ 属性,并赋值为 Function.prototype(参考前面的代码片段),所以我们能够像访问其它属性一样访问 myObject.length,即使我们并没有定义这个属性,因为它会顺着 __proto__ 原型链往上去找 length,最终在 Function 里面找到了。

那为什么找到的 length 属性的值是 1,而不是 0 呢,是什么时候给它赋值的呢?由于 myObject 是 Function 的一个实例:

console.log(myObject instanceof Function); // true console.log(myObject === Function); // false

当实例化一个对象的时候,对象的 __proto__ 属性会被赋值为其构造者的原型对象,在本示例中就是 Function,此时构造器回去计算参数的个数,改变 length 的值。

console.log(myObject.__proto__ === Function.prototype); // true

而当我们用 new 关键字创建一个新的实例时,新对象的 __proto__ 将会被赋值为 myObject.prototype,因为现在的构造函数为 myObject,而非 Function。

var myInstance = new myObject('foo'); console.log(myInstance.__proto__ === myObject.prototype); // true

新对象除了能访问 Function.prototype 中继承下来的 call 和 apply 外,还能访问从 myObject 中继承下来的 getName 方法:

console.log(myInstance.getName()); // foo var mySecondInstance = new myObject('bar'); console.log(mySecondInstance.getName()); // bar console.log(myInstance.getName()); // foo

其实这相当于把原型对象当做一个蓝本,然后可以根据这个蓝本创建 N 个新的对象。

再看一个多重prototype链的例子:

// 多重prototype链的例子 function Employee(name) { this.name = ""; this.dept = "general"; this.gender = "unknown"; } function WorkerBee() { this.projects = []; this.hasCar = false; } WorkerBee.prototype = new Employee; // 第一层prototype链 function Engineer() { this.dept = "engineer"; //覆盖了 "父对象" this.language = "javascript"; } Engineer.prototype = new WorkerBee; // 第二层prototype链 var jay = new Engineer("Jay"); if (flag) { alert(jay.dept); //engineer, 找到的是自己的属性 alert(jay.hasCar); // false, 搜索到的是自己上一层的属性 alert(jay.gender); // unknown, 搜索到的是自己上二层的属性 }

上面这个示例的对象关系如下:

201581395745486.jpg (222×362)

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wggssz.html