JavaScript面向对象程序设计(8): 优雅的封装还是执行的效率?
优雅的封装还是执行的效率?这是一个悖论。
?
优雅封装的程序看起来是那么的美妙:每个属性被隐藏在对象之后,你所能看到的就是这个对象让你看到的,至于它到底是怎么操作的,这个不需要你操心。
?
执行的效率就是另外一回事。就像是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 definedvar 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属性里面。
var Person = function(){ this.name = null; this.sex = null;}Person.prototype = { getName:function(){ return this.name; }, getSex:function(){ return this.sex; }}这么烂的你也好意思贴出来,都被js书用烂了的例子
神仙你从哪飞来,吃了火药了,一上来就喷人,你NB,自己写个模式出来看看
这模式在YUI中大量应用,你的意思是YUI的代码页烂了哦var person = new Object()person.name = 'xxxx'person.sex = 'y'.............
我只喜欢这么搞。
var person = new Object()person.name = 'xxxx'person.sex = 'y'.............
我只喜欢这么搞。
还是太嗦了,要这么搞:
var person = {}; obj.name = "xxx"; obj.sex = "yyy"; obj.myFunction = function(){ alter(obj.name); } var ePer = {}; ePer ['field'] = 'field'; ePer.myFunction = function(){ alter(ePer .'field') }; //继承 - (重载) ePer.extendBy = function(parant){ for(var i in person ){ if(parant[i]=="undefine")continue; extendPer[i] = parant[i]; } } //进行继承 或重载。呵呵 ePer.extendBy(person); var person = new Object()person.name = 'xxxx'person.sex = 'y'.............我只喜欢这么搞。
我那个代码意在表示类属性写在function里,而方法写在prototype里,怪我例子写的不明显导致感觉是在模拟pojovar p1 = new Person();var p2 = new Person();alert(p1.show == p2.show); // true
这么写确实得到的是true
但是 差别是有的 运行这个试试
var p1 = new Person();var show = p1.show;var p2 = new Person();alert(show == p2.show); // false
注意别改变第二三行的顺序
原因是在new p2的时候 把prototype的show换掉了
而p1.show和p2.show实际上都是prototype.show 相等是当然的
var p1 = new Person();var show = p1.show;var p2 = new Person();alert(show == p2.show); // false
楼上有没有跑一下啊, 这个怎么执行都是true.
关于优雅的问题,实在是没有规定说写成POJO那样就算优雅,关键还是在于习惯。prototype的方式,既好理解,又能方便重用,更易于扩展,是在没理由说它不优雅。var p1 = new Person();var show = p1.show;var p2 = new Person();alert(show == p2.show); // false
楼上有没有跑一下啊, 这个怎么执行都是true.
关于优雅的问题,实在是没有规定说写成POJO那样就算优雅,关键还是在于习惯。prototype的方式,既好理解,又能方便重用,更易于扩展,是在没理由说它不优雅。
当然跑过 我发的帖子是针对楼主所说的
function Person() { Person.prototype.show = function() { alert("I'm a person."); }}var p1 = new Person();var p1_show = p1.show;var p2 = new Person();alert(p1_show == p2.show); // falsealert(p1.show == p2.show); // true 43 楼 sankooc 2010-01-05 小问一下js里等号比较的是什么 值 还是?