探讨JavaScript:优雅的封装还是执行效率?

开发 前端
优雅的封装还是执行效率?这是一个悖论。像C语言和C++之间的差别一样,JavaScript作为一种解释性语言,其效率本身就比编译语言低很多,所以这个问题显得尤为重要。

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

执行的效率就是另外一回事。就像是C语言和面向对象的C++之间的差别:C++很优雅,但是执行效率,无论是编译后的二进制代码还是运行期的内存的占用,都要比简单的C语言多出一截来。

这个问题在脚本语言中显得更加重要,因为JavaScript根本就是一种解释语言,解释语言的执行效率要比编译语言低很多。

1. 优雅的封装

我们先来看看变量封装。这里的变量不仅仅是属性,也包括函数。前面已经说过,JavaScript中并没有类这个概念,是我们利用变量作用域和闭包“巧妙的模拟”出来的,这是一种优雅的实现。还是温故一下以前的代码:

  1. function Person() {     
  2.     var id;     
  3.     var showId = function() {     
  4.         alert("My id is " + id);     
  5.     }     
  6.     this.getId = function() {     
  7.         return id;     
  8.     }     
  9.     this.setId = function(newId) {     
  10.         id = newId;     
  11.     }     
  12. }     
  13. var p = new Person();     
  14. p.setId(1000);     
  15. alert(p.id); // undefined     
  16. // p.showId(); error: function not defined     
  17. var p2 = new Person();     
  18. alert(p.getId == p2.getId); // false    
  19.  
  20. function Person() {  
  21.     var id;  
  22.     var showId = function() {  
  23.         alert("My id is " + id);  
  24.     }  
  25.     this.getId = function() {  
  26.         return id;  
  27.     }  
  28.     this.setId = function(newId) {  
  29.         id = newId;  
  30.     }  
  31. }  
  32. var p = new Person();  
  33. p.setId(1000);  
  34. alert(p.id); // undefined  
  35. // p.showId(); error: function not defined  
  36. var p2 = new Person();  
  37. alert(p.getId == p2.getId); // false  

我们很优雅的实现了私有变量——尽管是投机取巧的实现的。但是,这段代码又有什么问题呢?为什么两个对象的函数是不同的呢?

想一下,我们使用变量的作用域模拟出私有变量,用闭包模拟出公有变量,那么,也就是说,实际上每个创建的对象都会有一个相同的代码的拷贝!不仅仅是那个id,就连那些showId、getId 等函数也会创建多次。注意,考虑到JavaScript函数就是对象,就不会感到那么奇怪了。

但是毫无疑问,这是一种浪费:每个变量所不同的只是自己的数据域,函数代码都是相同的,因为我们进行的是同一种操作。其他语言一般不会遇到这种问题,因为那些语言的函数和对象的概念是不同的,像Java,每个对象的方法其实指向了同一份代码的拷贝,而不是每个对象都会有自己的代码拷贝。

#p#

2. 去看效率

那种封装虽然优雅,但是很浪费。好在JavaScript是一种灵活的语言,于是,我们马上想到,把这些函数的指针指向另外的一个函数不就可以了吗?

  1. function show() {     
  2.     alert("I'm a person.");     
  3. }     
  4. function Person() {     
  5.     this.show = show;     
  6. }     
  7. var p1 = new Person();     
  8. var p2 = new Person();     
  9. alert(p1.show == p2.show); // true    
  10.  
  11. function show() {  
  12.     alert("I'm a person.");  
  13. }  
  14. function Person() {  
  15.     this.show = show;  
  16. }  
  17. var p1 = new Person();  
  18. var p2 = new Person();  
  19. alert(p1.show == p2.show); // true  

这个办法不错,解决了我们以前的那个问题:不同的对象共享了一份代码。但是这种实现虽然有了效率,可是却太不优雅了——如果有很多类,那么岂不是有很多全局函数?

好在JavaScript中还有一个机制:prototype。还记得这个prototype吗?每个对象都维护着一个prototype属性,这些对象的prototype属性是共享的。那么,我们就可以把函数的定义放到prototype里面,于是,不同的对象不就共享了一份代码拷贝吗?事实确实如此:

  1. function Person() {     
  2. }     
  3. Person.prototype.show = function() {     
  4.     alert("I'm a person.");     
  5. }     
  6. var p1 = new Person();     
  7. var p2 = new Person();     
  8. alert(p1.show == p2.show); // true    
  9.  
  10. function Person() {  
  11. }  
  12. Person.prototype.show = function() {  
  13.     alert("I'm a person.");  
  14. }  
  15. var p1 = new Person();  
  16. var p2 = new Person();  
  17. alert(p1.show == p2.show); // true  

不过,这种分开定义看上去很别扭,那么好,为什么不把函数定义也写到类定义里面呢?

  1. function Person() {        
  2.     Person.prototype.show = function() {        
  3.         alert("I'm a person.");        
  4.     }        
  5. }        
  6. var p1 = new Person();        
  7. var p2 = new Person();        
  8. alert(p1.show == p2.show); // true       
  9.  
  10. function Person() {     
  11.     Person.prototype.show = function() {     
  12.         alert("I'm a person.");     
  13.     }     
  14. }     
  15. var p1 = new Person();     
  16. var p2 = new Person();     
  17. alert(p1.show == p2.show); // true   

实际上这种写法和上面一种没有什么不同:唯一的区别就是代码位置不同。这只是一个“看上去很甜”的语法糖,并没有实质性差别。

最初,微软的.Net AJAX框架使用前面的机制模拟了私有变量和函数,这种写法和C#很相像,十分的优雅。但是,处于效率的缘故,微软后来把它改成了这种原型的定义方式。虽然这种方式不那么优雅,但是很有效率。

在JavaScript中,这种封装的优雅和执行的效率之间的矛盾一直存在。现在我们***的解决方案就是把数据定义在类里面,函数定义在类的prototype属性里面。

原文链接:http://devbean.javaeye.com/blog/412296

【编辑推荐】

  1. 浅析Javascript闭包的特性
  2. 提高Web网站性能:JavaScript优化细节
  3. 揭开Javascript闭包的真实面目
  4. JavaScript使用心得汇总:从BOM和DOM谈起 
责任编辑:王晓东 来源: JavaEye
相关推荐

2013-04-02 10:10:06

JavaScriptJS

2018-08-03 15:47:00

iOS框架开发

2009-11-27 15:24:48

PHP递归效率

2012-07-16 01:20:09

代码效率

2009-12-01 15:48:12

提高PHP运行效率

2021-05-07 06:27:29

JavaScript运算符开发

2019-09-20 15:47:24

代码JavaScript副作用

2024-04-01 13:05:13

C++接口类开发

2021-11-23 10:45:57

StopWatch代码Java

2011-03-21 15:51:27

SQL执行效率

2023-02-23 19:31:05

Golang函数重载

2021-10-25 10:30:12

JavaScript开发 代码

2022-03-02 15:31:32

架构网络请求代码

2024-03-28 14:29:46

JavaScript编程

2021-07-12 15:35:56

JavaScript代码运算符

2013-12-17 13:17:25

大数据

2022-06-07 08:59:58

hookuseRequestReact 项目

2024-07-04 08:02:59

2010-04-29 12:46:42

Oracle SQL

2022-09-05 14:17:48

Javascript技巧
点赞
收藏

51CTO技术栈公众号