译文
对象(object)是Javascript的基本数据结构。直观上来说,对象表示的是一个关联字符串和值的表。但是当你深度挖掘下去,会发现有很多机制包含在对象当中。
就像很多面向对象语言一样,JavaScript提供对“实现继承”的支持:通过动态委托机制对代码和数据进行复用。但是和其他很多传统的语言不同,JavaScript的继承机制是基于原型(prototype)而非类(class)。对很多编程人员来说,JavaScript是他们遇见的第一种没有类这个概念的面向对象语言。
在很多语言中,每一个对象都其相关联的类的一个实例,而且该类为它所有的实例都提供了共享的内容。Javasript却相反,没有内建类这个概念。但是,一个对象可以继承另外一个对象。每一个对象都和一个叫做原型(prototype)的对象有关联。对原型的处理不同于对类的处理,但是传统的面向对象语言的许多概念依然延续了下来。
原型包含了三个有联系的访问器,它们的名字都由prototype这个单词变化而来。这给初学者带来了很多困惑,让我们直接划重点。
1. C.prototype用来建立通过new C()创建的对象的原型对象。
2. Object.getPrototypeOf(obj)是ES5中检索obj的原型对象的标准方法。
3. obj.__proto__是用来检索obj的原型对象的非标准方式。
想要分别理解它们,要考虑JavaScript数据类型的定义。User构造函数希望通过new操作符来创建一个能够存储名字和密码哈希值的对象。
function User(name,passwordHash){
this.name = name;
this.passwordHash = passwordHash;
}
User.prototype.toString = function(){
return "[User ]" + this.name + "]";
}
User.prototype.checkPassword = function(password){
return hash(password) === this.passwordHash;
}
var u = new User("zapper","0ef33ae79");
User构造器起初伴随着一个默认的几乎为空的prototype属性。在上面的例子中,我们给User的prototype属性中加入了两个方法(toString和checkPassword)。当我们通过new创建User的实例的时候,它的实例u就把User.prototype对象自动设为了它的prototype对象。
实例对象u和User.prototype之间表现的就是继承关系。u的属性查找就是先从自己当前的属性开始找起(如name属性和passwordHash属性)。属性没有直接被找到,就会从prototype中去寻找。调用u.checkPassword方法,就是从prototype中找到的。
虽然prototype属性用来给构造器建立实例的的prototype关系,但是ES5中的Object.getPrototypeOf()方法可以获取一个已经存在的实例对象的prototype对象:
Object.getPrototypeOf(u) === User.prototype; //true
一些运行环境下为获取实例对象的prototype提供了非标准的__proto__方法。这是不支持ES5的Object.getPrototypeOf()方法的环境下的权宜之计。在这样的环境下可以进行测试:
u.__proto__ === User.prototype; //true
最后需要注意的一点是,很多JavaScript程序员经常将User描述成类,即使仅仅是因为它比一个构造器更加强大。但是本质上来说,JavaScript中类的概念是联合了构造器本身以及原型对象存在的。
Things to remember
1. 在JavaScript中,类是一种包含构造器和原型对象的设计模式。