对象的原型

机制和原理(对象基于原型)里所记述的那样,JavaScript是一个基于原型的面向对象的语言。 本文着重于对原型的实现机制进行剖析和说明。

 

原型链的实现

JavaScript里所有的对象都有一个名为__proto__的属性,这个属性里面存放的就是对象所参照的原型对象的引用。


__proto__中的对象连在一起就构成了一个原型链,链的顶端就是Object.prototype对象,Object.prototype的__proto__属性值则是null
__proto__属性被包含在ECMAScript6之中,但之前基本上已被大多数浏览器厂商所支持。
通过Object.getPrototypeOf()可以获得指定对象的原型对象,这也是被推荐的使用方法。但__proto__属性是可读写的,这也意味着程序可以通过该属性动态的改变对象的原型对象。

原型的自动设置

当通过构造函数创建新对象时,JavaScript会自动将构造函数的prototype属性值设置到新对象的__proto__属性里。

作为示例,我们首先声明一个类(构造函数)Person

var Person = function(name) {
  this.name = name;
};
Person.prototype.getName = function() {
  return this.name;
};

然后我们创建一个Person的对象。

var tom = new Person("Tom");

上面创建Person对象的代码与下面的程序逻辑是等价的,事实上JavaScript也是这样执行的。

var tom = new Object();
tom.__proto__ = Person.prototype;
tom = Person.call(tom,"Tom");

属性的继承

当访问对象的属性时,JavaScript会通过遍历原型链进行查找,直到找到给定名称的属性为止。

如果查找进行到原型链的顶部-Object.prototype仍然没有找到指定名称的属性时,就会返回undefined。

由于这个查值过程是一个遍历过程,所以当属性的值越靠近顶层,查找性能就会越低,而当值靠近底层时,查找性能就会高很多,所以在编写复杂的应用时,一定要提防原型链过长而带来的性能问题。

而设值对象属性则不会遍历原型链,而是直接将属性添加到该对象自身,并不影响到原型链中的对象。

//接上面的例子

//下面这条语句直接取的是tom对象的属性值
console.log(tom.name); // 输出:Tom

//下面这条语句执行的是tom.__proto__(引用的是Person.prototype)的getName方法
tom.getName(); // 输出:Tom