博客主页 : https://hcthink.github.io/h-blog/
基础工具库代码解析
后文的 UN 库, 代指 underscore
时间问题,只挑大版本进行阅读
库相对简单, 暂时不做太详细解析. 待补充
最常见的就是用一个自调用形成闭包
// jQuery: 一定能挂载到 window 上
(function (root, undefined) {
.....
})(window)
// underscop: [兼容 web 和 node, this 可以是 global
// 此方式存在问题, 在严格模式下 全局的 this 指向一个空对象. 不推荐参考
// lodash 式
(function () {
}.bind(this)());
// }.call(this));
// 目前大多 underscore 都是这种, 可以兼容 web 和 node 等多种宿主环境
(function () {
const root = this;
}())
// 或者 利用严格模式下级块作用域 (不能使用 var, 用 let 和 const 代替)
'use strict'
{
// ....
}
web 库必须考虑的一个问题就是 lib 的大小, web 库一般不能太影响网络传输.
比较常见的方式在于巧妙利用压缩策略.比如: 一般库都会传入一个 window. 一是性能考虑,避免过深的查询作用域, 另一考虑就在于传入 window 后,使用一个引用接受,这个引用可以被压缩为一个字母.
(function (a, b) {
// .....
})(window)
// 其他
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
var
toString = ObjProto.toString,
forEach = ArrayProto.forEach,
propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
// ....
// Object.prototype.xxx 这种代码是不可压缩的,Object,prototype这些名字改了浏览器就不认得了。
// 但是上面的代码中创建了ObjProto之后,源生代码经过压缩之后,ObjProto就可能命名成 a 变量,那么原来的代码就压缩成 a.xxx
// 类似的还有:
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
// 一个小建议就是凡事一段代码被使用两次以上都建议定义变量(函数),有利于修改和压缩代码。
3.1 类型转换容错
条件判断的类型: 确保条件判断始终在对 bool 值进行判断
error: if(a)
right: if(!!a)
3.2 基础操作调用容错
实际上用一个变量承接一个函数还有另一些作用. 比如避免调用出错, 怎么讲呢, 比如我们一般情况下调用一个基础操作, 比如 toString 时. 基本上都是对象点的方式.
// eg:
obj.toString();
// 但是库种一般不能这么些, 因为这个 obj 往往是外界传入的.不能保证这个对象一定有 toString 操作, 或者是否被覆盖了, 如果没有这样将会报错. 所以一般的库都会采取这种写法
var toString = Object.prototype.toString;
toString.call(obj);
// 这样是非常稳妥的写法.
[注: 必须选取主版本进行对比分析.]
- noConflict. 参考: noConflict demo
代码很简单.保留最初的 root._ 的引用. 当进入本库之后, root._ 被替换成当前库的引用.如果出现如下情况: 使用此方法
引用多个不同版本的 underscore.
某个库也使用了 _ 作为简写方式挂载到宿主上.
noConflict 方法可以释放对 _ 的引用. 并返回后加载的库.避免替换掉了同名引用库
jquery 也是如此. 但是 jquery 有两个引用: jQuery & $.
jquery 的 ready 函数建议写成 jQuery(function () { // ... })
noconfig: http://www.cnblogs.com/laoyu/p/5189750.html
// 最开始拿到初试引用
var previousUnderscore = root._;
// ...
_.noConflict = function() {
// 在 noconf 中进行还原. 并返回当前库的引用
root._ = previousUnderscore;
// 存在问题, 原因是 noConflict 返回 this. 所以这里需要做一些语法绑定保证 this 不会被改变指向.
// 可以考虑比如 bind(this)
return this;
};
- 函数(多为 iteratee)包装器createCallback 1.8 之后被替换为optimizeCb, 实现没有太大改变
createCallback in v 1.7.0 / optimizeCb in v 1.8.0 +
// Current version.
_.VERSION = '1.7.0';
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
// 服务于 数组类数组 的扩充方法的 'cb(itaratee)' 的函数优化,用于指定 context, 并返回多种入参函数
// 这个函数逻辑没有复杂的地方, 整体是一个函数执行器
var createCallback/*optimizeCb // replace with optimizeCb in v1.8 */
= function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
case 2: return function(value, other) {
return func.call(context, value, other);
};
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
// 应对 reduce 的 iteratee(memo, obj[currentKey], currentKey, obj);
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
- 一个比较有用的 is 系列方法实现: 如下的 is 事件的 toString 格式有一致表现,所以能归成一种实现.比较巧妙
code: is-fn code
_.each([
'Arguments',
'Function',
'String',
'Number',
'Date',
'RegExp',
'Error'
], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) === '[object ' + name + ']';
};
});
last. 我们通过对一些方法实现的变迁一窥 un 的进化史. 以 reduce(此方法不像max 等操作, 他存在兼容问题.会看到更多的思考在里面) 为例
跳过三位版本,确实太多了
v 0.1.0 版本此方法命名为 inject[0.2即更名 reduce. 直到 0.3.2 实现并没有改变], 直接使用一个兼容的 each 操作.
// v 0.1.0
inject : function(obj, memo, iterator, context) {
_.each(obj, function(value, index) {
memo = iterator.call(context, memo, value, index);
});
return memo;
}
v 0.3.3 的版本中尝试在已经支持 reduce 的浏览器上利用系统提供的 reduce
// v 0.3.3
if (obj && obj.reduce) return obj.reduce(_.bind(iterator, context), memo);
v 0.5.1 的版本中, 对 reduce 做了进一步的校验. 因为有可能出现恶作剧的情况. 对象的 reduce 并非一个函数
// v0.5.1
if (obj && _.isFunction(obj.reduce)) return obj.reduce(_.bind(iterator, context), memo);
v 0.6.0 - v 1.1.0 很明显 0.5 的校验策略还是不那么安全.
原因是 reduce 即便是函数, 也未必具有可信度.也有可能别的框架注入了一个 reduce 函数却并不是 underscore 中的 reduce 函数想要实现的功能.
于是乎 un 做了更严格的匹配. 使用 js Array 上扩展的 reduce 函数. 这是个更加可靠的选择. 当然这个操作可能是一些库的扩展.比如 jq
// v 0.6.0 - v 1.1.0 .没有太大的变化
var nativeReduce = Array.prototype.reduce;
if (nativeReduce && obj.reduce === nativeReduce)
// ....
v 1.2.0 - 1.6.* 使用 ECMAScript 5 * 的 reduce 操作. 这方法在接受参数上和 jq 和原本 un 的实现有些差异. 所以在这里做了兼容
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = memo !== void 0;
// v 1.3.0 判断 memo 参数逻辑
// var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
// ...
};
1.7 + 的版本中加了一个 createCallback 的概念.对大部分函数都做了一个切面处理.后面详述这个函数
1.7 + 去平行处理对象和数组.
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`.
// 函数别称
_.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) {
if (obj == null) obj = [];
iteratee = createCallback(iteratee, context, 4);
// 兼容 object 对象的迭代
var keys = obj.length !== +obj.length && _.keys(obj),
length = (keys || obj).length,
index = 0, currentKey;
if (arguments.length < 3) {
if (!length) throw new TypeError(reduceError);
// 外部没有提供的时候,获取数组或者对象的第一个属性 作为 memo 的值
memo = obj[keys ? keys[index++] : index++];
}
for (; index < length; index++) {
// 平行处理数组和对象. for 是万能的.
currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};
// keys 的处理逻辑
_.keys = function(obj) {
if (!_.isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
1.8.* 中对相似逻辑的 reduce 和 reduceRight 做了一致化处理.
// Create a reducing function iterating left or right.
function createReduce(dir) {
// Optimized iterator function as using arguments.length
// in the main function will deoptimize the, see #1991.
function iterator(obj, iteratee, memo, keys, index, length) {
// 比较巧妙的是利用 dir 作为增量.这个 dir 不仅承载了标识方向的作用. 还做了 step. 但是呢.这个 dir 的实际上应该设计成 [1, -1]的枚举序列,会比较 ok
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
}
return function(obj, iteratee, memo, context) {
iteratee = optimizeCb(iteratee, context, 4);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
// Determine the initial value if none is provided.
if (arguments.length < 3) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
return iterator(obj, iteratee, memo, keys, index, length);
};
}
var isArrayLike = function(collection) {
var length = collection && collection.length;
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
_.reduce = _.foldl = _.inject = createReduce(1);
_.reduceRight = _.foldr = createReduce(-1);
1.9.0 对1.8 的方案进行了优化, 将数据的格式化和数据准备放到了 reduce 了里面,提高了方法的原子性.
var createReduce = function(dir) {
// Optimized iterator function as using arguments.length
// in the main function will deoptimize the, see #1991.
var reducer = function(obj, iteratee, memo, initial) {
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if (!initial) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};
return function(obj, iteratee, memo, context) {
var initial = arguments.length >= 3;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
};
github:
api:
http://www.css88.com/doc/underscore/ http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001450370530539bc6e0e3dc02c4d3bb79993a8cde056b5000
https://yoyoyohamapi.gitbooks.io/undersercore-analysis/content/base/%E7%BB%93%E6%9E%84.html
source desc:
http://www.css88.com/doc/underscore/docs/underscore.html#section-31
欢迎爱好技术的朋友一起讨论,技术不论高低,关键在于成长.^.^
如有错误欢迎讨论斧正.
转载声明原文链接, 尊重博主. 感谢