分类导航

Underscore.js 源码学习笔记(下)

发布时间:2019-11-07 发布网站:大佬教程

   就是将 func 的 this 绑定到 context 并且预先传入参数 args (柯里化)

通过   实现了柯里化

bound 是绑定 this 后的函数,func 是传入的函数

  (!(callingContext boundFunc))  如果 callingContext 不是 boundFunc 的实例 就通过 apply 实现指定函数运行的 this

如果 callingContext 是 boundFunc 的实例,那意味着你可能是这么使用的

bindFoo = _.bind(foo,context bindFooInstance = bindFoo();

此时 bindFoo() 的 this 就是 bindFoo 的一个实例

那么 bindFooInstance 的 this 是应该绑定到 context 还是 bindFoo 的实例还是什么呢?

JavaScript 中 this 一共有四种绑定 默认绑定 < 隐式绑定 < 显示绑定 < new绑定

所以 这里应该优先使用... foo 的实例

思考一下嘛 如果是 ES5 中 new foo.bind(context) 是不是应该先使用 foo 的实例嘛 bound 只是一个中间函数

然后就是判断 foo 是否有返回值 有的话直接返回该值 否则返回 this 也是操作符 new 的规定

<div class="cnblogs_code">

_.partial = restArguments( placeholder = bound =  position = 0,length = args = ( i = 0; i < length; i++= boundArgs[i] === placeholder ? arguments[position++ (position < arguments.length) args.push(arguments[position++ executeBound(func,,args);
  };
  .partial.placeholder =<span style="color: #000000;"> ;
<span style="color: #008000;">//
<span style="color: #008000;"> e.g.

<span style="color: #0000ff;">function
add(a,b) { <span style="color: #0000ff;">return
a +<span style="color: #000000;"> b; }
<span style="color: #0000ff;">var
addOne = .partial(add,1<span style="color: #000000;">,);
addOne(
3); <span style="color: #008000;">//
<span style="color: #008000;"> 4

默认占位符是 _ 先给函数指定部分参数 不指定的就用下划线占位 生成一个新的只需要填写剩余参数的函数

<div class="cnblogs_code">

_.bindAll = restArguments(= flatten(keys,, index = (index < 1)   Error('bindAll must be passed function names' (index-- key ==
 obj ='xiaoming''25'   'hello,I am ' + .name + ' and i am ' + .age + ' years old.'= 'global name''getName','getAge' getName = obj.getName,getAge = obj.getAge,sayHello =
getAge();   
sayHello(); 

把一个对象的指定方法绑定到该对象。keys 可以是要绑定的函数数组或函数。

<div class="cnblogs_code">

_.memoize =  memoize =  cache = address = '' + (hasher ? hasher.apply( (!has(cache,address)) cache[address] = func.apply(=

这个函数还是简单实用的,通过缓存一个变量 cache 当传入相同的参数时直接返回上一次的结果即可。

hasher 是入参的哈希函数,来判断多次入参是否相同。如果不传哈希函数的话,默认就用第一个参数判断是否重复。所以如果入参不是只有一个的话,记得传 hasher 函数。

比如在计算斐波那契数列   可以通过记忆化递归防止大量重复计算。

<div class="cnblogs_code">

_.delay = restArguments( setTimeout( func.apply(

封装了一个函数,每次调用时都要等待 wait 毫秒再执行。

<div class="cnblogs_code">

_.defer = _.partial(_.delay,_,1);

通过 _.defer 来执行函数 _.defer(log) 可以使函数放到异步调用队列中,防止一些奇怪的错误吧。(确实遇到了一些时候需要  {...},0)  来执行函数才有效的情况,但是还不知道怎么总结规律= =)

<div class="cnblogs_code">


_.throttle =  previous = 0 (!options) options =<span style="color: #008000;">//<span style="color: #008000;"> later 函数是定时器指定执行的函数 context,args 不是设置定时器时指定的 而是执行 later 时决定的
<span style="color: #0000ff;">var
later = <span style="color: #0000ff;">function
<span style="color: #000000;">() {
<span style="color: #008000;">//
<span style="color: #008000;"> 如果 options.leading = false 的话就将 previous 设置为 0 作为标记 下一次执行 func 时就不会被立即执行了

previous = options.leading === <span style="color: #0000ff;">false
? 0<span style="color: #000000;"> : _.now();
timeout
= <span style="color: #0000ff;">null
<span style="color: #000000;">;
result
=<span style="color: #000000;"> func.apply(context,args);
<span style="color: #008000;">//
<span style="color: #008000;"> 这里判断 !timeout 真的好迷啊...

<span style="color: #0000ff;">if
(!timeout) context = args = <span style="color: #0000ff;">null
<span style="color: #000000;">;
};

<span style="color: #0000ff;">var throttled = <span style="color: #0000ff;">function<span style="color: #000000;">() {
<span style="color: #0000ff;">var now =<span style="color: #000000;"> _.now();
<span style="color: #008000;">//<span style="color: #008000;"> 如果没有上一次调用 或者 之前的调用已经结束 且 leading = false 会设置 previous = 0
<span style="color: #008000;">//<span style="color: #008000;"> previous = 0 且 options.leading = false 说明上一次 func 执行完成 此次的 fun 不需要立即执行 等 wait ms 再执行
<span style="color: #0000ff;">if (!previous && options.leading === <span style="color: #0000ff;">false) previous =<span style="color: #000000;"> now;
<span style="color: #008000;">//<span style="color: #008000;"> 根据当前时间和上一次调用的时间间隔与 wait 比较判断
<span style="color: #0000ff;">var remaining = wait - (now -<span style="color: #000000;"> previous);
context = <span style="color: #0000ff;">this; <span style="color: #008000;">//<span style="color: #008000;"> 注意每一次调用都会更新 context 和 args 而执行 later 用到的是这两个参数
args = arguments; <span style="color: #008000;">//<span style="color: #008000;"> 也就是说设置定时器时对应的参数 不一定是执行对应的参数~
<span style="color: #008000;">//<span style="color: #008000;"> remaining <= 0 则证明距离上次调用间隔大于 wait 了 可以被执行
<span style="color: #008000;">//<span style="color: #008000;"> 理论上 remaining > wait 不会存在 除非 now < previous 也就是系统时间出错了(被修改了
<span style="color: #0000ff;">if (remaining <= 0 || remaining ><span style="color: #000000;"> wait) {
<span style="color: #008000;">//<span style="color: #008000;"> 当设置了 leading 是不会进入这个分支的= =
<span style="color: #008000;">//<span style="color: #008000;"> 删除定时器 重置 previous 为当前时间 并执行 func
<span style="color: #0000ff;">if<span style="color: #000000;"> (timeout) {
clearTimeout(timeout);
timeout = <span style="color: #0000ff;">null<span style="color: #000000;">;
}
previous =<span style="color: #000000;"> now;
result =<span style="color: #000000;"> func.apply(context,args);
<span style="color: #0000ff;">if (!timeout) context = args = <span style="color: #0000ff;">null<span style="color: #000000;">;
}
<span style="color: #008000;">//<span style="color: #008000;"> 否则如果有 timeout 证明隔一段已经设置一段时间后执行 不再设置定时器

<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 间隔小于 wait 而且没有 timeout 的话 就设置一个定时器 到指定时间间隔后再执行</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 如果 options.trailing = false 则忽略这次调用 因为时间间隔在 timeout 之内</span>
<span style="color: #0000ff;"&gt;else</span> <span style="color: #0000ff;"&gt;if</span> (!timeout &amp;&amp; options.trailing !== <span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;) {
  </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 设置了 trailing 不会进入这个分支</span>
  timeout =<span style="color: #000000;"&gt; setTimeout(later,remaining);
}
</span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; result;

};
<span style="color: #008000;">//<span style="color: #008000;"> 重置 throttled 的状态 同时取消还没有执行的定时器
throttled.cancel = <span style="color: #0000ff;">function<span style="color: #000000;">() {
clearTimeout(timeout);
previous = 0<span style="color: #000000;">;
timeout = context = args = <span style="color: #0000ff;">null<span style="color: #000000;">;
};

<span style="color: #0000ff;">return<span style="color: #000000;"> throttled;
};
<span style="color: #008000;">//<span style="color: #008000;"> e.g.
<span style="color: #0000ff;">function<span style="color: #000000;"> log(sth) {
console.log('===> ' + sth + ' ' + <span style="color: #0000ff;">new<span style="color: #000000;"> Date().toLocaleTimeString());
}
<span style="color: #0000ff;">var tLog = .throttle(log,1000<span style="color: #000000;">);
<span style="color: #008000;">//<span style="color: #008000;"> === start === 20:29:54<span style="color: #008000;">
//<span style="color: #008000;"> ===> 1 20:29:54<span style="color: #008000;">
//<span style="color: #008000;"> ===> 4 20:29:55
<span style="color: #0000ff;">var tLog =
.throttle(log,1000,{ leading: <span style="color: #0000ff;">false<span style="color: #000000;"> });
<span style="color: #008000;">//<span style="color: #008000;"> === start === 20:30:15<span style="color: #008000;">
//<span style="color: #008000;"> ===> 4 20:30:16
<span style="color: #0000ff;">var tLog = .throttle(log,{ trailing: <span style="color: #0000ff;">false<span style="color: #000000;"> });
<span style="color: #008000;">//<span style="color: #008000;"> === start === 20:30:39<span style="color: #008000;">
//<span style="color: #008000;"> ===> 1 20:30:39<span style="color: #008000;">
//<span style="color: #008000;"> 不要同时设置 leading 和 trailing ~ 否则永远都不会被执行<span style="color: #008000;">
//<span style="color: #008000;"> var tLog =
.throttle(log,{ leading: false,trailing: false });
<span style="color: #000000;">
console.log('=== start === ' + <span style="color: #0000ff;">new<span style="color: #000000;"> Date().toLocaleTimeString());
tLog(1<span style="color: #000000;">);
tLog(2<span style="color: #000000;">);
tLog(3<span style="color: #000000;">);
tLog(4);

经典的函数来了= =

被称作节流函数 作用是在一定时间范围内只会被调用一次 即使被多次触发

<div class="cnblogs_code">

_.debounce = <span style="color: #0000ff;">var later = <span style="color: #0000ff;">function<span style="color: #000000;">(context,args) {
timeout
= <span style="color: #0000ff;">null<span style="color: #000000;">;
<span style="color: #0000ff;">if (args) result =<span style="color: #000000;"> func.apply(context,args);
};

<span style="color: #0000ff;">var debounced = restArguments(<span style="color: #0000ff;">function<span style="color: #000000;">(args) {
<span style="color: #0000ff;">if<span style="color: #000000;"> (timeout) clearTimeout(timeout);
<span style="color: #0000ff;">if<span style="color: #000000;"> (immediate) {
<span style="color: #0000ff;">var callNow = !<span style="color: #000000;">timeout;
<span style="color: #008000;">//<span style="color: #008000;"> 虽然有 timeout 但是这里的 later 没有传参所以不会执行 func
<span style="color: #008000;">//<span style="color: #008000;"> 只是为了标记之后的 wait 时间内都不会再执行函数
<span style="color: #008000;">//<span style="color: #008000;"> 如果等待的过程中又被调用 那么就从那个时间点开始再进行 wait 时间的不执行
timeout =<span style="color: #000000;"> setTimeout(later,wait);
<span style="color: #0000ff;">if (callNow) result = func.apply(<span style="color: #0000ff;">this<span style="color: #000000;">,args);
} <span style="color: #0000ff;">else<span style="color: #000000;"> {
timeout = _.delay(later,args);
}

</span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; result;

});

debounced.cancel = <span style="color: #0000ff;">function<span style="color: #000000;">() {
clearTimeout(timeout);
timeout = <span style="color: #0000ff;">null<span style="color: #000000;">;
};

<span style="color: #0000ff;">return<span style="color: #000000;"> debounced;
};

debounce 防抖函数 只有当隔指定时间没有重复调用该函数时才会执行,可应用于输入和页面滑动等情况

可以分成两种情况看 传 immediate 和不传 immediate

不传 immediate 的话 就是调用后设置定时器 wait 秒之后执行 这中间又被调用 那么从调用时刻开始重新计时

传 immediate 表示第一次调用就会被执行 然后标记之后的 wait ms 内不会被执行 这中间又被调用 那么从调用时刻开始重新计时

<div class="cnblogs_code">


_.wrap = 
 'hi ' + sayHi =sayHi('saber','kido'); 

<div class="cnblogs_code">

_.compose =  args = start = args.length - 1  i =
     result = args[start].apply(
     (i--) result = args[i].call(
 getName(firstname,lastname) {  firstname + ' ' + toUpperCase(str) {  sayHi(str) {  'Hi ' +'wenruo','duan'); 

我记得之前写过这个函数啊= =但是没找到 记忆出错了

就是一个把一堆函数从右到左连起来执行的函数。函数式编程中很重要的函数。

<div class="cnblogs_code">

_.after =   (--times < 1 func.apply(
=>* 1000'资源全部获取完成 可以进行下一步操作...'<span style="color: #0000ff;">var urls = ['urla','urlb','urlc'<span style="color: #000000;">];
<span style="color: #0000ff;">var
finishWithAfter =<span style="color: #000000;"> _.after(urls.length,finish);

<span style="color: #0000ff;">for (<span style="color: #0000ff;">var i = 0; i < urls.length; i++<span style="color: #000000;">) {
ajax(urls[i],finishWithAfter);
}
<span style="color: #008000;">//<span style="color: #008000;"> 获取 urla 资源...<span style="color: #008000;">
//<span style="color: #008000;"> 获取 urlb 资源...<span style="color: #008000;">
//<span style="color: #008000;"> 获取 urlc 资源...<span style="color: #008000;">
//<span style="color: #008000;"> 获取 urla 资源完成<span style="color: #008000;">
//<span style="color: #008000;"> 获取 urlc 资源完成<span style="color: #008000;">
//<span style="color: #008000;"> 获取 urlb 资源完成<span style="color: #008000;">
//<span style="color: #008000;"> 资源全部获取完成 可以进行下一步操作...

函数调用 times 遍才会被执行

<div class="cnblogs_code">

_.before =   (--times > 0= func.apply( (times <= 1) func = <span style="color: #008000;">//<span style="color: #008000;"> 调用前 times-1 次执行 之后每一次都返回之前的运行的值
<span style="color: #0000ff;">var
foo = .before(3<span style="color: #000000;">,.identity);

console.log(foo(1)) <span style="color: #008000;">//<span style="color: #008000;"> 1
console.log(foo(2)) <span style="color: #008000;">//<span style="color: #008000;"> 2
console.log(foo(3)) <span style="color: #008000;">//<span style="color: #008000;"> 2 (第 n 次开始调用不再执行 func 直接返回上一次的结果
console.log(foo(4)) <span style="color: #008000;">//<span style="color: #008000;"> 2

只有前 times-1 次执行传入的函数 func 后面就直接返回上一次调用的值。

<div class="cnblogs_code">

_.once = _.partial(_.before,2);

就是只有一次调用的时候会只执行,后面直接返回之前的值。

使用场景比如……单例模式?

<div class="cnblogs_code">

_.restArguments = restArguments;

将 restArguments 函数导出。

969行===下面是对象相关的函数了

<div class="cnblogs_code">


 hasEnumBug = !{toString: }.propertyIsEnumerable('toString' nonEnumerableProps = ['valueOf','isPrototypeOf','toString''propertyIsEnumerable','hasOwnProperty','toLocaleString'<span style="color: #0000ff;">var collectNonEnumProps = <span style="color: #0000ff;">function<span style="color: #000000;">(obj,keys) {
<span style="color: #0000ff;">var
nonEnumIdx =<span style="color: #000000;"> nonEnumerableProps.length;
<span style="color: #0000ff;">var
constructor =<span style="color: #000000;"> obj.constructor;
<span style="color: #0000ff;">var proto = _.isFunction(constructor) && constructor.prototype ||<span style="color: #000000;"> ObjProto;

<span style="color: #008000;">//<span style="color: #008000;"> Constructor is a special case.
<span style="color: #0000ff;">var prop = 'constructor'<span style="color: #000000;">;
<span style="color: #0000ff;">if (has(obj,prop) && !<span style="color: #000000;">_.contains(keys,prop)) keys.push(prop);

<span style="color: #0000ff;">while (nonEnumIdx--<span style="color: #000000;">) {
prop =<span style="color: #000000;"> nonEnumerableProps[nonEnumIdx];
<span style="color: #0000ff;">if (prop <span style="color: #0000ff;">in obj && obj[prop] !== proto[prop] && !<span style="color: #000000;">_.contains(keys,prop)) {
keys.push(prop);
}
}
};

IE9一下浏览器有bug就是一些属性重写后 不能在 for ... in 中遍历到,所以要单独判断。

<div class="cnblogs_code">

_.keys =  (!_.isObject(obj))  (nativeKeys)  keys = ( key  obj) 
  

如果ES5的   存在就直接调用,否则通过 for..in 获取所有的属性。

<div class="cnblogs_code">

_.allKeys =  (!_.isObject(obj))  keys = ( key 

获取对象的所有属性,包括原型链上的。

<div class="cnblogs_code">

_.values =  keys = length = values = ( i = 0; i < length; i++=

所有对象自有属性的值的集合

<div class="cnblogs_code">

_.mapObject = = keys === ( index = 0; index < length; index++ currentKey ==
 _2camel = str => str.replace(/_(\w)/g,(item,letter) => obj = { first: 'mo_li_xiang_pian',second: 'yong_ren_zi_rao'

对对象中每一个值执行 iteratee 函数,和 _.map 的区别是它返回的是对象。

<div class="cnblogs_code">

_.pairs =  keys = length = pairs = ( i = 0; i < length; i++=

返回一个数组,每一项都是键、值组成的数组。

<div class="cnblogs_code">

_.invert =  result = keys = ( i = 0,length = keys.length; i < length; i++=

对象的键值互换,值要变成建,所以确保值是可序列化的。

<div class="cnblogs_code">

_.functions = _.methods =  names = ( key 

对象中所有属性值为函数的属性名的集合按照字典序排序后返回。

<div class="cnblogs_code">

 createAssigner = (keysFunc,defaults) { 
    length = (defaults) obj = Object(obj); 
     (length < 2 || obj == )  ( index = 1; index < length; index++ source == keysFunc(source),
          l = ( i = 0; i < l; i++ key =
        
         (!defaults || obj[key] ===  0) obj[key] =.extend =<span style="color: #000000;"> createAssigner(.allKeys);
<span style="color: #008000;">//
<span style="color: #008000;"> .extend(obj,...otherObjs)
<span style="color: #008000;">
//
<span style="color: #008000;"> 把 otherObjs 上面的所有的属性都添加到 obj 上 相同属性后面会覆盖前面的

<span style="color: #000000;">
.extendOwn
= .assign =<span style="color: #000000;"> createAssigner(.keys);
<span style="color: #008000;">//
<span style="color: #008000;"> .extendOwn(obj,...otherObjs)
<span style="color: #008000;">
//
<span style="color: #008000;"> 把 otherObjs 上面的所有的自有属性都添加到 obj 上 相同属性后面会覆盖前面的

<span style="color: #000000;">
.defaults
= createAssigner(.allKeys,<span style="color: #0000ff;">true
<span style="color: #000000;">);
<span style="color: #008000;">//
<span style="color: #008000;"> .extend(obj,...otherObjs)
<span style="color: #008000;">
//
<span style="color: #008000;"> 对 otherObjs 上面的所有的属性 如果 obj 不存在相同属性名的话 就添加到 obj 上 相同属性后面被忽略

扩展对象的一些函数。

<div class="cnblogs_code">

 keyInObj =  key .pick = restArguments(<span style="color: #0000ff;">function<span style="color: #000000;">(obj,keys) {
<span style="color: #008000;">//
<span style="color: #008000;"> 通过 restArguments 传入的参数除了第一个都被合成了一个数组 keys

<span style="color: #0000ff;">var
result = {},iteratee = keys[0<span style="color: #000000;">];
<span style="color: #0000ff;">if (obj == <span style="color: #0000ff;">null) <span style="color: #0000ff;">return<span style="color: #000000;"> result;
<span style="color: #0000ff;">if<span style="color: #000000;"> (
.isFunction(iteratee)) {
<span style="color: #008000;">//<span style="color: #008000;"> 如果 iteratee (keys[0]) 是一个函数
<span style="color: #008000;">//<span style="color: #008000;"> 可以看做是 .pick(obj,context)
<span style="color: #008000;">//<span style="color: #008000;"> obj 中符合 iteratee(value,obj) 的键值对被返回
<span style="color: #0000ff;">if (keys.length > 1) iteratee = optimizeCb(iteratee,keys[1<span style="color: #000000;">]);
keys =<span style="color: #000000;">
.allKeys(obj);
} <span style="color: #0000ff;">else<span style="color: #000000;"> {
<span style="color: #008000;">//<span style="color: #008000;"> 如果 iteratee (keys[0]) 不是函数
<span style="color: #008000;">//<span style="color: #008000;"> 将 keys 数组递归压平 成为一个新数组 keys
<span style="color: #008000;">//<span style="color: #008000;"> 对于 obj 中的属性在 keys 中的键值对被返回
iteratee =<span style="color: #000000;"> keyInObj;
keys = flatten(keys,<span style="color: #0000ff;">false<span style="color: #000000;">);
obj =<span style="color: #000000;"> Object(obj);
}
<span style="color: #0000ff;">for (<span style="color: #0000ff;">var i = 0,length = keys.length; i < length; i++<span style="color: #000000;">) {
<span style="color: #0000ff;">var key =<span style="color: #000000;"> keys[i];
<span style="color: #0000ff;">var value =<span style="color: #000000;"> obj[key];
<span style="color: #0000ff;">if (iteratee(value,obj)) result[key] =<span style="color: #000000;"> value;
}
<span style="color: #0000ff;">return<span style="color: #000000;"> result;
});

筛选对象中部分符合条件的属性。

<div class="cnblogs_code">

_.omit = restArguments( iteratee = keys[0= (keys.length > 1) context = keys[1= _.map(flatten(keys,=  !

逻辑同上,相当于反向 pick 了。

<div class="cnblogs_code">

_.create =  result =

给定原型和属性创建一个对象。

<div class="cnblogs_code">

_.clone =  (!_.isObject(obj))  _.isArray(obj) ?

浅克隆一个对象。

看到   有点没看懂,感觉事情有点不简单……于是向下翻到了 1621 行,看到这有一堆代码……

首先一开始的时候 (42行) 我们看过   的定义,_ 是一个函数,_(obj) 返回一个 _ 实例,该实例有一个 _wrapped 属性是传入的 obj 。

我们上面的函数都是 _ 的属性,所以 _(obj) 中是没有这些属性的(_.prototype 中的属性才能被获得)

<div class="cnblogs_code">


_.chain =  instance == <span style="color: #008000;">//<span style="color: #008000;"> 返回链式结果 如果当前实例就有 _chain 则将结果包装成链式对象返回 否则就直接返回对象本身
<span style="color: #0000ff;">var
chainResult = <span style="color: #0000ff;">function
<span style="color: #000000;">(instance,obj) {
<span style="color: #0000ff;">return
instance.chain ?<span style="color: #000000;"> (obj).chain() : obj;
};

<span style="color: #008000;">//<span style="color: #008000;"> 将对象 obj 中的函数添加到 .prototype
.mixin = <span style="color: #0000ff;">function<span style="color: #000000;">(obj) {
<span style="color: #008000;">//<span style="color: #008000;"> 对于 obj 中每一为函数的属性
.each(.functions(obj),<span style="color: #0000ff;">function<span style="color: #000000;">(name) {
<span style="color: #008000;">//<span style="color: #008000;"> 都将该属性赋值给下划线
<span style="color: #0000ff;">var func = [name] =<span style="color: #000000;"> obj[name];
<span style="color: #008000;">//<span style="color: #008000;"> 同时在下划线的原型链上挂这个函数 同时这个函数可以支持链式调用
.prototype[name] = <span style="color: #0000ff;">function<span style="color: #000000;">() {
<span style="color: #0000ff;">var args = [<span style="color: #0000ff;">this<span style="color: #000000;">._wrapped];
push.apply(args,arguments);
<span style="color: #008000;">//<span style="color: #008000;"> 将 this._wrapped 添加到 arguments 最前面传入 func
<span style="color: #008000;">//<span style="color: #008000;"> 因为 this.wrapped 就是生成的一个下划线实例的原始的值
<span style="color: #008000;">//<span style="color: #008000;"> func 运行的 this 是
把 this._wrapped 也就是上一个链式函数的运行结果 传入 func
<span style="color: #008000;">//<span style="color: #008000;"> 将 this 和 func 的返回值传入 chainResult
<span style="color: #008000;">//<span style="color: #008000;"> 如果 this 是一个链式对象(有 chain 属性)就继续返回链式对象
<span style="color: #008000;">//<span style="color: #008000;"> 否则直接返回 obj
<span style="color: #0000ff;">return chainResult(<span style="color: #0000ff;">this<span style="color: #000000;">,func.apply(
,args));
};
});
<span style="color: #0000ff;">return<span style="color: #000000;"> _;
};

<span style="color: #008000;">//<span style="color: #008000;"> Add all of the Underscore functions to the wrapper object.<span style="color: #008000;">
//<span style="color: #008000;"> 将 传入 mixin<span style="color: #008000;">
//<span style="color: #008000;"> 下划线上每一个函数都会被绑定到
.prototype 这样这些函数才能被实例访问
<span style="color: #000000;">.mixin();

<span style="color: #008000;">//<span style="color: #008000;"> Add all mutator Array functions to the wrapper.<span style="color: #008000;">
//<span style="color: #008000;"> 把一些数组相关的函数也加到 .prototype
.each(['pop','push','reverse','shift','sort','splice','unshift'],<span style="color: #0000ff;">function<span style="color: #000000;">(name) {
<span style="color: #0000ff;">var method =<span style="color: #000000;"> ArrayProto[name];
_.prototype[name] = <span style="color: #0000ff;">function<span style="color: #000000;">() {
<span style="color: #0000ff;">var obj = <span style="color: #0000ff;">this<span style="color: #000000;">._wrapped;
method.apply(obj,arguments);
<span style="color: #0000ff;">if ((name === 'shift' || name === 'splice') && obj.length === 0) <span style="color: #0000ff;">delete obj[0<span style="color: #000000;">];
<span style="color: #0000ff;">return chainResult(<span style="color: #0000ff;">this<span style="color: #000000;">,obj);
};
});

<span style="color: #008000;">//<span style="color: #008000;"> Add all accessor Array functions to the wrapper.
.each(['concat','join','slice'],<span style="color: #0000ff;">function<span style="color: #000000;">(name) {
<span style="color: #0000ff;">var method =<span style="color: #000000;"> ArrayProto[name];
.prototype[name] = <span style="color: #0000ff;">function<span style="color: #000000;">() {
<span style="color: #0000ff;">return chainResult(<span style="color: #0000ff;">this,method.apply(<span style="color: #0000ff;">this<span style="color: #000000;">._wrapped,arguments));
};
});

<span style="color: #008000;">//<span style="color: #008000;"> 从一个含有链式的 实例中获取本来的值
.prototype.value = <span style="color: #0000ff;">function<span style="color: #000000;">() {
<span style="color: #0000ff;">return <span style="color: #0000ff;">this<span style="color: #000000;">._wrapped;
};

在  上添加一个函数,同时支持链式调用。惊叹于其实现的巧妙。

现在可以继续看 _.tap 作用就是插入一个链式调用中间,查看中间值。

_.tap = let obj = [1,2,3= (x) => { console.log('中间值是:'= _(obj).chain().map(x => x * x).tap(interceptor).filter(x => x < 5

通过例子可以感受的更清晰。 接下来_.isMatch 前面看过了,略。

<div class="cnblogs_code">


= 
  
   (a === b)  a !== 0 || 1 / a === 1 /
   (a ==  || b == )  
   (a !== a)  b !==
   type =  (type !== 'function' && type !== 'object' &&  b != 'object')  <span style="color: #008000;">//<span style="color: #008000;"> Internal recursive comparison function for isEqual.
deepEq = <span style="color: #0000ff;">function
<span style="color: #000000;">(a,bStack) {
<span style="color: #008000;">//
<span style="color: #008000;"> Unwrap any wrapped objects.

<span style="color: #0000ff;">if
(a <span style="color: #0000ff;">instanceof
_) a =<span style="color: #000000;"> a.wrapped;
<span style="color: #0000ff;">if
(b <span style="color: #0000ff;">instanceof
) b =<span style="color: #000000;"> b._wrapped;
<span style="color: #008000;">//
<span style="color: #008000;"> Compare [[Class]] names.

<span style="color: #0000ff;">var
className =<span style="color: #000000;"> toString.call(a);
<span style="color: #0000ff;">if
(className !== toString.call(b)) <span style="color: #0000ff;">return
<span style="color: #0000ff;">false
<span style="color: #000000;">;
<span style="color: #0000ff;">switch
<span style="color: #000000;"> (className) {
<span style="color: #008000;">//
<span style="color: #008000;"> Strings,numbers,regular expressions,dates,and booleans are compared by value.

<span style="color: #0000ff;">case
'[object RegExp]'<span style="color: #000000;">:
<span style="color: #008000;">//
<span style="color: #008000;"> RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')

<span style="color: #0000ff;">case
'[object String]'<span style="color: #000000;">:
<span style="color: #008000;">//
<span style="color: #008000;"> Primitives and their corresponding object wrappers are equivalent; thus,"5" is

<span style="color: #008000;">//
<span style="color: #008000;"> equivalent to new String("5").

<span style="color: #0000ff;">return
'' + a === '' +<span style="color: #000000;"> b;
<span style="color: #0000ff;">case
'[object Number]'<span style="color: #000000;">:
<span style="color: #008000;">//<span style="color: #008000;"> NaNs are equivalent,but non-reflexive.
<span style="color: #008000;">//<span style="color: #008000;"> Object(NaN) is equivalent to NaN.
<span style="color: #0000ff;">if (+a !== +a) <span style="color: #0000ff;">return +b !== +<span style="color: #000000;">b;
<span style="color: #008000;">//<span style="color: #008000;"> An egal comparison is performed for other numeric values.
<span style="color: #0000ff;">return +a === 0 ? 1 / +a === 1 / b : +a === +<span style="color: #000000;">b;
<span style="color: #0000ff;">case '[object Date]'<span style="color: #000000;">:
<span style="color: #0000ff;">case '[object Boolean]'<span style="color: #000000;">:
<span style="color: #008000;">//<span style="color: #008000;"> Coerce dates and booleans to numeric primitive values. Dates are compared by their
<span style="color: #008000;">//<span style="color: #008000;"> millisecond representations. Note that invalid dates with millisecond representations
<span style="color: #008000;">//<span style="color: #008000;"> of NaN are not equivalent.
<span style="color: #0000ff;">return +a === +<span style="color: #000000;">b;
<span style="color: #0000ff;">case '[object Symbol]'<span style="color: #000000;">:
<span style="color: #0000ff;">return SymbolProto.valueOf.call(a) ===<span style="color: #000000;"> SymbolProto.valueOf.call(b);
}

<span style="color: #0000ff;">var areArrays = className === '[object Array]'<span style="color: #000000;">;
<span style="color: #0000ff;">if (!<span style="color: #000000;">areArrays) {
<span style="color: #008000;">//<span style="color: #008000;"> 如果不是数组也不是对象的话 其他情况都已经比较完了 所以一定是 false
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof a != 'object' || <span style="color: #0000ff;">typeof b != 'object') <span style="color: #0000ff;">return <span style="color: #0000ff;">false<span style="color: #000000;">;

</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Objects with different constructors are not equivalent,but `Object`s or `Array`s</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; from different frames are.</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 如果都是自定义类型的实例 都有 constructor 的话 那么构造函数一定要相等</span>
<span style="color: #0000ff;"&gt;var</span> aCtor = a.constructor,bCtor =<span style="color: #000000;"&gt; b.constructor;
</span><span style="color: #0000ff;"&gt;if</span> (aCtor !== bCtor &amp;&amp; !(_.isFunction(aCtor) &amp;&amp; aCtor <span style="color: #0000ff;"&gt;instanceof</span> aCtor &amp;&amp;<span style="color: #000000;"&gt;
                         _.isFunction(bCtor) </span>&amp;&amp; bCtor <span style="color: #0000ff;"&gt;instanceof</span><span style="color: #000000;"&gt; bCtor)
                    </span>&amp;&amp; ('constructor' <span style="color: #0000ff;"&gt;in</span> a &amp;&amp; 'constructor' <span style="color: #0000ff;"&gt;in</span><span style="color: #000000;"&gt; b)) {
  </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;;
}

}
<span style="color: #008000;">//<span style="color: #008000;"> Assume equality for cyclic structures. The algorithm for detecting cyclic
<span style="color: #008000;">//<span style="color: #008000;"> structures is adapted from ES 5.1 section 15.12.3,abstract operation JO.

<span style="color: #008000;">//<span style="color: #008000;"> Initializing stack of traversed objects.
<span style="color: #008000;">//<span style="color: #008000;"> It's done here since we only need them for objects and arrays comparison.
<span style="color: #008000;">//<span style="color: #008000;"> 比较 stack 是为了防止对象的一个属性是对象本身这种情况
<span style="color: #008000;">//<span style="color: #008000;"> let obj = {}; obj.prop = obj;
<span style="color: #008000;">//<span style="color: #008000;"> 这种情况下比较对象再比较对象的每一个属性 就会发生死循环
<span style="color: #008000;">//<span style="color: #008000;"> 所以比较到每一个属性的时候都要判断和之前的对象有没有相等的
<span style="color: #008000;">//<span style="color: #008000;"> 如果相等的话 就判断另一个对象是不是也这样 来判断两个对象是否相等
<span style="color: #008000;">//<span style="color: #008000;"> 而不需要继续比较下去了~ 是不是很巧妙~
aStack = aStack ||<span style="color: #000000;"> [];
bStack = bStack ||<span style="color: #000000;"> [];
<span style="color: #0000ff;">var length =<span style="color: #000000;"> aStack.length;
<span style="color: #0000ff;">while (length--<span style="color: #000000;">) {
<span style="color: #008000;">//<span style="color: #008000;"> Linear search. Performance is inversely proportional to the number of
<span style="color: #008000;">//<span style="color: #008000;"> unique nested structures.
<span style="color: #0000ff;">if (aStack[length] === a) <span style="color: #0000ff;">return bStack[length] ===<span style="color: #000000;"> b;
}

<span style="color: #008000;">//<span style="color: #008000;"> Add the first object to the stack of traversed objects.
<span style="color: #000000;"> aStack.push(a);
bStack.push(b);

<span style="color: #008000;">//<span style="color: #008000;"> Recursively compare objects and arrays.
<span style="color: #0000ff;">if<span style="color: #000000;"> (areArrays) {
<span style="color: #008000;">//<span style="color: #008000;"> 如果是数组的话 需要比较其每一项都相等
<span style="color: #008000;">//<span style="color: #008000;"> Compare array lengths to determine if a deep comparison is necessary.
length =<span style="color: #000000;"> a.length;
<span style="color: #0000ff;">if (length !== b.length) <span style="color: #0000ff;">return <span style="color: #0000ff;">false<span style="color: #000000;">;
<span style="color: #008000;">//<span style="color: #008000;"> Deep compare the contents,ignoring non-numeric properties.
<span style="color: #0000ff;">while (length--<span style="color: #000000;">) {
<span style="color: #0000ff;">if (!eq(a[length],b[length],bStack)) <span style="color: #0000ff;">return <span style="color: #0000ff;">false<span style="color: #000000;">;
}
} <span style="color: #0000ff;">else<span style="color: #000000;"> {
<span style="color: #008000;">//<span style="color: #008000;"> 如果是对象的话 需要比较其每一个键都相等 对应的值再深度比较
<span style="color: #008000;">//<span style="color: #008000;"> Deep compare objects.
<span style="color: #0000ff;">var keys =<span style="color: #000000;"> .keys(a),key;
length =<span style="color: #000000;"> keys.length;
<span style="color: #008000;">//<span style="color: #008000;"> Ensure that both objects contain the same number of properties before comparing deep equality.
<span style="color: #0000ff;">if (
.keys(b).length !== length) <span style="color: #0000ff;">return <span style="color: #0000ff;">false<span style="color: #000000;">;
<span style="color: #0000ff;">while (length--<span style="color: #000000;">) {
<span style="color: #008000;">//<span style="color: #008000;"> Deep compare each member
key =<span style="color: #000000;"> keys[length];
<span style="color: #0000ff;">if (!(has(b,key) && eq(a[key],b[key],bStack))) <span style="color: #0000ff;">return <span style="color: #0000ff;">false<span style="color: #000000;">;
}
}
<span style="color: #008000;">//<span style="color: #008000;"> Remove the first object from the stack of traversed objects.
<span style="color: #008000;">//<span style="color: #008000;"> 讨论一个为什么要出栈 这个有点像 dfs 哈
<span style="color: #008000;">//<span style="color: #008000;"> obj = { a: { a1: ... },b: { b1: ... } }
<span style="color: #008000;">//<span style="color: #008000;"> 判断属性 a 的时候栈里是 [obj] 然后判断 a != obj
<span style="color: #008000;">//<span style="color: #008000;"> 接下来会递归判断 a1 以及其下属性
<span style="color: #008000;">//<span style="color: #008000;"> 到 a1 的时候 栈中元素为 [obj,a]
<span style="color: #008000;">//<span style="color: #008000;"> 当属性 a 被判断完全相等后 需要继续比较 b 属性
<span style="color: #008000;">//<span style="color: #008000;"> 当比较到 b 的时候 栈中应该是 [obj] 而不是 [obj,a]
<span style="color: #008000;">//<span style="color: #008000;"> a == b 不会造成死循环 我们不需要对不是父子(或祖先)关系的属性进行比较
<span style="color: #008000;">//<span style="color: #008000;"> 综上 这里需要出栈(大概没讲明白...反正我明白了...
<span style="color: #000000;"> aStack.pop();
bStack.pop();
<span style="color: #0000ff;">return <span style="color: #0000ff;">true<span style="color: #000000;">;
};

<span style="color: #008000;">//<span style="color: #008000;"> Perform a deep comparison to check if two objects are equal.
_.isEqual = <span style="color: #0000ff;">function<span style="color: #000000;">(a,b) {
<span style="color: #0000ff;">return<span style="color: #000000;"> eq(a,b);
};

深度比较两个对象是否相等。我已经开始偷懒了,英文有注释的地方不想翻译成中文了。

虽然很长,但是真的,考虑的很全面。

<div class="cnblogs_code">

_.isEmpty =  (obj == )   (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)))  obj.length === 0 _.keys(obj).length === 0

判断一个值是否为空。为 null、undefined、长度为空的(类)数组、空字符串、没有自己可枚举属性的对象。

<div class="cnblogs_code">

_.isElement =  !!(obj && obj.nodeType === 1

判断一个值是否是 DOM 元素。

nodeType 属性返回节点类型。

如果节点是一个元素节点,nodeType 属性返回 1。

如果节点是属性节点,nodeType 属性返回 2。

如果节点是一个文本节点,nodeType 属性返回 3。

如果节点是一个注释节点,nodeType 属性返回 8。

该属性是只读的。

<div class="cnblogs_code">

_.isArray = nativeIsArray ||  toString.call(obj) === '[object Array]'<span style="color: #008000;">//<span style="color: #008000;"> Is a given variable an object?
_.isObject = <span style="color: #0000ff;">function
<span style="color: #000000;">(obj) {
<span style="color: #0000ff;">var type = <span style="color: #0000ff;">typeof<span style="color: #000000;"> obj;
<span style="color: #0000ff;">return type === 'function' || type === 'object' && !!<span style="color: #000000;">obj;
};

isArray 判断一个值是否是数组 

isObject 判断对象是否是 object 或 function 注意判断 null

<div class="cnblogs_code">

_.each(['Arguments','Function','String','Number','Date','RegExp','Error','Symbol','Map','WeakMap','Set','WeakSet'],'is' + name] =  toString.call(obj) === '[object ' + name + ']'

批量增加一些判断类型的函数,逻辑和 isArray 一样呀。Map WeakMap Set WeakSet 都是 ES6 新增的数据类型。WeakSet 和 WeakMap 都没听过。该补习一波了~~~

<div class="cnblogs_code">

 (!=  has(obj,'callee'

一开始看到的,这个文件就是一个大的IIFE所以会有 arguments ,在 IE 低版本有 bug 不能通过

Object.prototype.toString.apply(arguments) === '[object Arguments]'

来判断。callee 是 arguments 对象的一个属性。可以通过该属性来判断。

都 8102 年了 放过 IE 不好吗?Edge 都开始使用 Chromium 内核了~~~~

<div class="cnblogs_code">


 nodelist = root.document && ( /./ != 'function' &&  Int8Array != 'object' &&  nodelist != 'function'=   obj == 'function' || 

优化 isFunction 因为在一些平台会出现bug 看了下提到的 issue #1621 (https://github.com/jashkenas/underscore/issues/1621)也不是很明白……

反正我试了下 nodejs v8 和最新版 Chrome 都进入了这个分支……emmm不管了……

<div class="cnblogs_code">


_.isFinite =  !_.isSymbol(obj) && isFinite(obj) && !<span style="color: #008000;">//<span style="color: #008000;"> Is the given value NaN?
.isNaN = <span style="color: #0000ff;">function
<span style="color: #000000;">(obj) {
<span style="color: #0000ff;">return
.isNumber(obj) &&<span style="color: #000000;"> isNaN(obj);
};

<span style="color: #008000;">//<span style="color: #008000;"> Is a given value a boolean?
_.isBoolean = <span style="color: #0000ff;">function<span style="color: #000000;">(obj) {
<span style="color: #0000ff;">return obj === <span style="color: #0000ff;">true || obj === <span style="color: #0000ff;">false || toString.call(obj) === '[object Boolean]'<span style="color: #000000;">;
};

<span style="color: #008000;">//<span style="color: #008000;"> Is a given value equal to null?
_.isNull = <span style="color: #0000ff;">function<span style="color: #000000;">(obj) {
<span style="color: #0000ff;">return obj === <span style="color: #0000ff;">null<span style="color: #000000;">;
};

<span style="color: #008000;">//<span style="color: #008000;"> Is a given variable undefined?
_.isUndefined = <span style="color: #0000ff;">function<span style="color: #000000;">(obj) {
<span style="color: #0000ff;">return obj === <span style="color: #0000ff;">void 0<span style="color: #000000;">;
};

emmm 显而易见了吧

<div class="cnblogs_code">

_.has =  (! length = ( i = 0; i < length; i++ key = (obj ==  || ! = !!
let obj = { a: { b: { c: 1'a','b','c']); 
_.has(obj,['a','d']); 
_.has(obj,[]); 

判断一个对象是否有指定属性,如果是数组则判断嵌套属性。空数组返回 false。和前面 deepGet 不同的是这里有 hasOwnProperty 判断是否是自有属性。

=== 1390 行 下面是 Utility Functions 一些工具方法 胜利在望✌️

<div class="cnblogs_code">

_.noConflict = = 

如果运行在浏览器等环境 不能直接导出变量 只能将 _ 赋值到全局变量 如果之前已经有变量叫做 _ 可以通过   underscore = _.noConflict();  获得_工具函数同时将 _ 赋值回原来的值。

<div class="cnblogs_code">

_.identity = 

是一个传入什么就返回什么的函数。看起来好像没什么用,但是前面有用到哒,可以作为 map 等函数的默认 iteratee 

a = [,,[1,3],[10,12],

参考 Stack Overflow 上面的一个找到的 >_<

<div class="cnblogs_code">

_.constant =  
image.fill( _.constant( black ) );

代码不难 同样让人困惑的是用途,在 Stack Overflow 找到一个用法举例。

<div class="cnblogs_code">

_.noop = (){};

返回一个空函数。可以用在需要填写函数但又不需要做任何操作的地方。

<div class="cnblogs_code">

_.propertyOf =  (obj ==    !_.isArray(path) ?

_.propertyOf 返回获取指定对象属性的方法。

<div class="cnblogs_code">

_.times =  accum = Array(Math.max(0,n)); 
  iteratee = optimizeCb(iteratee,1 ( i = 0; i < n; i++) accum[i] =
_.times(6,i => i * i); 
_.times(6,_.identity); 

运行一个函数 n 次来生成一个数组。每一次参数都是运行的次数,从 0 开始。

<div class="cnblogs_code">

_.now = Date.now ||  

Date.now 是 ES5(还是6)新增的,旧版本没有,通过new Date().getTime()获得

<div class="cnblogs_code">


 escapeMap ='&': '&amp;''<': '&lt;''>': '&gt;''"': '&quot;'"'": '&#x27;''`': '&#x60;' unescapeMap =<span style="color: #008000;">//<span style="color: #008000;"> Functions for escaping and unescaping strings to/from HTML interpolation.
<span style="color: #0000ff;">var
createEscaper = <span style="color: #0000ff;">function
<span style="color: #000000;">(map) {
<span style="color: #008000;">//
<span style="color: #008000;"> 以传入 escapeMap 举例

<span style="color: #0000ff;">var
escaper = <span style="color: #0000ff;">function
<span style="color: #000000;">(match) {
<span style="color: #008000;">//
<span style="color: #008000;"> 返回对应的转义后的字符串
<span style="color: #0000ff;">return<span style="color: #000000;"> map[match];
};
<span style="color: #008000;">//<span style="color: #008000;"> 生成一个正则表达式用来匹配所有的需要转义的字符 (?:&|<|>|"|'|)</span> <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 正则表达式有两种创建方式 通过 /.../ 字面量直接创建 或者通过 new RegExp(regStr) 创建</span> <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 这里的 ?: 表示正则表达不捕获分组 如果不添加这个的话 在 replace 中可使用 $i 代替捕获的分组</span> <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 比如</span> <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; '2015-12-25'.replace(/(\d{4})-(\d{2})-(\d{2})/g,'$2/$3/$1'); --> "12/25/2015"</span> <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; '2015-12-25'.replace(/(?:\d{4})-(\d{2})-(\d{2})/g,'$2/$3/$1'); --> "25/$3/12"</span> <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 为了防止 $1 变成捕获的字符串这里使用了 ?: (其实好像也用不到吧= =</span> <span style="color: #0000ff;"&gt;var</span> source = '(?:' + _.keys(map).join('|') + ')'<span style="color: #000000;"&gt;; </span><span style="color: #0000ff;"&gt;var</span> testRegexp = RegExp(source); <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 生成的正则表达式 /(?:&amp;|<|>|"|'|)/
<span style="color: #0000ff;">var replaceRegexp = RegExp(source,'g'); <span style="color: #008000;">//<span style="color: #008000;"> 生成的正则表达式 /(?:&|<|>|"|'|`)/g
<span style="color: #0000ff;">return <span style="color: #0000ff;">function<span style="color: #000000;">(string) {
string = string == <span style="color: #0000ff;">null ? '' : '' +<span style="color: #000000;"> string;
<span style="color: #0000ff;">return testRegexp.test(string) ?<span style="color: #000000;"> string.replace(replaceRegexp,escaper) : string;
};
};
.escape =<span style="color: #000000;"> createEscaper(escapeMap);
.unescape =<span style="color: #000000;"> createEscaper(unescapeMap);
<span style="color: #008000;">//<span style="color: #008000;"> e.g.
.escape('') <span style="color: #008000;">//<span style="color: #008000;"> &lt;html&gt;&lt;/html&gt;
.unescape('&lt;html&gt;&lt;/html&gt;') <span style="color: #008000;">//<span style="color: #008000;">

html实体字符的一些转义和反转义。

<div class="cnblogs_code">

_.result =  (!_.isArray(path)) path = length = (! _.isFunction(fallback) ? ( i = 0; i < length; i++ prop = obj ==  ?  0 (prop ===  0== length; 
= _.isFunction(prop) ?
_.result({ a: { b: 2 } },'d'],() => 'failed');   
_.result({ a: { b: 2 } },'b'],() => 'failed');   
_.result({ a: () => ({ b: 2 }) },'failed'); 
_.result({ a: () => ({ b: 2 }) },'failed'); 

又是一个看得莫名其妙的函数...

根据 path 获取 obj 的属性值,当获取不到时就返回 fallback 的执行结果。当遇到属性为函数时就把 上一层对象作为 this 传入执行函数然后继续向下查找。

<div class="cnblogs_code">

 idCounter = 0=  id = ++idCounter + '' prefix ? prefix +
_.uniqueId('DWR'); 
_.uniqueId('DWR'); 
_.uniqueId('XIA');   

就是通过闭包 返回一个不断递增的 id

_.template 我觉得值得用单独一篇博客来讲 = = 但其实我都是胡诌的!

首先要理解一下这个函数的用法

学过 jsp 的同学应该知道 jsp 中表达式可以写在 <%= %> 之间 而脚本可以写在 <% %> 在渲染的时候 会将脚本执行 表达式也会替换成实际值

这里的用法和那个基本一样

let template =用户ID:<%= userId %> 用户名:<%= username %> 用户密码:<%- password %> <% (userId === 1) { console.log('管理员登录...' { console.log('普通用户登录...'%>let render =<span style="color: #000000;"> _.template(template);

render({userId: 1,username: '管理员',password: ''<span style="color: #000000;">});
<span style="color: #008000;">/*<span style="color: #008000;"> render 返回:

用户ID:1 用户名:管理员 用户密码:&lt;pwd&gt;

前端三门语言中 只有 JavaScript 是图灵完备语言,你以为你写的模板是 html 添加了一些数据、逻辑,实际上 html 并不能处理这些代码

所以我们需要使用 JS 来处理它。处理后在生成对应的 HTML

把模板先生成一个 render 函数 然后为函数传入数据 就能生成对应 html 了。

除了上面的基础用法 我们可以自定义模板的语法 注意 key 要和 underscore 中定义的相等

默认是这样的

_.templateSettings =/<%([\s\S]+?)%>/g, js脚本 interpolate: /<%=([\s\S]+?)%>/g, 表达式 escape: /<%-([\s\S]+?)%>/g 表达式 生成后对 html 字符进行转义 如 < 转义为 &lt; 防止 XSS 攻击 };

我们可以自定义

let settings = { interpolate: /{{([\s\S]+?)}}/ }

现在 Vue 不是很火嘛 用一下 Vue 的语法

let template =
欢迎{{ data }}登录
= _.template(template,{ interpolate: /{{([\s\S]+?)}}/,variable: 'data''OvO'); 欢迎OvO登录

variable 指定了作用域 不指定时传入 render 的参数为 obj 的话 那么插值中 prop 获取到是 obj.prop 的值

variable 指定传入 render 函数参数的名字

理解了用法 现在思考怎样实现 如果让你写程序传入一段 js 代码输出运行结果 你会怎么办

憋说写一个解释器 >_<

大概就两种选择 eval() 和 new Function() (原谅我学艺不精 还有其他办法吗?)而 eval 只能运行一次 function 是生成一个函数 可以运行多次

生成的 render 有一个参数 source 是生成的函数字符串

这样我们可以达到预编译的效果 就像 vue 打包后的文件里面是没有 template 的 都是编译好的 render 函数

为什么要预编译?我们应该不想每一次运行都 new Function 吧 这个效率低大家应该都知道。其次,动态生成的函数,debug 不方便。

我们传入字符串 但这个字符串中不只有 js 代码还有些不相关的字符串。所以需要使用正则表达式将其中的 js 代码找出来,templateSettings 定义的就是这个正则表达式

如果是表达式就把运行结果和前后的字符串连接起来 如果是脚本就执行

具体看代码就好了

_.templateSettings =/<%([\s\S]+?)%>/g, interpolate: /<%=([\s\S]+?)%>/g, escape: /<%-([\s\S]+?)%>/g <span style="color: #008000;">//<span style="color: #008000;"> 这是一个一定不会匹配的正则表达式
<span style="color: #0000ff;">var
noMatch = /(.)^/<span style="color: #000000;">;

<span style="color: #008000;">//<span style="color: #008000;"> 因为后面要拼接一个函数体 有些字符放到字符串需要被转义 这里定义了需要转义的字符<span style="color: #008000;">
//<span style="color: #008000;"> \u2028 和 \u2029 不知道是啥 不想查了= =
<span style="color: #0000ff;">var escapes =<span style="color: #000000;"> {
"'": "'"<span style="color: #000000;">,'\': '\'<span style="color: #000000;">,'\r': 'r'<span style="color: #000000;">,'\n': 'n'<span style="color: #000000;">,'\u2028': 'u2028'<span style="color: #000000;">,'\u2029': 'u2029'<span style="color: #000000;">
};

<span style="color: #0000ff;">var escapeRegExp = /\|'|\r|\n|\u2028|\u2029/<span style="color: #000000;">g;

<span style="color: #0000ff;">var escapeChar = <span style="color: #0000ff;">function<span style="color: #000000;">(match) {
<span style="color: #0000ff;">return '\' +<span style="color: #000000;"> escapes[match];
};

.template = <span style="color: #0000ff;">function(text,settings,oldSettings) { <span style="color: #008000;">//<span style="color: #008000;"> oldSettings 为了向下兼容 可以无视
<span style="color: #0000ff;">if (!settings && oldSettings) settings =<span style="color: #000000;"> oldSettings;
<span style="color: #008000;">//<span style="color: #008000;"> 可以传入 settings 要和
.templateSettings 中属性名相同来覆盖 templateSettings
settings =<span style="color: #000000;"> .defaults({},.templateSettings);

<span style="color: #008000;">//<span style="color: #008000;"> reg.source 返回正则表达式两个斜杠之间的字符串 /\d+/g --> "\d+"
<span style="color: #008000;">//<span style="color: #008000;"> matcher 就是把三个正则连起来 /<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
<span style="color: #008000;">//<span style="color: #008000;"> 加了一个 $ 表示匹配字符串结尾
<span style="color: #0000ff;">var matcher =<span style="color: #000000;"> RegExp([
(settings.escape ||<span style="color: #000000;"> noMatch).source,(settings.interpolate ||<span style="color: #000000;"> noMatch).source,(settings.evaluate ||<span style="color: #000000;"> noMatch).source
].join('|') + '|$','g'<span style="color: #000000;">);

<span style="color: #0000ff;">var index = 0<span style="color: #000000;">;
<span style="color: #0000ff;">var source = "p+='"<span style="color: #000000;">;
<span style="color: #008000;">//<span style="color: #008000;"> 假设传入的 text 是 '

<%=x+1%>

'

text.replace(matcher,<span style="color: #0000ff;">function
<span style="color: #000000;">(match,escape,interpolate,evaluate,offset) {
<span style="color: #008000;">//
<span style="color: #008000;"> 函数的参数分别是:

<span style="color: #008000;">//
<span style="color: #008000;"> 匹配的字符串

<span style="color: #008000;">//<span style="color: #008000;"> 匹配的分组(有三个括号,所以有三个分组,分别表示 escape,evaluate 匹配的表达式)
<span style="color: #008000;">//<span style="color: #008000;"> 匹配字符串的下标
<span style="color: #008000;">//<span style="color: #008000;"> 第一次匹配: "

<%=x+1%>

" 会和 interpolate: /<%=([\s\S]+?)%>/g 匹配 interpolate 的值为 "x+1"
<span style="color: #008000;">//<span style="color: #008000;"> index = 0,offset 匹配的起始下标 就是截取字符串最前面未匹配的那一段
<span style="color: #008000;">//<span style="color: #008000;"> text.slice(index,offset) 就是 "

" 此时的 source 就是 "p+='

"
<span style="color: #008000;">//<span style="color: #008000;"> replace(escapeRegExp,escapeChar) 的作用是:
<span style="color: #008000;">//<span style="color: #008000;"> source 拼接的是一个 '' 包裹的字符串 有些字符放到 ' ' 里需要被转义

<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 第二次匹配:匹配字符串("<p><%=x+1%></p>")结尾</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; text.slice(index,offset) 此时获取的是 "</p>"</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 拼接后 source 为 "__p+='<p>'+\n((__t=(x+1))==null?'':__t)+\n'</p>"</span>
source +=<span style="color: #000000;"&gt; text.slice(index,offset).replace(escapeRegExp,escapeChar);
index </span>= offset + match.length; <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 匹配的起始下标+匹配字符串长度 就是匹配字符串末尾的下标</span>

<span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; (escape) {
  </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; ((__t = (_.escape(escape))) == null ? '' : __t)</span>
  <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; _.escape 是将生成的表达式中的 html 字符进行转义</span>
  source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"<span style="color: #000000;"&gt;;
} </span><span style="color: #0000ff;"&gt;else</span> <span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; (interpolate) {
  </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; ((__t = (interpolate)) == null ? '' : __t)</span>
  source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"<span style="color: #000000;"&gt;;
} </span><span style="color: #0000ff;"&gt;else</span> <span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; (evaluate) {
  </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 前面的字符串加分号 同时执行该脚本</span>
  source += "';\n" + evaluate + "\n__p+='"<span style="color: #000000;"&gt;;
}
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 第一次匹配后 interpolate 为 "x+1"</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 此时 source 是 "__p+='<p>'+\n((__t=(x+1))==null?'':__t)+\n'"</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 第二次匹配 escape、interpolate、evaluate 都不存在 不会改变 source</span>

<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Adobe VMs need the match returned to produce the correct offset.</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 返回 match 只是为了获取正确的 offset 而替换后的 text 并没有改变</span>
<span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; match;

});
source += "';\n"<span style="color: #000000;">;

<span style="color: #008000;">//<span style="color: #008000;"> 如果没有指定 settings.variable 就添加 with 指定作用域
<span style="color: #008000;">//<span style="color: #008000;"> 添加 with 之后 source 为 "with(obj||{}){\np+='

'+\n((t=(x+1))==null?'':__t)+\n'

\';\n}\n"
<span style="color: #0000ff;">if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'<span style="color: #000000;">;

source = "var t,p='',j=Array.prototype.join," +
"print=function(){
p+=j.call(arguments,'');};\n" +<span style="color: #000000;">
source + 'return
p;\n'<span style="color: #000000;">;
<span style="color: #008000;">//<span style="color: #008000;"> 最后生成的 source 为
<span style="color: #008000;">//<span style="color: #008000;"> "var t,print=function(){p+=j.call(arguments,'');};
<span style="color: #008000;">//<span style="color: #008000;"> with(obj||{}){
<span style="color: #008000;">//<span style="color: #008000;">
p+='

'+
<span style="color: #008000;">//<span style="color: #008000;"> ((t=(x+1))==null?'':t)+
<span style="color: #008000;">//<span style="color: #008000;"> '

';\n}\nreturn __p;
<span style="color: #008000;">//<span style="color: #008000;"> "

<span style="color: #0000ff;">var<span style="color: #000000;"> render;
<span style="color: #0000ff;">try<span style="color: #000000;"> {
<span style="color: #008000;">//<span style="color: #008000;"> 传入的参数1: settings.variable || obj
<span style="color: #008000;">//<span style="color: #008000;"> 传入的参数2: 使用于可以在插值中使用 里的函数
<span style="color: #008000;">//<span style="color: #008000;"> 函数体 source
render = <span style="color: #0000ff;">new Function(settings.variable || 'obj',''<span style="color: #000000;">,source);
<span style="color: #008000;">/*<span style="color: #008000;"> 生成函数 render
function anonymous(obj,
) {
var t,p = '',j = Array.prototype.join,print = function() {
p += j.call(arguments,'');
};
with(obj || {}) {
p += '

' +
((t = (x + 1)) == null ? '' : t) +
'

';
}
return __p;
}
<span style="color: #008000;">*/<span style="color: #000000;">
} <span style="color: #0000ff;">catch<span style="color: #000000;"> (e) {
e.source =<span style="color: #000000;"> source;
<span style="color: #0000ff;">throw<span style="color: #000000;"> e;
}

<span style="color: #0000ff;">var template = <span style="color: #0000ff;">function<span style="color: #000000;">(data) {
<span style="color: #0000ff;">return render.call(<span style="color: #0000ff;">this<span style="color: #000000;">,data,_);
};

<span style="color: #008000;">//<span style="color: #008000;"> Provide the compiled source as a convenience for precompilation.
<span style="color: #0000ff;">var argument = settings.variable || 'obj'<span style="color: #000000;">;
template.source = 'function(' + argument + '){\n' + source + '}'<span style="color: #000000;">;

<span style="color: #0000ff;">return<span style="color: #000000;"> template;
};

<span style="color: #0000ff;">var template = _.template("

<%=x+1%>

"<span style="color: #000000;">);
template({x: 'void'}) <span style="color: #008000;">//<span style="color: #008000;">

void1

尽管我看的一知半解,但是还是感觉学到了好多。

再下面就是 OOP 的部分上面已经基本分析过了

_.prototype.value = .prototype.valueOf = .prototype.toJSON =<span style="color: #000000;"> _.prototype.value;

_.prototype.toString = <span style="color: #0000ff;">function<span style="color: #000000;">() {
<span style="color: #0000ff;">return String(<span style="color: #0000ff;">this<span style="color: #000000;">._wrapped);
};

重写下划线的实例的 valueOf 、 toJSON 和 toString 函数

<div class="cnblogs_code">

 ( define == 'function' &&'underscore',[],

AMD(异步模块定义,Asynchronous Module Definition),这里为了兼容 amd 规范。

到此就把 下划线 1693 行全部看完了。

其实这是我第二遍看,到这次才能说勉强看懂,第一次真的是一头雾水。这期间看了点函数式编程的文章,也许有点帮助吧。

也开始理解了大家为什么说新手想阅读源码的话推荐这个,因为短、耦合度低、而且涉及到很多基础知识。

整体看下来,executeBound、OOP部分 和 _.template 这三部分花了很长时间思考。当然抽丝剥茧后搞懂明白的感觉,真的很爽呀哈哈哈哈哈

总之,完结撒花吧~

大佬总结

以上是大佬教程为你收集整理的Underscore.js 源码学习笔记(下)全部内容,希望文章能够帮你解决Underscore.js 源码学习笔记(下)所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ群:277859234,请注明来意。
标签:Underscore.js 源码学习笔记(下)
猜你在找的程序笔记相关文章
其他相关热搜词更多
php如何使用2javascriptJavaPythoncocos编程问答c#csschtmlrubyjsd3cocos2d文件
全站导航更多
最新程序笔记教程
热门程序笔记教程