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) {函数体}的方式来使用这个传入的数据。

 

 
相关文章
相关标签/搜索