在浏览 underscore 的过程当中,发现了它的一些小技能,对我们日常平凡的编程很有效。在这里向人人引见一二
void 0 替换 underfined
起首,看源码:
_.isUndefined = function(obj) {
return obj === void 0;
};
这里为啥要用 obj === void 0
, 而不是 obj === undefined
呢?
因为,在 js 中,undefined
并非相似关键字(js 关键字有 function
,return
…),所以,理论上是能够变动的。
事实上,在 IE8 上也的确是能够被变动的,
var undefined = 1;
alert(undefined); // 1 -- IE8, undefined --- chrome
而在 chrome 或高版本的 IE 中,并不能变动全局的 undefined
。然则,部分的 undefined
依然能够被转变。比方:
(function() {
var undefined = 1;
alert(undefined); // 1 -- chrome
})();
所以, undefined
并不异常牢靠,所以才须要 void 0
, void
是 js 的保留字,所以不可被变动。
在 MDN 上定义是:
The void operator evaluates the given expression and then returns undefined.
翻译:void 操作符会对 void 背面的表达式举行运算,然后返回 undefined
所以,运用void
会一向返回 undefined
,所以,能够用void 0
替换undefined
.
复制数组
Array.prototype.slice.call(array);
可用来复制一个数组,或将类数组转换为数组
在 js 中,假如我们想复制一个数组,要怎样复制呢?或许你会如许做:
function copy(array) {
var copyArray = [];
for (var i = 0, len = array.length; i < len; i++) {
copyArray.push(array[i]);
}
return copyArray;
}
实在,我们能够应用数组的 slice
和 concat
要领返回新的数组这个特征,来完成复制数组的功用;
var newArray = Array.prototype.slice.call(array);
var newArray2 = Array.prototype.concat.call(array);
而且,机能方面, slice
以及 concat
比纯真运用 for
轮回还要越发高效
var array = _.range(10000000); //_.range,是undescore一个要领,用于天生一个从0到10000000的数组
console.time('for copy push');
var copyArray1 = [];
for (var i = 0, length = array.length; i < length; i++) {
copyArray1.push(array[i]);
}
console.timeEnd('for copy push');
console.time('slice');
var copyArray2 = Array.prototype.slice.call(array);
console.timeEnd('slice');
console.time('concat');
var copyArray3 = Array.prototype.concat.call(array);
console.timeEnd('concat');
//效果
//for copy push: 379.315ms
//slice: 109.300ms
//concat: 92.852ms
别的,也是经由过程 slice
, call
将类数组转换为数组
function test() {
console.log(Array.prototype.slice.call(arguments));
}
test(1, 2, 3); //输出[1, 2, 3]
运用 Array[length]=value 替换 push 增加元素
现实营业代码,除非对机能请求极高,不然照样引荐 push,毕竟更相符习气
起首看源码 _.values()
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = Array(length); //等同于new Array(length)
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
一开始看这类写法,并不习气,我们大多数人能够更习气如许写(运用 push
):
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = []; //
for (var i = 0; i < length; i++) {
values.push(obj[keys[i]]); //运用push
}
return values;
};
现实测试中,第一种写法会比第二种更快。
关键在于,我们事前晓得要添补的数组 values
的长度,然后预先天生一个对应长度的数组,以后只须要给对应的位置赋值。而第二种在 push 的时刻,除了给对应位置赋值,还须要转变 values
数组的 length。
所以,发起在已知长度的情况下,运用第一种,而不晓得长度的情况下,运用第二种。
恰当的运用 return function
当我们编写两个功用异常邻近的函数时,比方,完成复制一个数组的功用,分别是正序和倒序,我们能够会如许子完成(这里只是为了举例子,复制数组引荐第二点提到的运用slice
或concat
):
function copyArray(array, dir) {
var copyArray = [];
var i = dir > 0 ? 0 : array.length - 1;
for (; i >= 0 && i < array.length; i += dir) {
copyArray.push(array[i]);
}
return copyArray;
}
var copyDesc = function(array) {
return copyArray(array, 1);
};
var copyAsce = function(array) {
return copyArray(array, -1);
};
如许子完成会有什么题目呢?
实在对copyDesc
,copyAsce
,来讲,只要 dir 是差别的罢了,然则,这类体式格局完成,却须要将 array
也作为参数传递给 `copyArray。
而copyDesc
,copyAsce
实在只是一个转发的作用罢了。
我们能够继承优化:
function copyArray(dir) {
return function(array) {
var copyArray = [];
var i = dir > 0 ? 0 : array.length - 1;
for (; i >= 0 && i < array.length; i += dir) {
copyArray.push(array[i]);
}
return copyArray;
};
}
var copyDesc = copyArray(1);
var copyAsce = copyArray(-1);
我以为 return function
这类写法比较文雅一点,你以为呢?
范例推断,运用 Object.prototype.toString()来推断
这里只举两个例子,isString
,isArray
,其他的比方 isArguments
, isFunction
, 因为有些浏览器兼容题目须要特别处置惩罚,这里就不细说了。
而像isNull
,isUndefined
,这些比较简单的,这里也不细说了:)
我们晓得:typeof
能够的返回值有:
范例 | 效果 |
---|---|
Undefined | "undefined" |
Null | "object" |
Boolean | "boolean" |
Number | "number" |
String | "string" |
Symbol(ES6 新增) | "symbol" |
宿主对象(由 JS 环境供应) | Implementation-dependent |
函数对象( [[Call]]) | "function" |
任何其他对象 | "object" |
然则, typeof
却有下面这类题目
typeof "test" ---> "string"
typeof new String("test") ---> "object"
typeof 123 -----> "number"
typeof new Number(123) --->"object"
跟我们的希冀不太一样,Object.prototype.toString
则没有这题目。
Object.prototype.toString.call('test'); //"[object String]"
Object.prototype.toString.call(new String('test')); //"[object String]"
Object.prototype.toString.call(123); //"[object Number]"
Object.prototype.toString.call(new Number(123)); //"[object Number]"
所以,我们能够经由过程Object.prototype.toString
来举行范例推断
function isNumber(obj) {
return Object.prototype.toString.call(obj) === '[object Number]';
}
function isString(obj) {
return Object.prototype.toString.call(obj) === '[object String]';
}