JavaScript new 运算符和构造函数
在 JavaScript 中可以使用 new 运算符搭配构造函数 (function constructor) 创建新对象。
构造函数 (function constructor) 是一个用来创造新对象的函数,需要和 new 运算符一起搭配使用。
举例来说,假设现在要用 Cat() 构造函数创造新对象,我们需要搭配 new 运算符一起调用构造函数 Cat(),调用 Cat() 时可以传入参数:
var kitty = new Cat("Kitty");
在 this 上定义对象属性
如果要定义对象的属性,需要在构造函数中修改 this 的属性 (property)。在构造函数中, this 代表我们要创造的新对象。
举例来说,假设对象需要有 name 属性,我们需要将构造函数的 name 参数指定给 this.name。我们可以定义 Cat 构造式如下:
// Constructor
function Cat(name) {
this.name = name;
}
搭配 new 运算符调用构造函数时,将 "Kitty" 当作构造函数的参数传入,则新对象的 name 属性即为 "Kitty"。我们可以调用 kitty.name 来验证:
var kitty = new Cat("Kitty");
console.log(kitty.name) // Kitty
在 prototype 上定义对象方法
要定义对象方法,需要将对象方法声明在构造函数的 prototype 属性里。构造函数的 prototype 属性,就是新对象的 prototype。
举例来说,如果 Cat 构造函数产生的对象都要有 speak() 方法,我们可以定义 Cat.prototype.speak():
// Define 'speak' method for Cat objects
Cat.prototype.speak = function() {
console.log(this.name + ": meow!");
};
接着用 new 运算符调用构造函数创造新对象,就可以在新对象上调用 speak() 方法:
var kitty = new Cat("Kitty");
kitty.speak(); // Kitty: meow!
原型继承
要了解 new 和构造函数的运作原理,首先我们要了解何谓原型继承 (prototypal inheritance)。
原型继承的意思是,JavaScript 中每个对象都有个 prototype 属性,对象能够继承 prototype 上的属性或方法。这个机制可以让我们产生继承自同一个 prototype 的多个对象,达到代码复用的效果。
用 new 运算符调用构造函数时背后发生了什么事?
当我们用 new 运算符调用构造函数 new Cat("Kitty") 的时候,JavaScript 引擎在背后做了几件事:
- 创建新对象。
- 将新对象的 prototype 指定为构造函数的
prototype属性。以上面的例子来说就是Cat.prototype。 - 将新对象绑定到
this对象,并调用构造函数。 - 在不特别指定
return值的情况下,返回刚创造的新对象。
因为新对象的原型是 Cat.prototype,所以新对象可以调用定义在 Cat.prototype 上的 speak() 方法。
使用 new 运算符与构造函数容易犯的错误
构造式必须和 new 运算符搭配使用,但万一我们忘了 new,直接调用构造函数:
var kitty = Cat("kitty");
此时并不会有任何错误或警告, this 会直接绑定全局变量,有可能会导致很难察觉的 bug!
用 Object.create() 创造新对象
ES5 中提供了 Object.create() 的方法,用途是创造新对象,并令其 prototype 等于第一个被传入的参数。
例如,我们想要创造很多猫对象,所以我们先创造一个对象 cat 来当作 prototype,里面定义了 speak() 方法:
var cat = {
speak: function() {
console.log(this.name + ": meow!");
}
};
当我们调用 Object.create(cat) 时,返回的新对象的 prototype 就是 cat。
// Create a new cat
var kitty = Object.create(cat);
kitty.name = "Kitty";
kitty.speak(); // Kitty: meow!
虽然 kitty 本身没有定义 speak() 方法,但它的 prototype (也就是 cat 对象) 定义了 speak() 方法,于是可以成功调用。
使用 Object.create() 的好处是,省去了可能会忘记用 new 调用构造式的风险。
Object.create() 的 polyfill
被传进作为参数的对象,将会被当成新对象的原型对象。所以 Object.create() 的 polyfill 可以这样写:
if (!Object.create) {
Object.create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
其中 F() 是构造函数,将构造函数的 prototype 设为传入的对象 o,并且由 new 运算符调用构造函数,产生新对象。
结论
JavaScript 中可以用构造函数搭配 new 运算符,或是 ES5 的 Object.create() 来创造新对象。
定义构造函数的必要步骤:
- 声明构造函数。
- 在构造函数中,将对象属性定义在
this上。 - 将对象方法定义在构造式的
prototype属性里。 - 用
new运算符调用构造函数。
使用 Object.create(obj) 方法创造的新对象,将继承自 obj。