了解prototype,getPrototypeOf和proto的不同
- C.prototype是用来建立与用new C()生成的对象实例的原型关系
- Object.getPrototypeOf(obj)是ES5用来获取某个对象原型的方法
- obj._proto_是某些浏览器获取某个对象原型的方式,该属性并未纳入到标准中
拿代码来举例
function Foo(name, passwordHash) {
this.name = name;
this.passwordHash = passwordHash;
}
Foo.prototype.toString = function() {
return "[User "+ this.name + "]";
};
Foo.prototype.checkPassword = function(password) {
return hash(password) === this.passwordHash;
};
var u = new Foo("sfalken","0ef33ae791068ec64b502d6cb0191387");
Object.getPrototypeOf(u) === Foo.prototype; // true
u.__proto__ === Foo.prototype; // true 某些提供了__proto__
如果说Foo是Class的话,那么Foo.prototype就是这个函数的原型,并被所有实例继承,某些js引擎中实例访问原型能通过 proto属性,再拿一副图来说明
一般人认为Foo就是一个Class,但其实Foo函数和它的原型对象Foo.prototype共同组合起来才有点像是传统意义上的类,User 函数有点像构造函数,而Foo.prototype提供了继承机制,使得各个实例中可以共享方法
另外通过上图可知Foo.prototype.constructor反过来指向了Foo函数
通过new实例出来的构造函数的默认返回值是this指向的对象,如果显示返回其他对象则返回其他对象,如果返回非Obect类型的如string等,则仍然默认返回this指向的对象
如果使用Foo函数的时候忘记new关键字是件很可怕的事情,那样的话Foo函数会返回undefined,并且this指向了全局的window
function User(name, passwordHash) {
this.name = name;
this.passwordHash = passwordHash;
}
var u = User("baravelli","d8b74df393528d51cd19980ae0aa028e");
u; // undefined
this.name; // "baravelli"
this.passwordHash;// "d8b74df393528d51cd19980ae0aa028e"
如果使用ES5的严格模式,this会为undefined,设置this.name会报错
function User(name, passwordHash) {
"use strict";
this.name = name;//Uncaught TypeError: Cannot set property 'name' of undefined
this.passwordHash = passwordHash;
}
var u = User("baravelli","d8b74df393528d51cd19980ae0aa028e");
// error: this is undefined
为了使得代码更健壮,无论是否遗漏new关键字都能当作构造函数运行,使用如下的方法
function User(name, passwordHash) {
if(!(this instanceofUser)) {
return new User(name, passwordHash);
}
this.name = name;
this.passwordHash = passwordHash;
}
另外一种借助ES5的方式如下
function User(name, passwordHash) {
var self = this instanceof User ? this: Object.create(User.prototype);//Object.create接受一个对象作为原型并返回一个继承自该原型的对象
self.name = name;
self.passwordHash = passwordHash;
return self;
}
对于不支持ES5的浏览器,自己实现Object.create方法
if(typeofObject.create === "undefined") {
Object.create = function(prototype) {//注意这里只实现了一个参数的情况,ES5的create后面有第2个可选参数
function C() { }
C.prototype = prototype;
return new C();
};
}
尽量使用Object.getPrototypeOf,而不要使用 _ proto_
ES5中标准的获取prototype的方法是Object.getPrototypeOf,但是有些js引擎提供了_ proto_ 属性来实现相同的功能,有些则没有
根据js引擎环境的不同,还有些兼容性方面的差异,有些浏览器下,_ proto是继承自Object.prototype,因此如果某个object的原型为null,他们它应该没有 _ proto __
var empty = Object.create(null);// object with no prototype
"__proto__" in empty; // false (in some environments)
` 但是有些js引擎环境会无论object的状态都会处理_ proto _
var empty = Object.create(null);// object with no prototype
"__proto__" in empty; // true (in some environments)
以后的ES规范可能会禁用掉_ proto _,因此为了我们的代码足够兼容而且健壮,尽可能的使用Object.getPrototypeOf,除非浏览器不支持此方法
if(typeof Object.getPrototypeOf === "undefined") {
Object.getPrototypeOf = function(obj) {
var t = typeofobj;
if(!obj || (t !== "object"&& t !== "function")) {
throw newTypeError("not an object");
}
return obj.__proto__;
};
}
不要手动修改 _ proto _
直接修改proto属性会影响对象的整个继承链,同时会产生性能影响,而且由于修改了proto原型链,可能产生未预期的一些行为,如果真的想给某个对象设置原型链,使用ES5的Object.create方法