优雅的封装仍是执行的效率?这是一个悖论。
优雅封装的程序看起来是那么的美妙:每一个属性被隐藏在对象以后,你所能看到的就是这个对象让你看到的,至于它究竟是怎么操做的,这个不须要你操心。
执行的效率就是另一回事。就像是C语言和面向对象的C++之间的差异:C++很优雅,可是执行效率,不管是编译后的二进制代码仍是运行期的内存的占用,都要比简单的C语言多出一截来。
这个问题在脚本语言中显得更加剧要,由于JavaScript根本就是一种解释语言,解释语言的执行效率要比编译语言低不少。
1. 优雅的封装
咱们先来看看变量封装。这里的变量不单单是属性,也包括函数。
前面已经说过,JavaScript中并无类这个概念,是咱们利用变量做用域和闭包“巧妙的模拟”出来的,这是一种优雅的实现。仍是温故一下之前的代码:
function Person() {
var id;
var showId =
function() {
alert(
"My id is " + id);
}
this.getId =
function() {
return id;
}
this.setId =
function(newId) {
id = newId;
}
}
var p =
new Person();
p.setId(1000);
alert(p.id);
// undefined
// p.showId(); error: function not defined
var p2 =
new Person();
alert(p.getId == p2.getId);
// false
咱们很优雅的实现了私有变量——尽管是投机取巧的实现的。可是,这段代码又有什么问题呢?为何两个对象的函数是不一样的呢?
想一下,咱们使用变量的做用域模拟出私有变量,用闭包模拟出公有变量,那么,也就是说,实际上每一个建立的对象都会有一个相同的代码的拷贝!不单单是那个id,就连那些showId、getId 等函数也会建立屡次。注意,考虑到JavaScript函数就是对象,就不会感到那么奇怪了。可是毫无疑问,这是一种浪费:每一个变量所不一样的只是本身的数据域,函数代码都是相同的,由于咱们进行的是同一种操做。其余语言通常不会遇到这种问题,由于那些语言的函数和对象的概念是不一样的,像Java,每一个对象的方法其实指向了同一份代码的拷贝,而不是每一个对象都会有本身的代码拷贝。
2. 去看效率
那种封装虽然优雅,可是很浪费。好在JavaScript是一种灵活的语言,因而,咱们立刻想到,把这些函数的指针指向另外的一个函数不就能够了吗?
function show() {
alert(
"I'm a person.");
}
function Person() {
this.show = show;
}
var p1 =
new Person();
var p2 =
new Person();
alert(p1.show == p2.show);
// true
这个办法不错,解决了咱们之前的那个问题:不一样的对象共享了一份代码。可是这种实现虽然有了效率,但是却太不优雅了——若是我有不少类,那么岂不是有不少全局函数?
好在JavaScript中还有一个机制:prototype。还记得这个prototype吗?每一个对象都维护着一个prototype属性,这些对象的prototype属性是共享的。那么,咱们就能够把函数的定义放到prototype里面,因而,不一样的对象不就共享了一份代码拷贝吗?事实确实如此:
function Person() {
}
Person.prototype.show =
function() {
alert(
"I'm a person.");
}
var p1 =
new Person();
var p2 =
new Person();
alert(p1.show == p2.show);
// true
不过,这种分开定义看上去很别扭,那么好,为何不把函数定义也写到类定义里面呢?
function Person() {
Person.prototype.show =
function() {
alert(
"I'm a person.");
}
}
var p1 =
new Person();
var p2 =
new Person();
alert(p1.show == p2.show);
// true
实际上这种写法和上面一种没有什么不一样:惟一的区别就是代码位置不一样。这只是一个“看上去很甜”的语法糖,并无实质性差异。
最初,微软的.Net AJAX框架使用前面的机制模拟了私有变量和函数,这种写法和C#很相像,十分的优雅。可是,处于效率的缘故,微软后来把它改为了这种原型的定义方式。虽然这种方式不那么优雅,可是颇有效率。
在JavaScript中,这种封装的优雅和执行的效率之间的矛盾一直存在。如今咱们最好的解决方案就是把数据定义在类里面,函数定义在类的prototype属性里面。