总想找个机会夯实一下自己的 JS 基础, 正好最近略有清闲, 看视频? 读书? 撸代码? 我选择了第三者(怎么感觉有点别扭), 看视频的话效率不高适合入门, 看书的话, 一本你不知道的 JavaScript推荐给大家, 选择继续看书的话还是算了吧, 毕竟读万卷书不如行万里路是吧. 撸代码的话, 撸个项目? 自己杂七杂八写了不少了, 都是老套路难有新的突破, 想想倒不如研究研究大神的代码, 学习学习大神的结构设计, 技术细节等站在大神的肩膀上是吧. 在选择是翻译 Loadsh 或 Underscore 的时候我更偏向于前者, 因为念起来比较顺嘴, 但当我看到 Loadsh 源码的时候发现, 这个! 这个! 我发现 "安得儿私购" 其实也挺顺嘴的, 1600 多行, API 丰富, 结构清晰, 就你了. 最后在补充几句, 看源码的感觉有点像 "春天的雨, 润物细无声", 没感觉自己技术有多大提高, 但当看到其他类库的时候已经知其大概结构, 当要自己实现. flatten 或. chain 的时候已经有了思路, 也算是有所进步吧. 此分析文章会持续更新, 如有错误感谢提出!
- // 参考文档: http://underscorejs.org/
- // 参考文档: http://www.bootCSS.com/p/underscore/
- /*
- 建议:
- 1 刚开始不要一行一行跟下来敲, 先了解一下库的整体结构
- 2 遇到不懂的打个断点, 多跟踪几遍, 断点很重要
- 3 有些内部函数使用频率很高(cb....), 这些内部函数了解清楚了后续看起来轻松不少
- 4 有些函数内, 有较多的函数引用, 建议多读几遍
- */
- (function() {
- // 获取根对象, 浏览器是 window(self),Node 是 global,window.window===window 返回 true,global 亦然
- var root = typeof self == 'object' && self.self === self && self || typeof global == 'object' && global.global === global && global || this || {};
- // 获取现有的_对象, 避免冲突, 具体解决冲突方法后面会说
- var previousUnderscore = root._;
- // 获取原型对象, 为的是写起来方便不用每次都 xxx.proto... 一大推
- var ArrayProto = Array.prototype,
- ObjProto = Object.prototype;
- var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype: null;
- var push = ArrayProto.push,
- slice = ArrayProto.slice,
- toString = ObjProto.toString,
- hasOwnProperty = ObjProto.hasOwnProperty;
- var nativeIsArray = Array.isArray,
- nativeKeys = Object.keys,
- nativeCreate = Object.create;
- // 中转函数, 后面用到会说
- var Ctor = function() {};
- /*
- _的构造函数,
- 第一步是看看 obj 是否是_的实例, 如果是就不操作直接返回, 有点像 $($("#d1")),jq 或 zepto 里也有相似的判断
- 第二步是判断 this 是否是_的实例, 不是则进行 new 调用,
- 注意, 在进行 new 调用的时候 new 会做 4 件事, 1 创建空对象, 2 空对象的__proto__指向函数的 prototype,3this 指向空对象(此时可能会添加属性等),4 判断返回值,
- 而此时在当前的_里 this 已经指向空对象
- 第三步为当前对象添加_wrapped 属性, 这是为了后面的链式调用做准备
- */
- var _ = function(obj) {
- if (obj instanceof _) return obj;
- if (! (this instanceof _)) return new _(obj);
- this._wrapped = obj;
- };
- // 根据当前环境添加_对象
- if (typeof exports != 'undefined' && !exports.nodeType) {
- if (typeof module != 'undefined' && !module.nodeType && module.exports) {
- exports = module.exports = _;
- }
- exports._ = _;
- } else {
- root._ = _;
- }
- // 版本号
- _.VERSION = '1.8.3';
- // 下面就是一些常用的方法了, 后面会一点一点分析
- // _.each=_.forEach=function(){......}
- //...
- //...
- //...
- /*
- 链式函数
- 实例化当前对象, 设置_china 为 true, 此为判断链式调用属性, true 为链式调用
- */
- _.chain = function(obj) {
- var instance = _(obj);
- instance._chain = true;
- return instance;
- };
- // 判断是否继续链式调用
- var chainResult = function(instance, obj) {
- return instance._chain ? _(obj).chain() : obj;
- };
- /*
- 扩展_的方法
- 第一步遍历 obj 里所含方法, 执行回调
- 回调内
- 1 获取 obj 的 function, 扩展到_里, 并保存到 func
- 2 对_的 prototype 进行扩展, 扩展函数里进行取值添加等操作 (注意 this 指向), 最后执行 func.apply(_, args)(注意 apply 还有打散数组的功能) 把结果和 this 作为参数传递到 chainResult 中, 判断是否继续链式调用
- 第二步 返回_
- 最后在解释一下为什么_.prototype[name]=function(){....}, 如果理解请跳过此段
- 大家一般都是_.filter({name:"Mr.zhou"},function(){.....})
- 链式调用说白了就是将第一个方法的执行结果作为参数传到第二个方法里, 如此依次传递, 直到最后一个返回结果;
- 想要链式调用常用的_.filter(...)的方法肯定是不行了, 具体实现请看例子
- var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];
- var youngest = _.chain(stooges)
- .sortBy(function(stooge){ return stooge.age; })
- .value();
- 1 创建 stooges 对象
- 2 创建 youngest 变量
- 3 详细看一下 youngest 值的计算方法
- 3.1 先是_.chain(stooges)这句话做了什么呢?(可以回顾一下之前的代码)
- 调用_.chain(stooges), 内部对_进行实例化, 并把 stooges 作为_wrapped 的值, 并添加了一个名为_chain 值为 true 的属性,
- 最后得到的就是这样一个对象{_wrapped:[{name: 'curly', age: 25}...],_chain:true}
- 3.2 继续调用
- {_wrapped:[{name: 'curly', age: 25}...],_chain:true}.sortBy(function(stooge){ return stooge.age; })
- .value();
- 等等, 这样对吗? 内个什么对象调用. sortBy 不报错吗? 它有这个方法吗?
- 是有的, 你没听错, 那么在哪里呢?
- 请看_.mixin 的这句换_.prototype[name]=function(){....}
- 这句话就是在往_的原型对象中添加方法, 在这句话之前的_.mixin(_), 与其内部的_.each(_.function(obj),...)就是将_上面的所有方法的地址引用传递给_.prototype 上, 而 {_wrapped:[{name: 'curly', age: 25}...],_chain:true} 对象又是_的实例对象,
- 自然也就继承了_.prototype 的方法, 这也就是链式调用的原理
- 3.3 最后调用 value()来返回它的_wrapped 就此结束
- */
- _.mixin = function(obj) {
- _.each(_.functions(obj),
- function(name) {
- var func = _[name] = obj[name];
- _.prototype[name] = function() {
- var args = [this._wrapped];
- push.apply(args, arguments);
- return chainResult(this, func.apply(_, args));
- };
- });
- return _;
- };
- // 自调 mixin 并把_传入
- _.mixin(_);
- // 同 mixin 差不多添加方法
- _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'],
- function(name) {
- var method = ArrayProto[name];
- _.prototype[name] = function() {
- var obj = this._wrapped;
- method.apply(obj, arguments);
- if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
- return chainResult(this, obj);
- };
- });
- // 同 mixin 差不多添加方法
- _.each(['concat', 'join', 'slice'],
- function(name) {
- var method = ArrayProto[name];
- _.prototype[name] = function() {
- return chainResult(this, method.apply(this._wrapped, arguments));
- };
- });
- // _.chain 的 value 方法
- _.prototype.value = function() {
- return this._wrapped;
- };
- // 添加相应方法
- _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
- // 添加相应方法
- _.prototype.toString = function() {
- return String(this._wrapped);
- };
- // 对 AMD 的兼容
- if (typeof define == 'function' && define.amd) {
- define('underscore', [],
- function() {
- return _;
- });
- }
- } ());
来源: http://www.qdfuns.com/article/28453/28531a5727a5c91f3c4397236ec3e20a.html