JavaScript的“新”关键字被视为有害吗?

时间:2008-12-20 15:20:08

标签: javascript

在另一个question中,用户指出new关键字使用起来很危险,并提出了一个不使用new的对象创建解决方案。我不相信这是真的,主要是因为我使用了Prototype,Scriptaculous和其他优秀的JavaScript库,并且每个人都使用new关键字。

尽管如此,昨天我正在观看道格拉斯·克罗克福德在YUI剧院的演讲,他说的完全一样,他的代码中不再使用new关键字(Crockford on JavaScript - Act III: Function the Ultimate - 50:23 minutes

使用new关键字是否“不好”?使用它的优点和缺点是什么?

13 个答案:

答案 0 :(得分:592)

Crockford为推广优秀的JavaScript技术做了很多工作。他对语言关键要素的看法立场引发了许多有益的讨论。也就是说,有太多的人把每一个“坏”或“有害”的宣言当作福音,拒绝超越一个人的意见。有时可能会有点令人沮丧。

使用new关键字提供的功能比从头构建每个对象有几个好处:

  1. Prototype inheritance。虽然习惯于基于类的OO语言的人经常看到混合的怀疑和嘲笑,但JavaScript的本机继承技术是一种简单且令人惊讶的有效的代码重用方法。新关键字是规范(并且只有可用的跨平台)使用它的方式。
  2. 性能。这是#1的副作用:如果我想为我创建的每个对象添加10个方法,我可以只写一个创建函数,手动将每个方法分配给每个新对象......或者,我可以将它们分配给创建函数prototype并使用new来删除新对象。这不仅更快(原型上的每个方法都不需要代码),它避免了为每个方法使用单独的属性来膨胀每个对象。在较慢的机器上(或者尤其是较慢的JS解释器),当创建许多对象时,这意味着可以节省大量的时间和内存。
  3. 是的,new有一个关键的缺点,其他答案巧妙地描述了:如果你忘了使用它,你的代码会在没有警告的情况下破解。幸运的是,这个缺点很容易减轻 - 只需向函数本身添加一些代码:

    function foo()
    {
       // if user accidentally omits the new keyword, this will 
       // silently correct the problem...
       if ( !(this instanceof foo) )
          return new foo();
    
       // constructor logic follows...
    }
    

    现在您可以拥有new的优势,而不必担心意外误用造成的问题。你甚至可以在检查中添加一个断言,如果破坏代码的思想默默地工作困扰你。或者,如some所述,使用检查引入运行时异常:

    if ( !(this instanceof arguments.callee) ) 
       throw new Error("Constructor called as a function");
    

    (请注意,此代码段能够避免对构造函数名称进行硬编码,因为与前面的示例不同,它不需要实际实例化对象 - 因此,可以将其复制到每个目标函数中而无需修改。)< / p>

    John Resig在他的Simple "Class" Instantiation帖子中详细介绍了这项技术,并在默认情况下包含了将此行为构建到“类”中的方法。绝对值得一读...就像他即将出版的书Secrets of the JavaScript Ninja一样,它在JavaScript语言的这个和许多其他“有害”特征中找到了隐藏的金色({strong>章节 {{1}对于我们这些最初将这种备受诟病的特征视为噱头的人来说,尤其具有启发性。)

答案 1 :(得分:178)

我刚刚阅读了他的Crockfords书中的一些部分“Javascript:The Good Parts”。我觉得他认为一切都让他感到有害:

关于开关坠落:

  

我从不允许开关盒落下   直到下一个案例。我曾经发现过   我的代码中的一个错误   意外跌倒了   在发表了激烈的演讲之后   关于为什么有时会跌倒的原因   有用。 (第97页,ISBN   978-0-596-51774-8)

关于++和 -

  

++(增量)和 - (减量)   运营商已为人所知   通过鼓励来弥补不良代码   过分狡猾。他们是第二名   只有错误的建筑   启用病毒和其他安全性   恫吓。 (第122页)

关于新的:

  

如果您忘记加入 new   调用构造函数时的前缀   功能,那么这个将不会   绑定到新对象。可悲的是,这个   将被绑定到全局对象,所以   而不是增加你的新对象,   你会破坏全球化   变量。那真是太糟糕了。那里   没有编译警告,也没有   运行时警告。 (第49页)

还有更多,但我希望你能得到这张照片。

我对你的问题的回答:不,这不是有害的。但是如果你忘了使用它,你可能会遇到一些问题。如果你在良好的环境中发展,你会发现它。

<强>更新

写完这个答案大约一年后,第五版ECMAScript发布了,支持strict mode。在严格模式下,this不再绑定到全局对象,而是绑定到undefined

答案 2 :(得分:91)

Javascript是动态语言,有很多方法可以搞砸另一种语言会阻止你。

避免使用new这样的基本语言功能,因为你可能会陷入困境,有点像在穿过雷区之前移除闪亮的新鞋,以防万一你的鞋子变得泥泞。

我使用一种约定,其中函数名称以小写字母开头,实际类别定义的“函数”以大写字母开头。结果是一个非常引人注目的视觉线索,即“语法”错误: -

var o = MyClass();  // this is clearly wrong.

除此之外,良好的命名习惯也有帮助。在所有函数执行之后,因此名称中应该有一个动词,而类代表对象,是没有动词的名词和形容词。

var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.

有趣的是SO的语法着色如何解释上面的代码。

答案 3 :(得分:42)

我是Javascript的新手,所以也许我只是没有太多的经验来提供一个很好的观点。但我想分享我对这个“新”事物的看法。

我来自C#世界,使用关键字“new”是如此自然,以至于工厂设计模式对我来说很奇怪。

当我第一次使用Javascript编写代码时,我没有意识到存在像“YUI模式”中的“new”关键字和代码,并且我不会花费很长时间来遇到灾难。在回顾我编写的代码时,我忘记了特定行应该做的事情。更混乱的是当我“干运行”代码时,我的思维无法真正在对象实例边界之间转换。

然后,我找到了“new”关键字给我,它“分开”了。使用new关键字,它会创建一些东西。没有new关键字,我知道我不会把它与创建事物混淆,除非我调用的函数给了我强有力的线索。

例如,对于var bar=foo();我没有任何线索,因为它可能是什么吧......它是一个返回值还是一个新创建的对象?但是var bar = new foo();我确定bar是一个对象。

答案 4 :(得分:39)

另一个案例 new是我称之为Pooh Coding的案例。小熊维尼跟着他的肚子。我说使用您正在使用的语言,而不是反对

有可能语言的维护者会优化他们试图鼓励的习语的语言。如果他们在语言中添加了新的关键字,他们可能认为在创建新实例时要明确是有意义的。

按照语言的意图编写的代码将在每次发布时提高效率。避免语言关键结构的代码会随着时间的推移而受到影响。

编辑:这远远超出了性能。我无法计算我听过(或说过)“为什么他们做了那个?”的时间。找到奇怪的代码。事实证明,在编写代码时,有一些“好”的理由。遵循语言道是你最好的保险,因为从现在开始几年没有你的代码被嘲笑。

答案 5 :(得分:24)

我写了一篇关于如何在没有new关键字的情况下缓解调用构造函数问题的帖子 它主要是教学,但它显示了如何创建使用或不使用new的构造函数,并且不要求您在每个构造函数中添加boilerplate code来测试this

http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html

这是该技术的要点:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly 
 */
function ctor(realCtor){
  // This is going to be the actual constructor
  return function wrapperCtor(){
    var obj; // object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

以下是如何使用它:

// Create our point constructor
Point = ctor(function(x,y){
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);

答案 6 :(得分:22)

不使用new关键字背后的理由很简单:

完全没有使用它,你可以避免意外遗漏它带来的陷阱。 YUI使用的构造模式是一个如何完全避免使用新关键字的示例“

var foo = function () {
    var pub= { };
    return pub;
}
var bar = foo();

或者你可以这样:

function foo() { }
var bar = new foo();

但是这样做可能会导致某人忘记使用关键字,并且运算符都是fubar。 AFAIK这样做没有任何好处(除了你已经习惯了)。

在今天结束时:这是关于防守。你能使用新的声明吗?是。它会使您的代码更危险吗?是。

如果您曾经编写过C ++,那么删除它们之后就像将指针设置为NULL一样。

答案 7 :(得分:20)

我认为“新”会增加代码的清晰度。清晰度值得一切。很高兴知道存在陷阱,但是通过避免清晰来避免这些陷阱似乎不适合我。

答案 8 :(得分:15)

案例1:new不是必需的,应该避免

var str = new String('asd');  // type: object
var str = String('asd');      // type: string

var num = new Number(12);     // type: object
var num = Number(12);         // type: number

案例2:new是必需的,否则您将收到错误

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error

答案 9 :(得分:12)

以下是我对使用new运算符支持和反对的两个最强有力论据的最简要总结:

针对new

的论点
  1. 设计的功能 使用。实例化为对象 new运营商可能会造成灾难性后果 如果他们不正确的影响 作为普通函数调用。一个 在这种情况下函数的代码会 在范围内执行 函数被调用,而不是在 本地对象的范围为 意。这可能导致全球化 要获得的变量和属性 用灾难性的东西覆盖 后果。
  2. 最后,写function Func(), 然后拨打Func.prototype 并添加东西,以便你 可以调用new Func()来构建 你的对象看起来有些难看 程序员,谁宁愿使用 另一种对象继承方式 用于建筑和风格 的原因。
  3. 有关此论点的更多信息,请查看Douglas Crockford的精彩简洁书籍Javascript:The Good Parts。事实上无论如何要检查它。

    赞成new

    的论据
    1. 使用new运算符 原型分配很快。
    2. 意外的那些东西 运行构造函数 全局命名空间中的代码可以 如果你总是容易被阻止 包括你的一些代码 要检查的构造函数 看看他们是否被召唤 正确的,并且,在哪些情况下 他们不是,处理电话 根据需要适当。
    3. 有关此技术的简单解释,请参阅John Resig's post,并对他提倡的继承模型进行更深入的解释。

答案 10 :(得分:9)

我同意pez和一些人的意见。

我觉得“新”是自描述对象创建,GUI Dean描述的YUI模式是完全模糊的

有人可以写var bar = foo;var bar = baz();的可能性,其中baz不是对象创建方法,似乎更危险。

答案 11 :(得分:8)

我认为新事物是邪恶的,不是因为如果你忘记错误地使用它可能会导致问题,但是因为它搞砸了继承链,使语言难以理解。

JavaScript是基于原型的面向对象。因此,每个对象必须从另一个对象创建,如var newObj=Object.create(oldObj)。这里 oldObj 被称为 newObj 的原型(因此是“基于原型的”)。这意味着如果在 newObj 中找不到属性,那么将在 oldObj 中搜索它。默认情况下, newObj 将是一个空对象,但由于其原型链,它似乎具有 oldObj 的所有值。

另一方面,如果你var newObj=new oldObj() newObj 的原型是 oldObj.prototype ,这是不必要的。

诀窍是使用

Object.create=function(proto){
  var F = function(){};
  F.prototype = proto;
  var instance = new F();
  return instance;
};

它在这个函数里面,只有在这里才应该使用new。在此之后,只需使用 Object.create()方法。该方法解决了原型问题。

答案 12 :(得分:0)

IMNSHO“new”是 2021 JavaScript 中的一个有缺陷的概念。它在不需要的地方添加了单词。它使函数/构造函数的返回值隐式并强制在函数/构造函数中使用 this。在代码中添加噪音从来都不是一件好事。

// With new
function Point(x, y) {
    this.x = x
    this.y = y
}
let point = new Point(0,0)

对比

// Without new
function Point(x, y) {
    return { x, y }
}
let point = Point(0,0)