图解学习JavaScript第二部分翻译
作者:kun10 发布时间:January 28, 2011 分类:JavaScript
图解学习JavaScript第二部分翻译
原文分三篇,第一篇是说一些JavaScript的基础特性,第二篇是说JavaScript向对象的一些思路,第三篇是把JavaScript和Ruby做了对比。
我比较在意的第二部分。翻译一下试试。
原文地址:
http://howtonode.org/object-graphs-2
图解学习之二
第一篇幅用图描述了Javascript的语法,这篇我会讲解三种JavaScript用于对象创建的技巧,分别是
- 构造器+原型
- 纯原型链
- 对象工厂
我希望本文能够帮助人们理解每一个技巧的优点和缺点。
一、传统JavaScript构造器
我们先来创建一个简单的包含原型的构造器,这是你在原生JavaScript里面能找到的和类最相近的东西。他很强大。
但是,他又与传统的类语言有区别。
如下:
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.getArea = function getArea() {
return this.width * this.height;
};
Rectangle.prototype.getPerimeter = function getPerimeter() {
return 2 * (this.width + this.height);
};
Rectangle.prototype.toString = function toString() {
return this.constructor.name + " a=" + this.getArea() + " p=" + this.getPerimeter();
};
现在,我们再来创建一个新的类叫做Square,他继承于Rectangles。为此,Square的构造器的原型要继承父类的原型。
然后我们会演示如何重写getPerimeter方法。
function Square(side) {
this.width = side;
this.height = side;
}
Square.prototype.__proto__ = Rectangle.prototype;
Square.prototype.getPerimeter = function getPerimeter() {
return this.width * 4;
};
最后的调用也很简单,只要用new来创建一个实例。
var rect = new Rectangle(6, 4); var sqr = new Square(5); console.log(rect.toString()) console.log(sqr.toString())
传统构造器方式注意点:

对于上面的例子,注意rect实例和Square.prototype并不一样,虽然他们都是继承于Rectangle原型的简单对象,JavaScript的对象是一串链式的调用。
唯一特殊的对象是function,它们获取参数,并且掌握着内部执行的代码和作用域。
二、纯原型链式
我们依然来实现上面的例子,但我们这次不使用构造函数,这次我们只用原型链式继承。
我们先来定义一个Ractangle的原型作为我们所有对象的基础。
var Rectangle = {
name: "Rectangle",
getArea: function getArea() {
return this.width * this.height;
},
getPerimeter: function getPerimeter() {
return 2 * (this.width + this.height);
},
toString: function toString() {
return this.name + " a=" + this.getArea() + " p=" + this.getPerimeter();
}
};
现在我们再来定义一个子对象,它叫Square,并且重写了一些方法。
var Square = {
name: "Square",
getArea: function getArea() {
return this.width * this.width;
},
getPerimeter: function getPerimeter() {
return this.width * 4;
},
};
Square.__proto__ = Rectangle;
然后我们就来看看创建具体的对象,我们在原型对象的基础上创建新的对象,并手工的修改它们的本地属性。
var rect = Object.create(Rectangle); rect.width = 6; rect.height = 4; var square = Object.create(Square); square.width = 5; console.log(rect.toString()); console.log(square.toString());
输出:
Rectangle a=24 p=20 Square a=25 p=20
继续看图:

它并不如构造器+原型的方式来的强大,但是它直接易懂,如果你熟悉一个原型链式继承的语言,你会觉得很熟悉。
三、对象工厂
这是我很喜欢的方式————用一个工厂函数来创建对象的方式。它的不同在于,我只用简单的调用一个函数,它每次返回一个新的对象,而不是用一个原型对象供所有构造函数共享。
下面是一个很简单的MVC系统,controller函数接受表示model和view的两个参数,并输出一个新的controller对象。
这个过程中所有的状态都存贮在作用域的闭包里面。
function Controller(model, view) {
view.update(model.value);
return {
up: function onUp(evt) {
model.value++;
view.update(model.value);
},
down: function onDown(evt) {
model.value--;
view.update(model.value);
},
save: function onSave(evt) {
model.save();
view.close();
}
};
}
使用时只要简单的代入参数运行一下这个function。注意,现在我们不用先把工厂函数绑定给某个对象就可以直接传递函数给事件handlers(setTimeout)。
因为工厂函数没有在内部使用this,没有必要再去混淆this的值。
var on = Controller(
// Inline a mock model
{
value: 5,
save: function save() {
console.log("Saving value " + this.value + " somewhere");
}
},
// Inline a mock view
{
update: function update(newValue) {
console.log("View now has " + newValue);
},
close: function close() {
console.log("Now hiding view");
}
}
);
setTimeout(on.up, 100);
setTimeout(on.down, 200);
setTimeout(on.save, 300);
//输出
View now has 5
View now has 6
View now has 5
Saving value 5 somewhere
Now hiding view
上图:

此图对应了上面的调用过程,注意,我们可以通过工厂函数的隐藏作用域获取到两个匿名对象,也就是说,我们可以从工厂函数的闭包里面获取到model和view。