大佬教程收集整理的这篇文章主要介绍了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() 的 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;">//</span><span style="color: #008000;"> 间隔小于 wait 而且没有 timeout 的话 就设置一个定时器 到指定时间间隔后再执行</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 如果 options.Trailing = false 则忽略这次调用 因为时间间隔在 timeout 之内</span> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (!timeout && options.Trailing !== <span style="color: #0000ff;">false</span><span style="color: #000000;">) { </span><span style="color: #008000;">//</span><span style="color: #008000;"> 设置了 Trailing 不会进入这个分支</span> timeout =<span style="color: #000000;"> setTimeout(later,remaining); } </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> 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;">return</span><span style="color: #000000;"> 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 作用就是插入一个链式调用中间,查看中间值。
通过例子可以感受的更清晰。 接下来_.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 forisEqual
.
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 tonew 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;">NaN
s 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;"> Anegal
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;"> ofNaN
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;">//</span><span style="color: #008000;"> Objects with different constructors are not equivalent,but `Object`s or `Array`s</span> <span style="color: #008000;">//</span><span style="color: #008000;"> from different frames are.</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 如果都是自定义类型的实例 都有 constructor 的话 那么构造函数一定要相等</span> <span style="color: #0000ff;">var</span> aCtor = a.constructor,bCtor =<span style="color: #000000;"> b.constructor; </span><span style="color: #0000ff;">if</span> (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor <span style="color: #0000ff;">instanceof</span> aCtor &&<span style="color: #000000;"> _.isFunction(bCtor) </span>&& bCtor <span style="color: #0000ff;">instanceof</span><span style="color: #000000;"> bCtor) </span>&& ('constructor' <span style="color: #0000ff;">in</span> a && 'constructor' <span style="color: #0000ff;">in</span><span style="color: #000000;"> b)) { </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">; }
}
<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 operationJO
.<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 不能通过
来判断。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 valueNaN
?
.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
参考 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 ={ '&': '&''<': '<''>': '>''"': '"'"'": ''''`': '`' 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;">//</span><span style="color: #008000;"> 正则表达式有两种创建方式 通过 /.../ 字面量直接创建 或者通过 new RegExp(regStr) 创建</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 这里的 ?: 表示正则表达不捕获分组 如果不添加这个的话 在 replace 中可使用 $i 代替捕获的分组</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 比如</span> <span style="color: #008000;">//</span><span style="color: #008000;"> '2015-12-25'.replace(/(\d{4})-(\d{2})-(\d{2})/g,'$2/$3/$1'); --> "12/25/2015"</span> <span style="color: #008000;">//</span><span style="color: #008000;"> '2015-12-25'.replace(/(?:\d{4})-(\d{2})-(\d{2})/g,'$2/$3/$1'); --> "25/$3/12"</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 为了防止 $1 变成捕获的字符串这里使用了 ?: (其实好像也用不到吧= =</span> <span style="color: #0000ff;">var</span> source = '(?:' + _.keys(map).join('|') + ')'<span style="color: #000000;">; </span><span style="color: #0000ff;">var</span> testRegexp = RegExp(sourcE); <span style="color: #008000;">//</span><span style="color: #008000;"> 生成的正则表达式 /(?:&|<|>|"|'|
)/
<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;"> <html></html>
.unescape('<html></html>') <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 中表达式可以写在 <%= %> 之间 而脚本可以写在 <% %> 在渲染的时候 会将脚本执行 表达式也会替换成实际值
这里的用法和那个基本一样
render({userId:
1,username: '管理员',password: '前端三门语言中 只有 JavaScript 是图灵完备语言,你以为你写的模板是 html 添加了一些数据、逻辑,实际上 html 并不能处理这些代码
所以我们需要使用 JS 来处理它。处理后在生成对应的 HTML
把模板先生成一个 render 函数 然后为函数传入数据 就能生成对应 html 了。
除了上面的基础用法 我们可以自定义模板的语法 注意 key 要和 underscore 中定义的相等
默认是这样的
我们可以自定义
现在 Vue 不是很火嘛 用一下 Vue 的语法
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 定义的就是这个正则表达式
如果是表达式就把运行结果和前后的字符串连接起来 如果是脚本就执行
具体看代码就好了
<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%>
'<%=x+1%>
" 会和 interpolate: /<%=([\s\S]+?)%>/g 匹配 interpolate 的值为 "x+1"" 此时的 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;">//</span><span style="color: #008000;"> 第二次匹配:匹配字符串("<p><%=x+1%></p>")结尾</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> text.slice(index,offset) 此时获取的是 "</p>"</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> 拼接后 source 为 "__p+='<p>'+\n((__t=(x+1))==null?'':__t)+\n'</p>"</span>
source +=<span style="color: #000000;"> text.slice(index,offset).replace(escapeRegExp,escapeChar);
index </span>= offset + match.length; <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配的起始下标+匹配字符串长度 就是匹配字符串末尾的下标</span>
<span style="color: #0000ff;">if</span><span style="color: #000000;"> (escapE) {
</span><span style="color: #008000;">//</span><span style="color: #008000;"> ((__t = (_.escape(escapE))) == null ? '' : __t)</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> _.escape 是将生成的表达式中的 html 字符进行转义</span>
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"<span style="color: #000000;">;
} </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> (interpolatE) {
</span><span style="color: #008000;">//</span><span style="color: #008000;"> ((__t = (interpolatE)) == null ? '' : __t)</span>
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"<span style="color: #000000;">;
} </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> (evaluatE) {
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 前面的字符串加分号 同时执行该脚本</span>
source += "';\n" + evaluate + "\n__p+='"<span style="color: #000000;">;
}
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 第一次匹配后 interpolate 为 "x+1"</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> 此时 source 是 "__p+='<p>'+\n((__t=(x+1))==null?'':__t)+\n'"</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> 第二次匹配 escape、interpolate、evaluate 都不存在 不会改变 source</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> Adobe VMs need the match returned to produce the correct offset.</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> 返回 match 只是为了获取正确的 offset 而替换后的 text 并没有改变</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> 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"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;"> '
<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) +
'
<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;">);void1
尽管我看的一知半解,但是还是感觉学到了好多。
再下面就是 OOP 的部分上面已经基本分析过了
_.prototype.toString
= <span style="color: #0000ff;">function<span style="color: #000000;">() {重写下划线的实例的 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,请注明来意。