[JavaScript]new运算符及构造函数的用法

JavaScript new 运算符和构造函数

在 JavaScript 中可以使用 new 运算符搭配构造函数 (function constructor) 创建新对象。

构造函数 (function constructor) 是一个用来创造新对象的函数,需要和 new 运算符一起搭配使用。

举例来说,假设现在要用 Cat() 构造函数创造新对象,我们需要搭配 new 运算符一起调用构造函数 Cat(),调用 Cat() 时可以传入参数:

javascript

var kitty = new Cat("Kitty");

在 this 上定义对象属性

如果要定义对象的属性,需要在构造函数中修改 this 的属性 (property)。在构造函数中, this 代表我们要创造的新对象。

举例来说,假设对象需要有 name 属性,我们需要将构造函数的 name 参数指定给 this.name。我们可以定义 Cat 构造式如下:

javascript

// Constructor
function Cat(name) {
	this.name = name;
}

搭配 new 运算符调用构造函数时,将 "Kitty" 当作构造函数的参数传入,则新对象的 name 属性即为 "Kitty"。我们可以调用 kitty.name 来验证:

javascript

var kitty = new Cat("Kitty");
console.log(kitty.name) // Kitty

在 prototype 上定义对象方法

要定义对象方法,需要将对象方法声明在构造函数的 prototype 属性里。构造函数的 prototype 属性,就是新对象的 prototype。

举例来说,如果 Cat 构造函数产生的对象都要有 speak() 方法,我们可以定义 Cat.prototype.speak()

javascript

// Define 'speak' method for Cat objects
Cat.prototype.speak = function() {
	console.log(this.name + ": meow!");
};

接着用 new 运算符调用构造函数创造新对象,就可以在新对象上调用 speak() 方法:

javascript

var kitty = new Cat("Kitty");
kitty.speak(); // Kitty: meow!

原型继承

要了解 new 和构造函数的运作原理,首先我们要了解何谓原型继承 (prototypal inheritance)。

原型继承的意思是,JavaScript 中每个对象都有个 prototype 属性,对象能够继承 prototype 上的属性或方法。这个机制可以让我们产生继承自同一个 prototype 的多个对象,达到代码复用的效果。

用 new 运算符调用构造函数时背后发生了什么事?

当我们用 new 运算符调用构造函数 new Cat("Kitty") 的时候,JavaScript 引擎在背后做了几件事:

  1. 创建新对象。
  2. 将新对象的 prototype 指定为构造函数的 prototype 属性。以上面的例子来说就是 Cat.prototype
  3. 将新对象绑定到 this 对象,并调用构造函数。
  4. 在不特别指定 return 值的情况下,返回刚创造的新对象。

因为新对象的原型是 Cat.prototype,所以新对象可以调用定义在 Cat.prototype 上的 speak() 方法。

使用 new 运算符与构造函数容易犯的错误

构造式必须和 new 运算符搭配使用,但万一我们忘了 new,直接调用构造函数:

javascript

var kitty = Cat("kitty");

此时并不会有任何错误或警告, this 会直接绑定全局变量,有可能会导致很难察觉的 bug!

用 Object.create() 创造新对象

ES5 中提供了 Object.create() 的方法,用途是创造新对象,并令其 prototype 等于第一个被传入的参数。

例如,我们想要创造很多猫对象,所以我们先创造一个对象 cat 来当作 prototype,里面定义了 speak() 方法:

javascript

var cat = {
	speak: function() {
		console.log(this.name + ": meow!");
	}
};

当我们调用 Object.create(cat) 时,返回的新对象的 prototype 就是 cat

javascript

// 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 可以这样写:

javascript

if (!Object.create) {
	Object.create = function(o) {
		function F() {}
		F.prototype = o;
		return new F();
	};
}

其中 F() 是构造函数,将构造函数的 prototype 设为传入的对象 o,并且由 new 运算符调用构造函数,产生新对象。

结论

JavaScript 中可以用构造函数搭配 new 运算符,或是 ES5 的 Object.create() 来创造新对象。

定义构造函数的必要步骤:

  1. 声明构造函数。
  2. 在构造函数中,将对象属性定义在 this 上。
  3. 将对象方法定义在构造式的 prototype 属性里。
  4. new 运算符调用构造函数。

使用 Object.create(obj) 方法创造的新对象,将继承自 obj

标签

发表评论