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("sfalken", "0ef33ae791068ec64b502d6cb0191387"); User函数拥有一个默认的prototype属性,该属性的值是一个空对象。在以上的例子中,向prototype对象添加了两个方法,分别是toString和checkPassword。当调用User构造函数得到一个新的对象u时,它的原型对象会被自动赋值到User.prototype对象。即u.prototype === User.prototype会返回true。
上图中的箭头表示的是继承关系。当访问u对象的某些属性时,会首先尝试读取u对象上的属性,如果u对象上并没有这个属性,就会查找其原型对象。
比如当调用u.checkPassword()时,因为checkPassword定义在其原型对象上,所以在u对象上找不到该属性,就在u的原型上查找,查找顺序是u-> u.prototype(User.prototype)。
前面提到过,getPrototypeOf方法是ES5中用来得到某个对象的原型对象的标准方法。因此:
Object.getPrototypeOf(u) === User.prototype; // true在一些环境中,同时提供了一个非标准的_ proto _ 属性用来得到某个对象的原型对象。当环境不提供ES5的标准方法getPrototypeOf方法时,可以暂时使用该属性作为替代。可以使用下面的代码测试环境中是否支持_ proto _:
u.__ proto __ === User.prototype; // true所以在JavaScript中,类的概念是由构造函数(User)和用于实例间共享方法的原型对象(User.prototype)共同完成的。构造函数中负责构造每个对象特有的属性,比如上述例子中的name和password属性。而其原型对象中负责存放所有对象共有的属性,比如上述例子中的checkPassword和toString方法。就像下面这张图表示的那样:

二、获取对象优先使用Object.getPrototypeOf,而不是_ proto _
在ES5中引入了Object.getPrototypeOf作为获取对象原型对象的标准API。但是在很多执行环境中,也提供了一个特殊的_ proto _属性来达到同样的目的。
因为并不是所有的环境都提供了这个_ proto _属性,且每个环境的实现方式各不相同,因此一些结果可能不一致,例如,对于拥有null原型的对象:
// 在某些环境中 var empty = Object.create(null); // object with no prototype "_proto _" in empty; // false (in some environments) // 在某些环境中 var empty = Object.create(null); // object with no prototype "_proto_" in empty; // true (in some environments)所以当环境中支持Object.getPrototypeOf方法时,优先使用它。即使不支持,也可以为了实现一个:
if (typeof Object.getPrototypeOf === "undefined") {Object.getPrototypeOf = function(obj) { var t = typeof obj; if (!obj || (t !== "object" && t !== "function")) {throw new TypeError("not an object"); } return obj._proto_;}; } 上述代码首先会对当前环境进行检查,如果已经支持了Object.getPrototypeOf,就不会再重复定义。function User(name, passwordHash) {this.name = name;this.passwordHash = passwordHash; } 如果在调用上述构造函数时,忘记了使用new关键字,那么:var u = User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); u; // undefined this.name; // "baravelli" this.passwordHash; // "d8b74df393528d51cd19980ae0aa028e"可以发现得到的u是undefined,而this.name以及this.passwordHash则被赋了值。但是这里的this指向的则是全局对象。
function User(name, passwordHash) {"use strict";this.name = name;this.passwordHash = passwordHash; } var u = User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); // error: this is undefined 那么在忘记使用new关键字的时候,在调用this.name= name的时候会抛出TypeError错误。这是因为在strict模式下,this的默认指向会被设置为undefined而不是全局对象。function User(name, passwordHash) {if (!(this instanceof User)) { return new User(name, passwordHash);}this.name = name;this.passwordHash = passwordHash; } var x = User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); var y = new User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); x instanceof User; // true y instanceof User; // true 以上的if代码块就是用来处理没有使用new进行调用的情况的。当没有使用new时,this的指向并不是一个User的实例,而在使用了new关键字时,this的指向是一个User类型的实例。function User(name, passwordHash) {var self = this instanceof User ? this : Object.create(User.prototype);self.name = name;self.passwordHash = passwordHash;return self; } Object.create方法是ES5提供的方法,它能够接受一个对象作为新创建对象的prototype。那么在非ES5环境中,就需要首先实现一个Object.create方法:if (typeof Object.create === "undefined") {Object.create = function(prototype) { function C() { } C.prototype = prototype; return new C();}; } 实际上,Object.create方法还有接受第二个参数的版本,第二个参数表示的是在新创建对象上赋予的一系列属性。