每种编程语言里都有一些小伎俩,有些确实能带来方便,有些则只是纯蛋疼。
这里收集一些常见的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
+
运算符
该问题来源于: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类型。