资讯专栏INFORMATION COLUMN

JS基础篇-underscore源码解析

anyway / 2671人阅读

摘要:总想找个机会夯实一下自己的基础,正好最近略有清闲,看视频读书撸代码我选择了第三者怎么感觉有点别扭,看视频的话效率不高适合入门,看书的话一本你不知道的推荐给大家,选择继续看书的话还是算了吧,毕竟读万卷书不如行万里路是吧。

总想找个机会夯实一下自己的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 _;
    });
  }
}());

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87325.html

相关文章

  • 源码解读这半年

    摘要:作者韩子迟不知不觉间,源码解读系列进入了真正的尾声,也请允许我最后一次下项目的原始地址这半年以来,花费了大量的业余时间,共计写了篇随笔包括此文,也给的源码加了差不多行注释,对于当初说的要做史上最详细的源码剖析,至此我也觉得问心无愧。 作者:韩子迟 What? 不知不觉间,「Underscore 源码解读系列」进入了真正的尾声,也请允许我最后一次 po 下项目的原始地址 https://...

    zzzmh 评论0 收藏0
  • javascript知识点

    摘要:模块化是随着前端技术的发展,前端代码爆炸式增长后,工程化所采取的必然措施。目前模块化的思想分为和。特别指出,事件不等同于异步,回调也不等同于异步。将会讨论安全的类型检测惰性载入函数冻结对象定时器等话题。 Vue.js 前后端同构方案之准备篇——代码优化 目前 Vue.js 的火爆不亚于当初的 React,本人对写代码有洁癖,代码也是艺术。此篇是准备篇,工欲善其事,必先利其器。我们先在代...

    Karrdy 评论0 收藏0
  • 即将立秋的《课多周刊》(第2期)

    摘要:即将立秋的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。课多周刊机器人运营中心是如何玩转起来的分享课多周刊是如何运营并坚持下来的。 即将立秋的《课多周刊》(第2期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大...

    ruicbAndroid 评论0 收藏0
  • 即将立秋的《课多周刊》(第2期)

    摘要:即将立秋的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。课多周刊机器人运营中心是如何玩转起来的分享课多周刊是如何运营并坚持下来的。 即将立秋的《课多周刊》(第2期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大...

    MRZYD 评论0 收藏0

发表评论

0条评论

anyway

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<