function User(name, passwordHash) {
this.name = name;
this.passwordHash = passwordHash;
}
当使用User函数创建一个构造函数时,程序依赖于调用者是否记得使用new操作符来调用该构造函数。注意的是,该函数假设接收者是一个全新的对象。
如果调用者忘记使用new
关键字,那么函数的接收者将是全局对象。
// undefined
var u = User('zhangsan', 'djkgjk23lkl4332l3k289ds');
该函数不但会返回无意义的undefined
,而且会灾难性地创建全局变量name和passwordHash。
如果将User函数定义为ES5的严格代码,那么接收者默认认为是undefined
。
function User(name, passwordHash) {
'use strict';
// Cannot set property 'name' of undefined
this.name = name;
this.passwordHash = passwordHash;
}
var u = User('zhangsan', 'djkgjk23lkl4332l3k289ds');
在这种情况下,这种错误的调用会导致一个即使错误:Cannot set property 'name' of undefined
。
一个更为健壮的方式是提供一个不管怎样调用都工作如构造函数的函数。实现这样的函数一个简单的方法是检查函数的接收者是否是一个正确的User实例。
// 检查函数的接受者是否是一个正确的User实例
// 缺点,不适应于可变参数函数
function User(name, passwordHash) {
if (!(this instanceof User)) {
return new User(name, passwordHash);
}
this.name = name;
this.passwordHash = passwordHash;
}
// User {name: "zhangsan", passwordHash: "djkgjk23lkl4332l3k289ds"}
var u = User('zhangsan', 'djkgjk23lkl4332l3k289ds');
这种模式的一个缺点是它需要额外的函数调用,而且不适应可变参数函数(因为没有直接模拟apply方法将可变参数函数作为构造函数调用的方法)。一种更为高效的方式是利用ES5的Object.create
函数。
function User(name, passwordHash) {
var self = this instanceof User ? this : Object.create(User.prototype);
console.log(self);
self.name = name;
self.passwordHash = passwordHash;
return self;
}
// User {name: "zhangsan", passwordHash: "djkgjk23lkl4332l3k289ds"}
var u = User('zhangsan', 'djkgjk23lkl4332l3k289ds');
Object.create
需要一个原型对象作为参数,并返回一个继承自该原型对象的新对象。
Object.create
只有在ES5环境下才是有效的,这里提供了一个兼容版的。
if (typeof Object.create === 'undefined') {
Object.create = function(prototype) {
var C = function() {};
C.prototype = prototype;
return new C;
}
};
这里只实现了单参数版本的Object.create
函数。
参考:编写高质量JavaScript代码的68个有效方法