有哪些值得一读的不使用 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 闭包

train 10 years, 2 months ago

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道行还差了点,你我就差距更大了,还是先按着多数人能接受的路子来比较踏实一点

快要坏掉的番茄 answered 10 years, 2 months ago

我觉得你误解了 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 的。不问出处会害人呐!

至于你问有没有项目里这样使用,稍微上点规模的项目从头到尾这样用的我是没见过(没必要),不过用到上面举例的方法的代码倒是有见过几次。究其原因可能就是我上面说的那些,欢迎补充或讨论。

真·大头怪婴 answered 10 years, 2 months ago

Your Answer