underscore中的小技巧

在閱讀 underscore 的過程當中,發現了它的一些小技巧,對咱們平時的編程頗有用。在這裏向你們介紹一二javascript

void 0 代替 underfined

首先,看源碼:java

_.isUndefined = function(obj) {
    return obj === void 0;
};

這裏爲啥要用 obj === void 0, 而不是 obj === undefined 呢?
由於,在 js 中,undefined 並非相似關鍵字(js 關鍵字有 function,return ...),因此,理論上是能夠更改的。
事實上,在 IE8 上也的確是能夠被更改的,chrome

var undefined = 1;
alert(undefined); // 1 -- IE8, undefined --- chrome

而在 chrome 或高版本的 IE 中,並不能更改全局的 undefined 。可是,局部的 undefined 仍然能夠被改變。例如:express

(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;
}

其實,咱們能夠利用數組的 sliceconcat 方法返回新的數組這個特性,來實現複製數組的功能;性能

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

當咱們編寫兩個功能很是相近的函數時,例如,實現複製一個數組的功能,分別是正序和倒序,咱們可能會這樣子實現(這裏只是爲了舉例子,複製數組推薦第二點提到的使用sliceconcat):

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);
};

這樣子實現會有什麼問題呢?

其實對copyDesccopyAsce,來講,只有 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,其餘的例如 isArgumentsisFunction , 因爲有些瀏覽器兼容問題須要特殊處理,這裏就不細說了。

而像isNullisUndefined,這些比較簡單的,這裏也不細說了:)

咱們知道:
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]';
}

待續。。。

相關文章
相關標籤/搜索