程序笔记   发布时间:2019-11-07  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Underscore.js 源码学习笔记(下)大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

上接 https://www.cnblogs.com/wenruo/p/10078450.html" target="_blank">Underscore.js 源码学习笔记(上)

=== 756 行开始 函数部分。

<div class="cnblogs_code">

 executeBound = sourceFunc,boundFunc,context,callingContext,args) {
   (!(callingContext  boundFunC)) sourceFunc.apply(context,args);
   self =sourceFunc.prototypE);
   result =sourceFunc.apply(self,args);
   (_.isObject(result)) .bind = restArguments(<span style="color: #0000ff;">function<span style="color: #000000;">(func,args) {
<span style="color: #0000ff;">if
(!.isFunction(funC)) <span style="color: #0000ff;">throw
<span style="color: #0000ff;">new
TypeError('Bind must be called on a function'<span style="color: #000000;">);
<span style="color: #0000ff;">var
bound = restArguments(<span style="color: #0000ff;">function
<span style="color: #000000;">(callArgs) {
<span style="color: #0000ff;">return
executeBound(func,bound,<span style="color: #0000ff;">this
<span style="color: #000000;">,args.concat(callArgs));
});
<span style="color: #0000ff;">return<span style="color: #000000;"> bound;
});

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

通过  s.concat(callArgs)  实现了柯里化

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 =l.placeholder;
   bound = {
     position = 0,length =s.length;
     args =);
     ( i = 0; i < length; i++{
      args[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({
  keys = flatten(keys,false,false);
   index =s.length;
   (index < 1)   Error('bindAll must be passed function names');
   (index--{
     key ==.bind(obj[key],obj);
  }
});

 obj ={
  name: 'xiaoming''25'{
     {
     Hello() {
     'Hello,I am ' + .name + ' and i am ' + .age + ' years old.'= 'global name'.bindAll(obj,'getName','getAge');
 getName = obj.getName,getAge = obj.getAge,sayHello =Hello;
getName();  
getAge();   
sayHello(); Hello,I am global name and i am undefined years old.

把一个对象的指定方法绑定到该对象。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);
});

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

<div class="cnblogs_code">

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

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

<div class="cnblogs_code">

在某一时间点 函数被执行 那么之后 wait 时间内的调用都不会被立即执行 而是设置一个定时器等到间隔等于 wait 再执行有两个字段可填  { leading: false } 或 { Trailing: false }{ leading: false } 表示调用时不会立即执行 而是等待 wait 毫秒之后执行{ Trailing: false } 表示执行之后的 wait 时间内的调用都忽略掉
_.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 = E) {
  <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">

C) 是预先给 wrapper 传入参数 func
_.wrap = {
  );
};

Name) {
   '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 ' +Name)('wenruo','duan'); 

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

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

<div class="cnblogs_code">

_.after = C) {
   {
     (--times < 1{
       func.apply();
    }
  };
};

{
  console.log(`获取 ${url} 资源...`);
  setTimeout(() =>{
    console.log(`获取 ${url} 资源完成`);
    fn();
  },Math.random() * 1000);
}
{
  console.log('资源全部获取完成 可以进行下一步操作...');
}

<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 = C) {
   {
     (--times > 0{
      memo = 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">

and thus missed.
 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) s.push(key);
  
  numbug) collectNonEnumProps(obj,keys);
  

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

<div class="cnblogs_code">

_.allKeys = {
   (!_.isObject(obj))  keys = ( key s.push(key);
  );
  

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

<div class="cnblogs_code">

_.values = {
   keys =);
   length =s.length;
   values =);
   ( i = 0; i < length; i++{
    values[i] =

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

<div class="cnblogs_code">

_.mapObject = ntext) {
  iteratee =ntext);
   keys ==s.length,results ={};
   ( 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'ject(obj,_2camel); { first: 'moLiXiangPian',second: 'yongRenZiRao' }

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

<div class="cnblogs_code">

_.pairs = {
   keys =);
   length =s.length;
   pairs =);
   ( i = 0; i < length; i++{
    pairs[i] =

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

<div class="cnblogs_code">

_.invert = {
   result ={};
   keys =);
   ( i = 0,length = keys.length; i < length; i++{
    result[obj[keys[i]]] =

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

<div class="cnblogs_code">

_.functions = _.methods = {
   names = ( key {
    y])) names.push(key);
  }
  s.sort();
};

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

<div class="cnblogs_code">

 createAssigner = (keysFunc,defaults) { {Boolean}
   {
     length =s.length;
     (defaults) obj = Object(obj); 
     (length < 2 || obj == ) j;
     ( index = 1; index < length; index++{
       source == keysFunc(sourcE),
          l =s.length;
       ( i = 0; i < l; i++{
         key =
        
         (!defaults || obj[key] ===  0) obj[key] =source[key];
      }
    }
    j;
  };
};

.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 j;
};

.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[0ntext;
  E)) {
    iteratee =E);
     (keys.length > 1) context = keys[1{
    keys = _.map(flatten(keys,falseString);
    iteratee = {
       !);
    };
  }
  ntext);
});

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

<div class="cnblogs_code">

_.create = {
   result =E);
  );
  

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

<div class="cnblogs_code">

_.clone = {
   (!_.isObject(obj)) j;
   _.isArray(obj) ?{},obj);
};

浅克隆一个对象。

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

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

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

<div class="cnblogs_code">


_.chain = {
   instance =);
  instance._chain = <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 = { interceptor(obj); j; }; let obj = [1,2,3= (X) => { console.log('中间值是:'X) } let result = _(obj).chain().map(x => x * X).tap(interceptor).filter(x => x < 5); { [number: 4] _wrapped: 4,_chain: true } 获取本来的数据 );

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

<div class="cnblogs_code">

cursive comparison function for `isEqual`.
= {
  jects are equal. `0 === -0`,but they aren't identical.
  http://wiki.ecmascript.org/doku.php?id=harmony:egal).
   (a === b)  a !== 0 || 1 / a === 1 /Strict comparison).
   (a ==  || b == )  false
   (a !== a)  b !==checks
   type =  (type !== 'function' && type !== 'object' &&  b != 'object')  false);
};

<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">

_.iSELER_405_11845@ent = {
   !!(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'],Name) {
  _['is' + name] = {
     toString.call(obj) === '[object ' + name + ']');

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

<div class="cnblogs_code">

 (!{
  _.isArguments = {
     has(obj,'callee');
  };
}

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

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

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

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

<div class="cnblogs_code">

9),and PhantomJS (#2236).
 nodelist = root.document && ( /./ != 'function' &&  Int8Array != 'object' &&  nodelist != 'function'{
  _.isFunction = {
      obj == 'function' || false

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

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

<div class="cnblogs_code">

ject a finite number?
_.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 ==  || !{
       false= !!
let obj = { a: { b: { c: 1'a','b','c']); 
_.has(obj,['a','d']); false
_.has(obj,[]); false

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

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

<div class="cnblogs_code">

_.noConflict = {
  root._ = 

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

<div class="cnblogs_code">

_.identity = value) {
  

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

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

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

<div class="cnblogs_code">

_.constant = value) {
   {
    { return color })
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 = ntext) {
   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 =p);

<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 = fallBACk) {
   (!_.isArray(path)) path = length = (!{
     _.isFunction(fallBACk) ?fallBACk.call(obj) : fallBACk;
  }
   ( i = 0; i < length; i++{
     prop = obj ==  ?  0 (prop ===  0{
      prop =fallBACk;
      i = length; Tinue iteraTing.
= _.isFunction(prop) ?j;
};

_.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 = 0EID = X) {
   id = ++idCounter + '' prefix ? prefix +
_.uniquEID('DWR'); 
_.uniquEID('DWR'); 
_.uniquEID('XIA');   

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

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

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

学过 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 ={ evaluate: /<%([\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'); render('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 ={ evaluate: /<%([\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 = "@H_607_2266@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||{}){\n@H_607_2266@p+='

'+\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 @H_607_2266@t,p='',@H_607_2266@j=Array.prototype.join," +
"print=function(){p+=@H_607_2266@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 @H_607_2266@t,print=function(){p+=@H_607_2266@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;"> ((@H_607_2266@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 @H_607_2266@t,p = '',@H_607_2266@j = Array.prototype.join,print = function() {
p += @H_607_2266@j.call(arguments,'');
};
with(obj || {}) {
p += '

' +
((@H_607_2266@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' &&{
  define('underscore',[],{
    );
}

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

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

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

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

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

总之,完结撒花吧~

大佬总结

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

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

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签:
猜你在找的程序笔记相关文章
其他相关热搜词更多
phpJavaPython程序员load如何string使用参数jquery开发安装listlinuxiosandroid工具javascriptcap