Underscore-分析

0.Ecmascript的版本重要時間點

Ecmascript262-3 1999年,ie5.5及之後徹底支持,ff,chrome等所有支持
Ecmascript262-4 由於跨越太大,廢棄
Ecmascript262-3.1 後更名ecmascript5 主流瀏覽器支持,ie8部分支持,ie9所有支持chrome

1.經常使用ecmascript5原生方法

1.1 Array.isArray(arg)
若是是數組,放回true數組

if (!Array.isArray) {
Array.prototype.isArray = function(ar) {
return Object.prototype.toString.call(ar) === 「[object Array]」
}
}瀏覽器

1.2 Object.keys(obj)
把obj的全部屬性枚舉放到數組裏返回,順序取決於for in的順序,Object.keys不枚舉原型鏈中的屬性緩存

if (!Object.keys) {
Object.prototype.keys = function(obj) {
var ar = [];
for (var i in obj) {
if (Object.prototype.hasOwnProperty.call(obj,i) {
ar.push(i);
}
}
return ar;
}
}安全

1.3 FuncProto.bind
fun.bind(thisArg[, arg1[, arg2[, ...]]])
if (!Function.bind) {
Function.prototype.bind = function(context) {
var fn = this,
arg = [];
for (var i = 1, len = arguments.length; i < len; i++) {
arg[i-1] = arguments[i];
}
return function() {
for (var j = 0, len2 = arguments.length; j < len2; j++) {
arg[i++] = arguments[j];
}
fn.apply(context, arg);
}
}
}閉包

1.4 Object.create
Object.create(prototype, [ propertiesObject ])
建立一個對象並返回,讓該對象的prototype對象指向參數的對象app

if (!Object.prototype.create) {
Object.prototype.create = (function() {
function fn() {dom

};
var hasown = Object.prototype.hasOwnProperty;
return function(proto, args) {
var obj;
fn.prototype = proto;
obj = new fn();
fn.prototype = null;
if (args) {
for (var i in args) {
if (hasown.call(args, i)) {
obj[i] = args[i];
}
}
}
return obj;ecmascript

}
}())
}函數

2. _

構建UnderScore的安全的構造函數

關鍵點是安全:
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
若是參數obj是經過構造函數_new出來的對象,那麼直接返回
若是this不是_的對象,說明可能不是經過new _的調用,因此執行
return new _(obj);
最終執行this._wrapped = obj;

2.1 new操做符的過程

當new ConstructorFn(...) 執行時:
1.一個新對象被建立。它繼承自ConstructorFn.prototype(newobj.prototype=foo.prototype).
2.構造函數 ConstructorFn 被執行。執行的時候,相應的傳參會被傳入,同時上下文(this)會被指定爲這個新實例。new ConstructorFn 等同於 new ConstructorFn(), 只能用在不傳遞任何參數的狀況。
3.若是構造函數返回了一個「對象」,那麼這個對象會取代整個new出來的結果。若是構造函數沒有返回對象,那麼new出來的結果爲步驟1建立的對象,ps:通常狀況下構造函數不返回任何值,不過用戶若是想覆蓋這個返回值,能夠本身選擇返回一個普通對象來覆蓋。固然,返回數組也會覆蓋,由於數組也是對象。

 

3.baseCreate 

建立一個對象,讓它的prototype指向參數對象,多用於子類繼承父類

首先判斷參數是否爲對象或函數,若是不是,返回空對象。

而後判斷是否存在原生create,若是有,調用。

若是沒有,經過ctor函數,讓ctort.prototype = prototype;

var result = new ctor();

ctor.prototype = null;

var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};

4.var property

var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};

生成安全的取對象屬性的方法,防止undefined或null取屬性值而報錯。

關鍵點:

obj == null ? void 0 : obj[key];

obj == null 其實是一個隱式轉換,就是當obj是null或undefined的時候。

==的隱式轉換規則,詳見Ecmascript: http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.1

若是雙方類型一致,對比值:

若是是undefined, true

若是是null, true

若是是number,

  若是其中一個是NaN,不相等

  若是值相等,相等

  +0, -0, 0是相等的。

若是是String,

  當兩個字符串長度一致,並且對應位置字符一致那麼相等。

若是是Boolean

  當二者都是true或者false時候相等。

若是是Object

  當二者的指針指向一個對象時相等。

 

若是類型不一致,

null, undefined相等

string,boolean和number比較時候會轉換成數字再比較

boolean和其餘的比較會轉換成number,true1 false 0

Object和string或number比較會先調用toString或valueOf,獲得值後在比較。

  

5. optimizeCb

用於優化回調函數:

1)  綁定上下文context,若是有context,返回綁定context的函數,用於後續操做。若是沒有context,直接返回

2)經過客戶端傳入的argCount決定返回函數的參數個數,防止使用arguments對象,由於在一些舊的v8版本中,使用

arguments會致使函數得不到瀏覽器優化。

6.createreduce

在函數內部建立一個閉包的迭代函數,而後返回一個函數,判斷參數,初始化而後調用這個迭代函數。

閉包做用

閉包大體有這麼幾個做用:避免命名衝突;私有化變量;變量持久化,讓函數參數固定值

這裏的做用主要就是變量(函數)持久化,好處就是重複調用的時候不須要再從新建立函數,從而提高執行速度。

爲何要用兩層閉包呢?第一層閉包持久化iterator函數,調用reducereduceRight函數避免重複新建函數。第二層閉包保存keys,index,length這些變量。

 

7.find

調用狀況以下,以findIndex爲例,咱們一個個分析它的函數

createPredicateIndexFinder(1)
相似於createReduce,傳入一個用於表示方向的參數,初始化
迭代的長度,起點位置,迭代器,而後執行,若是迭代器返回true,表示找到了,返回index。不然返回-1
而後看生成迭代器的cb函數
cb
是一個全部回調函數的入口函數,會根據傳入的值類型進行判斷,而後優化。
若是傳入的value是方法,調用optimizeCb優化。返回一個迭代器,參數是value, index, obj
 

8.filter

對每一個元素根據傳入的函數進行計算,若是true,將這個元素放入數組,最後返回數組。

內部仍是先用optimizeCb進行處理函數的綁定和優化,而後執行each,對每一個元素處理。

 

9.contains

1.調用values把對象的值轉換成數組返回

2.調用indexOf >=0 判斷是否在給定的數組(或對象中)

3.使用createIndexFinder生成indexOf方法,關鍵點有:

1)根據開始index和dir值,生成循環的條件

2)判斷是否被查找的元素是否是NaN(obj !== obj),若是是的話,對每一個元素調用isNaN判斷是否是NaN,若是有的話,返回true

 

10. isNaN

_.isNaN = function(obj) {
return _.isNumber(obj) && obj !== +obj;
};

通常咱們判斷NaN的方法只須要:a !== a,那麼就是NaN了。

而這裏是爲了同時檢驗出:new Object(NaN)

這個數的類型(toString)是Number,若是+obj的話就轉換成了NaN,知足new Object(NaN) !== NaN

另附對象轉換成數字:

When the [[DefaultValue]] internal method of O is called with hint Number, the following steps are taken:

  1. Let valueOf be the result of calling the [[Get]] internal method of object O with argument "valueOf".
  2. If IsCallable(valueOf) is true then,
    1. Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and an empty argument list.
    2. If val is a primitive value, return val.
  3. Let toString be the result of calling the [[Get]] internal method of object O with argument "toString".
  4. If IsCallable(toString) is true then,
    1. Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and an empty argument list.
    2. If str is a primitive value, return str.
  5. Throw a TypeError exception.

 首先調用valueOf,若是可執行,返回值,不然調用toString,返回值。

11.invoke

_.invoke(obj, method)

對obj的每一個元素執行method方法,結果組成數組並返回;

若是method是自定義函數,那麼執行method,不然,得到eachvalue[method]並執行。

 

12.shuffle

亂序排列一個數組,幾個關鍵點:

1)返回的是新數組

2)有可能順序是不變的

3)random的

3)核心是:

if (random !== index) shuffle[index] = shuffle[random]

shuffle[random] = set[index];

 

13.partial 

給函數設置預約義的變量,容許使用佔位符。

核心思想是在partial函數中處理參數,而後返回一個閉包函數,把兩部分參數組裝在一塊兒。

_.partial = function(func) {
  var args = slice.call(arguments, 1);
  return function() {
    return func.apply(this, args.concat(slice.call(arguments)));
  };
};
能夠把固定的操做和參數定義成fn,
var fn = function(myargs1,myargs2..);
而後使用
var mydefin = partial(fn, args);返回一個函數
 
14.memorize
緩存耗費長時間的函數的結果
經過var fab = memorize(fn);
得到一個執行函數,每次執行的時候看是否有關鍵字,若是
 
15.throttle
節流閥函數, _.throttle(function, wait, [options]) 
若是有很短期內的屢次調用,只在大於等於wait時間時候調用
 
 
15.after和before的技巧

_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};

由於參數也是函數內的一個變量,因此返回的閉包函數對參數的操做也是同一個內存位置,所以直接更新times進行判斷。

 
16. Infinity的妙用
用於設置無窮大或小的值,用於查找最大(小)值時候的初始化值:
var max = -Infinity;
if (value > max) {
  max = value;
}
 
17.eq
關鍵點
+0和-0如何區分:
1/+0 = Infinity
1/-0 =  -Infinity
 
18. 雙歎號做用
用於返回true或false
好比:
var a
此時a是undefined ,而!!a是false
 
 19.template函數
_.template(templateString, [settings]) 
總體思路:
在templateString裏設置
三個關鍵正則參數:
interpolate   插入內容,默認正則是       /<%=([\s\S]+?)%>/g
escape       插入轉義後內容,默認正則是       /<%-([\s\S]+?)%>/g
evaluate    只執行一次不返回值,默認正則是       /<%([\s\S]+?)%>/g 
 
填充選項對象,若是自定義的選項沒有上面的三個屬性,用默認填充。
建立這三個關鍵字的正則,對輸入的模板字符串進行全局的替換,
1.先對非捕獲的內容進行轉義

var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\u2028': 'u2028',
'\u2029': 'u2029'
};

2.而後對捕獲的內容處理,而後組裝成js代碼的字符串形式

對全部匹配的正則都作處理,組裝成一個大的js代碼字符串。

再組裝,增長參數定義,經常使用函數定義等內容在前頭。

最後使用new Function建立render函數,函數的參數有兩個:

第一個參數是傳入的變量對象,若是有setting.variable的話,使用這個字符串做爲變量名,而後模板中都是用這個變量名調用和訪問,速度會快。

若是沒有的話,就命名爲obj,而後經過with(obj) {函數體}的方式來使用這個傳入的數據。

 

 
相關文章
相關標籤/搜索