有哪些值得一读的不使用 this 和原型,而使用闭包进行面向对象封装的 JS 项目
Douglas Crockford 提倡过使用闭包而不是传统的
this
和
prototype
来实现面向对象封装(示例代码参见
http://mikehadlow.blogspot.hk/2010/12/javascript-defining-classes-with.html
)。
但是业界大多数代码还是使用
this
和
prototype
来实现面向对象封装。那么有哪些使用闭包进行面向对象封装的优秀 JavaScript 代码?
由这个问题启发: 有哪些必须一读的优秀开源 JS 代码
UPDATE
:来这个答案下面黒 DC 而不回答问题的我也是醉了,一律 vote down。我不知道你们是什么思路会以为我是因为 DC 提倡过这么写而提出这个问题。
我从来都是资深 DC 黒,反对他的大多数观点。
我只是恰巧由于
我自己的函数式编程背景
,认为使用闭包而不是
this
更好而已。
closure JavaScript prototype 闭包
Answers
DC是我唯一忍不住要黑的外国程序员,虽然蝴蝶书是我非常喜欢的一本书(爱之深恨之切?)
他的许可里面硬是加一句
The Software shall be used for Good, not Evil.
,一个人怒战整个社区,在
GNU
、
Debian
、Fedora、Red Hat等等所有社区全都留下Bad license的宣告。 简单地考古一下可以找到他的各种顽固言论,JSMin JSLint JSON等等各种伟大的项目都被这么个License糟蹋了
介绍一下DC的傲娇本质,好像歪楼了
我觉得最大的问题在于,JS已经是工程性很弱,很难被理解的一门语言了。这种完全构造,完全砍掉原型机制,把JS中最后一点点和“类”性质类似的部分也去掉的做法,只能让JS,让你的代码更加远离多数程序员的理解范围,让你的代码难以维护。如果你的项目只有一个人开发,只有一个人维护,自然没问题。但想在开源或者商业的实际项目中用,必然事倍功半。这种事倍功半的事,除非有着压倒性的其他优势(嗯,比如性能和硬件亲和性致胜的汇编,比如元编程能力致胜的FP),否则没有出路。
Linus可以插着腰说“I'm always right”,DC道行还差了点,你我就差距更大了,还是先按着多数人能接受的路子来比较踏实一点
我觉得你误解了 DC 的本意,或者说是你没有完全领会你读到的那个例子,所以才误解了 DC 的本意。
可能不是误解,是我考虑不周,见答案之后的讨论。
最近一年,DC 在各 JS 会议中发表了关于他的 JavaScript: Good Parts 的续篇演讲: JavaScript: Better Parts ,最早好像是在 YUI Conf 上吧,也是最完整最详细的一次,视频可以上 Youtube 找,很多版本都有。
在这次演讲里,他提到了自己:
not to use
this
anymore
但是他从头到尾都没有说过:
not to use prototype anymore
这就是我说你误解了 DC 的部分。而他本人也解释了不再用
this
的原因,其一是希望代码更安全(因为
this
的动态绑定特性),其二则是追求代码更“函数式”化。
另外他也谈到了很多关于 ES6 的部分,特别是他认为让 JavaScript Better 的部分,比如更多的函数式编程特性如尾调用等;还谈到一些让大家有些“担心”,觉得 JavaScript 不怎么好了的部分,比如
Class
的引入等。我个人很赞同他的观点,
Class
的引入是标准委员会希望在语言层面能对那些从传统 OO 语言过渡到 JavaScript 的程序员更加友好,但是
Class
的引入并没有改变 JavaScript 基于原型链的面向对象的本质,
Class
只是一层语法糖,它实现的还是传统的构造器+原型的对象模型,只是在语法层面上更简单友好,且让一些经典的 OO 元素变得更便于实现和使用,比如私有成员,继承等等。因此不必对此“耿耿于怀”,尽管它算不上让 JavaScript 变得更好,却也不会让 JavaScript 变得更坏——只要你知道如何去用就好,当然你可以选择不用。
回到你提供的范例代码,比如像这样来创建一个对象:
var new_animal = function(name) {
var animal = {};
animal.sayHello = function() {
return "Hello, my name is " + name;
};
return animal;
};
var new_dog = function(name) {
var dog = new_animal(name);
dog.bark = function() {
return "woof";
};
return dog;
};
这的确是 DC 所说的不用
this
的构造器写法,但是坦白地说,就算用了
this
,和
animal
以及
dog
这两个人工创造的对象又有多少不同?
是的,我们不需要时刻谨记加上
new
了。
是的,我们也能这样来封装隐藏私有成员了:
var animalApp = function() {
var new_animal = function(name) {
//...
};
var new_dog = function(name) {
//...
};
return {
main: function() {
var dog = newdog('doggy');
console.log(dog.sayHello());
console.log(dog.bark());
}
};
};
但是这就够了吗?如果我们要创建很多很多的
doggy
怎么办?每次都要重新定义一个
bark
方法?这显然是不合理的。这个例子并没有延伸到原型扩展的范畴,它纯粹是为了展示不用
this
创建对象的方法,但是却被误读为:“完全使用闭包封装,不使用
this
和
prototype
“了。
另外,就算不使用
this
,DC 也没有把它列入到“推荐的最佳实践”这一范畴里去,这样做的确有好处,但是对于能够理解和正确使用
this
的程序员来说,并非“只能这样做”的限制,而是“有时候这样做会更好”的选择。DC 在演讲中并没有单独那它作为一条“最佳实践”来讲,只是在“不使用 Object.create()“这一条里解释原因的时候提到了关于不再使用
this
的。不问出处会害人呐!
至于你问有没有项目里这样使用,稍微上点规模的项目从头到尾这样用的我是没见过(没必要),不过用到上面举例的方法的代码倒是有见过几次。究其原因可能就是我上面说的那些,欢迎补充或讨论。