保护施工人员错过“新”是一种好的做法吗?

时间:2012-03-10 08:56:15

标签: javascript

From Secrets of the Javascript Ninja(很棒的演练顺便说一句):

// We need to make sure that the new operator is always used
function User(first, last){ 
  if ( !(this instanceof User) ) 
    return new User(first, last); 

  this.name = first + " " + last; 
} 

var name = "Resig"; 
var user = User("John", name); 

assert( user, "This was defined correctly, even if it was by mistake." ); 
assert( name == "Resig", "The right name was maintained." );

任何真实的代码样式指南都会让所有构造函数都做这种保护(前两行)吗?我相信linters会接到User("John", "Resig")之类的电话并警告失踪的new,不是吗?

4 个答案:

答案 0 :(得分:7)

我建议您使用严格模式并在支持它的浏览器中进行测试。例如,

function User(first, last) {
    "use strict";
    this.name = first + " " + last;
}
如果忽略new

将进入严格模式。例如,

User("a", "b")

会在Firefox中抛出TypeError。这使您可以捕获错误,而无需支付instanceof支票的罚款。即使您在严格模式之外调用它,也会抛出类型错误。这是因为直接调用的严格模式函数this绑定到undefined而不是全局。在undefined上设置属性会导致TypeError例外。

答案 1 :(得分:2)

JSHint.com上检查您的代码时,它没有告诉我我错过了new关键字:

function User(first, last){ 
  this.name = first + " " + last; 
} 

var name = "Resig"; 
var user = User("John", name); 

<强>结果:

Line 2: this.name = first + " " + last;
Missing "use strict" statement.

Line 5: var name = "Resig";
Redefinition of 'name'.

但是linter不是重点。

至于它是否是一种好习惯,是的,这是一种很好的做法,因为人类不是短暂的,他们可能忘记放new关键字。即使一个linter警告它,我仍然会继续保护我的代码。

简单地说没有错;你可以说额外的条件可以帮助你避免这个问题。

有一个约定,构造函数的 第一个字母 应该是大写;出于同样的原因引入的是这样,当一个JS开发人员说一个函数有首字母大写而不是所有的开发人员都知道它时,他永远不会忘记new关键字(直到最近我开始探索时才知道JS认真地说,如果没有外部世界指定的话,那就是你自己保护代码的地方。

答案 2 :(得分:1)

代码不会发出警告,如果首先没有使用它,则会调用new,因此调用User(x, y)最终会在内部执行new User(x, y)

那就是说,我不喜欢这种透明的修复方式。防御性编码是可以的(例如抛出一个错误),但是我不喜欢代码对我想要做的事情(或者我可能已经做过的事情做了假设,比如当我想调用一个叫做函数时使用大写字母的错误user)。


我们(在我们公司)使用相同的JavaScript“功能”虽然在我们内部使用的组件和模板系统的组合中。

我们的“组件”(具有附加到特定DOM对象的行为的JS对象)以这种样式创建:new MyComponent(domObject) - 这将创建组件并将其附加到DOM元素(它可以监听事件等) )。

我们还使用一个模板化引擎来编译JS函数,这样你就可以像这样的函数调用模板:myTemplate(someData) - 这会将给定的模板呈现为HTML字符串。模板也可以调用partials,这显然只是返回HTML字符串的普通函数。

为了直接生成组件的HTML,组件构造函数将使用“检测”是否通过new调用它来充当构造函数或模板。当它作为模板工作时,它将为其根元素(在我们的例子中为data-component)添加一个特殊属性,以便之后可以在生成的HTML转换为DOM树然后所有DOM元素的情况下完成修复。此属性会自动获取为其生成的组件(此时使用new)。

这只是一个如何利用这种语言结构的例子。

答案 3 :(得分:1)

只是为了好玩:这也可以防止忘记new关键字:

function User(first,last,id){
  User.Instance = User.Instance|| function(firstname,lastname,id){
      this.firstname = firstname || 'nofirstname';
      this.lastname = lastname || 'nolastname';
      this.id = id || 0;
      if (!User.Instance.prototype.nameUpper){
       User.Instance.prototype.nameUpper = function(){
                   return this.name.toUpperCase();
       };
       User.Instance.prototype.resetId = function(){
                   this.id = 0; return this;
       };
       User.Instance.prototype.toString = function(){
           return [this.id,': ',
                   this.firstname[0].toUpperCase(),
                   this.firstname.slice(1),
                   ' ',
                   this.lastname[0].toUpperCase(),
                   this.lastname.slice(1)].join('');
       };
      }
  }
  return new User.Instance(first,last,id);
}
//usage
var pete = User('pete','johanssen',1),
    jean = User('jean','harlowe',2),
    mary = new User('mary','wsnovsky',3);
console.log(pete); //=> 1: Pete Johanssen'
console.log(mary); //=> 3: Mary Wsnovsky'

关于你的问题:我会认为' Good Practice'非常规范/评判。每天吃奶牛是“好习惯”吗?如果我回答这个问题,我会拒绝,但我相信世界上并不是每个人都同意这一点。使用您演示的模式来规避忘记的new关键字本身并不好或坏。这取决于您设定的标准:您是否希望强制执行某种严格性,或者您是否希望防范您自己或同事的草率?这种模式的性能会降低吗?来自chukj的答案的严格性是否适用于所有情况,如果不是,那对你来说是否足够?

我喜欢在javascript中讨厌的一件事就是它的灵活性。据我所知,在Douglas Crockford的讲座中,JSLint旨在强迫你采用统一的编程风格 - 用他自己的话说:

  

使用jslint 伤害你

但另一方面,jslint提供了很多可能性,可以使用'容忍错误的定义''容忍非大写的构造函数等选项来缓解这种力量。换句话说,即使使用jslint,也没有明确定义“良好实践”。

有时,javascript社区的行为就像“javascript教派”。在其中我们找到了非常严格的信徒,对于他们来说,法律信条是法律,并且每一个信号都是一个主要的罪(对于他们来说可能是“ use strict ”是专门发明的;)。其他人则采取更务实的方式。有定期的教徒和永远不会出现的人。 Schism出现了。有些人计划离开这个教堂并找到一个新教堂,例如允许classes被认为是好的和神圣的。

现在可能很清楚:我认为没有“良好做法”这样的事情。或者用更伪的宗教术语来表达它:好的做法是在旁观者的眼中