你真的会写JavaScript吗? by 小马

今天看到MSDN 上的新文章:Create Advanced Web Applications With Object-Oriented Techniques

很久没有看到这样让人唇齿留香的好文了。上次看到的是一篇是 Douglas Crockford 的JavaScript, We Hardly new Ya (我简单翻译了一下,译文在后)。

同其他教你如何用面向对象的思想编写JavaScript的其他文章一样,该文也是着重在这么几个要素:

  • JavaScript的对象就是一个关联数组。
  • JavaScript 函数也是一个对象。
  • 原型(Prototype)
  • 闭包(Closures)
  • 继承/私有属性/静态方法
  • 命名空间

作者文笔很好,英文很容易看懂,没有生僻的用词(顺便提一下,《PPK on JavaScript》作者的英文不敢恭维)。用来举例的代码也很贴切。

特别是文章的开头很有意思,作者写到他和一个据说已经写了快4年JavaScript的女程序员聊天,女程序员认为她的JS水平very good,后来作者发现她确实会写,但仅仅是会写,其实对JavaScript的内涵所知甚少。

作者想用这个例子说明,有很多具备Java/C++/C#开发经验的开发人员在编写JavaScript或者转行到FED(比如我)的时候,想当然的把那些标准面向对象语言的思想套用在JavaScript上,反而走入迷途。

对此我深有体会,我正是在真正参与了一次Ajax的项目并真正读懂了Prototype框架的源码之后,对JavaScript有了完全全新的认识。

总之,推荐阅读。附上JavaScript, We Hardly new Ya的译文,译得匆忙,定有行文不通之处,请客官见谅!


JavaScript 的 new, 好久不见啊

原文: JavaScript, We Hardly new Ya --Douglas Crockford。

JavaScript是一门基于原型的语言,但它却拥有一个 new 操作符使得其看起来象一门经典的面对对象语言。那样也迷惑了程序员们,导致一些有问题的编程模式。

其实你永远不需要在JavaScript使用 new Object()。用字面量的形式{}去取代吧。

同理,不要使用 new Array() ,而代之以字面量[]。JavaScript中的数组并不象Java中的数组那样工作的,使用类似Java的语法只会让你糊涂。

同理不用使用 new Number, new String, 或者 new Boolean。这些的用法只会产生无用的类型封装对象。就直接使用简单的字面量吧。

不要使用 new Function 去创建函数对象。用函数表达式更好。比如:

frames[0].onfocus = new Function(”document.bgColor=’antiquewhite’”)

更好的写法是:

frames[0].onfocus = function () {document.bgColor = ‘antiquewhite’;};

第二种形式让脚本编译器更快的看到函数主体,于是其中的语法错误也会更快被检测出来。有时候程序员使用 new Function 是因为他们没有理解内部函数是如何工作的。

selObj.onchange = new Function(”dynamicOptionListObjects["+
dol.index+"].change(this)”);

如果我们让用字符串做函数体,编译器不能看到它们。如果我们用字符串表达式做函数体,我们同样也看不到它们。更好的方式就是不要盲目编程。通过制造一个返回值为函数的函数调用,我们可以明确的按值传递我们想要绑定的值。这允许我们在循环中初始化一系列 selObj 对象。

selObj.onchange = function (i) {
return function () {
dynamicOptionListObjects[i].change(this);

};
}(dol.index);

直接对一个函数使用new永远不是一个好主意。比如, new function 对构造新对象没有提供什么优势。

myObj = new function () {
this.type = ‘core’;
};

更好的方式是使用对象字面量,它更轻巧,更快捷。

myObj = {
type: ‘core’
};

假如我们需要创建的对象包含的方法需要访问私有变量或者函数,更好的方式仍然是避免使用new.var foo = new function() {
function processMessages(message) {
alert(”Message: ” + message.content);
}
this.init = function() {
subscribe(”/mytopic”, this, processMessages);
}
}
通过使用 new 去调用函数,对象会持有一个无意义的原型对象。这只会浪费内存而不会带来任何好处。如果我们不使用new,我们就不用在对象链维护一个无用的prototype对象。所以我们可以用()来正确的调用工厂函数。var foo = function () {
function processMessages(message) {
alert(”Message: ” + message.content);
}
return {
init: function () {
subscribe(”/mytopic”, this, processMessages);
}
};
}();
所以原则很简单: 唯一应该要用到new操作符的地方就是调用一个古老的构造器函数的时候。当调用一个构造器函数的时候,是强制要求使用new的。有时候可以来new一下, 有的时候还是不要了吧。

14 Responses to “你真的会写JavaScript吗?”

  1. coocboy Says:

    so fast

  2. lenel Says:

    对于javaScript的Prototype我理解是一颗描述继承树的链子
    使用javaScript进行OO开发是完全可以的
    和java,C 等对比,有一点做不到:
    对于父类的成员方法(非构造方法)要么完全重写,要么原封不动,也就是无法在子类的成员方法中调用父类的同名方法
    其他的重写、重载、构造、接口、继承、多态都是没问题的
    当然脚本语言没有编译器就无法进行比如做接口实现是否完整等检查
    靠程序员自己控制好吧

  3. hax Says:

    关于new,new func对于literal的优点是,literal只能直接赋值,而func内部可以执行任何语句。另外有时候你确实需要prototype,而不是每次从literal手动初始化。
    再有,(new function() {…})()是一种惯用法,虽然产生了一个无用对象,但是马上被垃圾回收了,所以无所谓。当然,有些人喜欢用 void function(){…}()。

  4. hax Says:

    子类调父类是可以的。例如B是A的子类,在B的x方法(或任何地方)中,可以用 A.prototype.x.call(this)来调用A的x方法。当然,你不是通过super关键字调用,总有很多不便。不过可以写出一些精巧的方法来处理,例如多数js引擎有function.caller属性,因此可以构造一个base方法,然后在base方法中判断调用者,并找到调用者的父类,并调用父类方法。

  5. Frank Says:

    DC的都是好文章
    JS本质上是各种语言妥协后的大杂烩 什么都有点 OOP 、FP、过程式

  6. Frank Says:

    借地方发发我老友的翻译,大家各取所需~赫赫
    http://jstang.5d6d.com/thread-54-1-1.html
    [翻译]废掉new

    昨晚被JavaScript的new操作符所困扰,幸得frank介招这篇文章,据说是清风提供的,由衷感谢这二位,不敢独享。
    原文地址:http://yuiblog.com/blog/2006/11/13/javascript-we-hardly-new-ya/
    作者:Douglas Crockford
    由于各人水平所限,如果翻译有错,请大家及时报错,我好及时改正,以免荼毒众生。。。

    本文适合想读Ext源码的js新手,或是希望提高js素养的新手们。

    JavaScript本是一种基于原形的(prototypal)语言,但它的“new”操作符看起来有点像经典语言。这迷惑了广大程序员们,并导致了很多使用上的问题。

    在JavaScript中,不要用到new Object()这种操作,该用{ }来代替。同理,也不要用new Array(),相应的,用[]来代替。JavaScript的Array绝不同于Java的Array,模仿Java的语法只会使人越来越糊涂。

    也不要用new Number,new String,或是new Boolean。在JavaScript使用包装对象完全没有必要,只需使用普通的声明就可以了。

    不要用new Function来新建一个function,而应当用最普通的function表达式。例如:

    frames[0].onfocus = new Function(”document.bgColor=’antiquewhite’”)

    更好的写法是:

    frames[0].onfocus = function () {document.bgColor = ‘antiquewhite’;};

    同样定义一个事件,第二句不会立即检测函数体,哪怕是函数体有错,也不会立即报错。只有不了解function内部机制的人才会用new Function这种声明方式。

    selObj.onchange = new Function(”dynamicOptionListObjects["

    dol.index "].change(this)”);

    如果我们直接写出函数体,编译器是无法看到其内部的(也就无法立即执行、无法立即引发错误)。但如果我们用表达式方式声明函数(如上),看不到函数内部的则是我们。编程的话,还是尽量对一且都尽量清晰可见吧。以声明函数的方式来返回一个函数,我们可以更加明确地传值,如下代码,它还可以设定一个初始值。

    selObj.onchange = function (i) {

    return function () {

    dynamicOptionListObjects.change(this);

    };
    }(dol.index);

    还有一个不好的习惯是将new放到function的前面,比如,new function对于构造新的对象来说,毫无意义。

    myObj = new function () {

    this.type = ‘core’;
    };
    最好使用一个对象,更轻盈,更快捷,如下:

    myObj = {

    type: ‘core’
    };

    如你希望新建一个既有私有变量,也有私有方法的函数对象,也不要使用new操作符。

    var foo = new function() {

    function processMessages(message) {

    alert(”Message: ” message.content);

    }

    this.init = function() {

    subscribe(”/mytopic”, this, processMessages);

    }
    }

    如果像上面一样使用new来执行此方法,这个对象会挂在一个没有意义的prototype上,它只会浪费内存。如果我们不用new,就不会浪费prototype对象于prototype链,所以,更佳的方案是使用()。如下所示:

    var foo = function () {

    function processMessages(message) {

    alert(”Message: ” message.content);

    }

    return {

    init: function () {

    subscribe(”/mytopic”, this, processMessages);

    }

    };
    }();

    译后总结:由于以前对yui并不了解直接就用了ext,对js的不少地方也一知半解,直到看了这篇frank所给的文章才对new操作符略知一二。
    给我的感觉就是:尽量不要显示地用new操作符,因为new这个东西在编译期就已经对对象进行初始化了,不用new的话运行期才进行初始化检查。(实际上我的用词不准确,在js中都是编译期,或都是运行期)。这样可以节省内存,且有利于调试,亦便于理解。
    Ext的源码中用到了很多上面所提及的写法,如果以前不很明白,看完篇文章之后会有种豁然开朗的感觉。

    对“prototype链”不太明白的可以参考这篇 http://www.javaeye.com/topic/53537

  7. vsky Says:

    的确是好文,了解语言内涵对深层开发有很大帮助

  8. JerryTongCorp Says:

    幸亏偶英文好= =
    不错,可是听他这么一说,JS好像变难鸟

  9. xu Says:

    –!UTF-8提交的。。不知道这种思想是否正确,面向对象使程序变的容易理解,也降低的程序的难度,我们往往会把很复杂的封装起来。new的出现使我们从其他语言更快的融入javascript;或许我只是会用javascript,但按我的理解已渐渐远离了面向对象的初衷,难道new就不该出现的javascript,那为什么会有new的存在?在硬件已远远超出软件所需,那点消耗还重要吗?

  10. Anteniedo Says:

    Hello
    thank

  11. 路人 Says:

    請問 ppk 的英文是太差嗎?

  12. fire in hole! Says:

    秀一下本人的JS功力:

    function take(obj) {
    if (obj.src == ‘1.gif’) {
    obj.src = “2.gif”;
    } else {
    obj.src = “1.gif”
    }
    }

    各位觉得如何,满足中 :]

  13. roviury Says:

    作者文笔很好,英文很容易看懂,没有生僻的用词(顺便提一下,《PPK on JavaScript》作者的英文不敢恭维)
    ->《PPK on JavaScript》作者文笔差,英文不容易看懂,有生僻的用词

    function take(obj) {
    obj.src = ( obj.src == ‘1.gif’ ? ‘2′ : ‘1′ )+’.gif’;
    }

  14. roviury Says:

    to xu
    new是有作用的
    至少 new Array()比Array() 快
    new是指建立新的東西
    new function和function都是指function,但建立新的還是每次用同一個都沒差
    但如果指是物件性質的function(大寫字首,例如CreateLine)
    就需要new,因為是建立新的東西

Leave a Reply