一些JavaScript的编码习惯和奇怪写法

标签 IT工具网 javascript

每种编程语言里都有一些小伎俩,有些确实能带来方便,有些则只是纯蛋疼。

这里收集一些常见的JavaScript伎俩,按需自取。

Array.prototype.slice.call(arguments)

原问题:how does Array.prototype.slice.call() work?

这是我们常用的用来把函数的arguments转换成正常数组的方式。我们可以从中一探两个究竟:

  • Array.prototype.slice()是如何工作的
  • Function.arguments的结构是怎样的
    • 一个array-like object
    • 有length属性
    • 没有push/pop/forEach/indexOf等Array.prototype对象上的方法

下面是我在chrome console测试的:

var data = {'1' : 'a', '2' : 'b', length : 4}
undefined
Array.prototype.slice.call(data)
[undefined × 1, "a", "b", undefined × 1]

可以大概感受一下arguments的结构是怎样的。除了arguments之外,document.links, document.forms也是array-like object。

我们还可以用Array.prototype.slice.call(arguments, i)来截取arguments的一部分。比如常用的Object.extend就可以用它来去掉第一个默认的this参数。

var objectExtend = function(where) {
  Array.prototype.slice.call(arguments, 1).forEach(function(source) {
    var key;
    for (key in source) {
      if (source.hasOwnProperty(key)) {
        where[key] = source[key];
      }
    }
  });
  return where;
};

var combined = objectExtend({}, {a: 1}, {b: 2});
}, )

!function foo(){}()

原问题:What does the exclamation mark do before the function?

对于!function foo(){}(),有以下几点:

  • 运算符()!的优先级高,所以它等价于!(function foo(){}());
  • !(statement);会触发JavaScript引擎去解析statement,然后取其结果的布尔反值
  • 该语句的目的就是执行这个函数,它等价于(function foo(){})();

直接使用function foo(){}()会导致语法错误,因为

function foo() {}

是一个函数声明语句,末尾没有;号,对它的解析到}号为止,你不能在后面接(。想要调用foo,只能使用foo();

因此,这个伎俩的目的只是为了省一个字符:!function foo(){}() VS (function foo(){})();

!!variant

原问题:What is the !! (not not) operator in JavaScript?

JavaScript中没有!!运算符,它实际上是两次!逻辑运算,结果是把任意一个JavaScript对象转化为一个布尔值。

这其中,如果variant本身一个falsy value,那么!!variant是布尔假。否则为真。

console.log(!!1);   // true
Boolean(1) === !!1;  // true
console.log(!!"");   // false
Boolean("") === !!"";    //true

可以把它看作是布尔值转换运算。但如果你是为了在if语句中使用,大可不必这样做。因为JavaScript引擎会对if(statemen){}中的statement自动执行这个布尔值转换操作。比如,以下代码会把bar当作布尔真值来执行。

var bar = {a: 1};
if(bar){
    console.log(true);
}else{
    console.log(fasle);
}

$variant

原问题:Why would a JavaScript variable start with a dollar sign?

这只是一个习惯。使用$作为变量名的前缀,是来源于jQuery。jQuery变量的别名是$,所以在使用jQuery的JavaScript代码中,一般把jQuery对象的变量名都加一个$前缀,表明这是一个jQuery对象,不是一个DOM对象或者普通的Object。

后来在Backbone以及相关的前端框架中也沿用了这一习惯。

反过来看,有这种风格的框架一般都依赖于jQuery。

函数参数的缺省值

原问题:Set a default parameter value for a JavaScript function

在C++/PHP/Python中定义方法的时候都能为参数指定一个默认值,但JavaScript语言没有这个机制。因此我们只能仿造。

一般是这样来做的:

function myFunc(requiredArg, optionalArg){
  optionalArg = (typeof optionalArg === "undefined") ? "defaultValue" : optionalArg;

  //do stuff
}

如果你确定这个参数是一个非falsy value的对象,还可以简化(这是我用得最多的方式):

function myFunc(requiredArg, optionalArg){
  optionalArg = optionalArg || defaultValue;

  //do stuff
}

注意:

  • 跟Python一样,你只能把带默认值的参数放在参数列表的最后。
  • ECMAScript6中的Function已经加入该特性,Firefox中已经支持:Default parameters

+运算符

原问题:What is the explanation for these bizarre JavaScript behaviours mentioned in the ‘Wat’ talk for CodeMash 2012?

该问题来源于:Wat - A lightning talk by Gary Bernhardt from CodeMash 2012

这个问题中有很多蛋疼的例子,在这里说它的目的是提醒大家:严谨地使用+运算符。

以下是使用+的蛋疼示例,运行示例请参考jsfidde

  • [] + []
    • // an empty string
  • [] + {}
    • [object Object]
  • {} + []
    • 0
  • {} + {}
    • NaN
  • Array(16).join(“wat” + 1)
    • wat1wat1wat1wat1wat1wat1wat1wat1wat1wat1wat1wat1wat1wat1wat1
  • Array(16).join(“wat” - 1)
    • NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

实际上以上结果都是遵循ECMAScript5的执行结果。对于+运算符的规定为 The Addition operator ( + )

  • 不限制运算数必须为primitive
  • 首先将两个操作数转化为primitive - ToPrimitive
  • 将一个object转化为primitive就是返回其default value,如果该object有toString()方法,将返回该方法的值
  • 对于Array来说,就是调用其.join()方法
  • 对于object来说,调用toString()将返回"[object Object]"

以上规范能解释大部分使用+的场合,但还有两个示例需要进一步的解释:

  • {} + []
    • {}会被解析为一个block,而不是一个对象(除非你使用({})使它成为一个表达式)
    • 因此{} + []等价于+[]
    • +operand将返回toNumber(ToPrimitive(operand))
    • +[]将返回toNumber(""),也就是0
  • {} + {}
    • 由上可知它将返回toNumber("[object Object]")
    • 根据toNumber()的定义,这无法转成number,因此返回NaN

太复杂。实际使用中,最好保证+的两个操作数均为primitive类型,不能是object类型。

相关文章:

java - 重写(Override)equals和hashCode方法时应考虑的问题

欢迎来到IT工具网

什么是JavaScript严格模式?

java - 如何产生一个随机的字母数字串作为 session 的唯一标识符

Javascript 回调时间

javascript - Angular JSON-RPC : processing error

JavaScript的.prototype是如何工作的

javascript - 将本地函数传递给 setTimeout()

javascript - 我是否必须指定映射才能在 Kuzzle 中使用 document.search Controller ?

javascript - 动态将字符串转换为 bool 值