论坛首页 Web前端技术论坛

JavaScript面向对象程序设计(8): 优雅的封装还是执行的效率?

浏览 15735 次
精华帖 (5) :: 良好帖 (1) :: 新手帖 (2) :: 隐藏帖 (11)
作者 正文
   发表时间:2009-06-21   最后修改:2009-06-21

优雅的封装还是执行的效率?这是一个悖论。

 

优雅封装的程序看起来是那么的美妙:每个属性被隐藏在对象之后,你所能看到的就是这个对象让你看到的,至于它到底是怎么操作的,这个不需要你操心。

 

执行的效率就是另外一回事。就像是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属性里面。

   发表时间:2009-06-21  
其实很丑,此外代码量变多(下载变慢),而且效率下降(更影响用户体验)……
0 请登录后投票
   发表时间:2009-06-21  
night_stalker 写道
其实很丑,此外代码量变多(下载变慢),而且效率下降(更影响用户体验)……


我想这里指的优雅是能够比较好的模拟出面向对象理论里面的相关概念,而不是综合起来考虑。所以我称为“优雅的封装”,并不是“优雅的代码”。
0 请登录后投票
   发表时间:2009-06-22  
首先得习惯javascript的写法,js有他自己的特点,写多了,看着就习惯了。
0 请登录后投票
   发表时间:2009-06-23  
var Person = {  
    id:'',  
    showId : function() {  
        alert("My id is " + id);  
    },
    getId : function() {  
        return id;  
    },
    setId : function(newId) {  
        id = newId;  
    }  
}  
Person.setId(1000);  
Person.showId();
alert(Person.id);

这样的封装和function this 有什么区别呢?
0 请登录后投票
   发表时间:2009-06-24  
greengnn 写道
var Person = {  
    id:'',  
    showId : function() {  
        alert("My id is " + id);  
    },
    getId : function() {  
        return id;  
    },
    setId : function(newId) {  
        id = newId;  
    }  
}  
Person.setId(1000);  
Person.showId();
alert(Person.id);

这样的封装和function this 有什么区别呢?


那个,我没看明白这里的function this是什么意思。但是,您的代码是使用的JSON进行定义的,而不是类似于其他语言的那种定义方法。JSON大约类似于一种散列,Python中的字典定义好像就是这种样子。在这里,: 前面的是键,后面的是值,印证了JavaScript中对象就是字典的说法。
0 请登录后投票
   发表时间:2009-06-25  
楼主也读了李战老师的<悟透javascript>吧.
0 请登录后投票
   发表时间:2009-06-25  
ls说得对
0 请登录后投票
   发表时间:2009-06-25  
本来就是不面向对象的东西
0 请登录后投票
   发表时间:2009-06-25  
建议读以下WROX的Professional JavaScript
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics