在閱讀 underscore 的過程當中,發現了它的一些小技巧,對咱們平時的編程頗有用。在這裏向你們介紹一二javascript
首先,看源碼: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 的保留字,因此不可被更改。編程
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]
實際業務代碼,除非對性能要求極高,不然仍是推薦 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
這種寫法比較優雅一點,你以爲呢?
這裏只舉兩個例子,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]'; }