丁鹿学堂:丁鹿带你读源码之underscore篇

82 阅读3分钟
什么是underscore

underscore作为开发中比较常用的一个javascript工具库,提供了一套丰富的函数式编程功能,该库并没有拓展原有的javascript原生对象,而是在自定义的_对象上,提供了100多个方法函数。它是帮助我们进行js增强的一个库。 和它类似的有lodash

编程思想之流式编程

学习underscore源码,精髓在于学习他的函数式编程的架构思想。 我们学习了解一下stream流式编程 流是一系列数据项,一次只生成一项。程序可以从输入流中一个一个读取数据项,再写入输出流。 我们可以理解为每个函数都是管道的一部分,这个函数的结果可以作为另一个函数的参数。最终通过拼接完成我们的需求。

underscore的封装思想,可以通过两种方式调用

一个库,我们既可以用函数的方式去调用,也可以用对象的方式去调用。怎么实现呢? ccc.filter(xxx) 或者通过 ccc(xxx).fliter 去调用
下面我们看一下,一个库内部怎么封装的原理,可以通过对象或者函数的形式去使用这个库,对于以后我们自己封装一些库是很有帮助的。 我们知道,函数本身也是对象的一种,所以我们可以把函数本身自己的功能逻辑放到自己的原型链上去,这样就实现了两种的调用方式。

(function(root){
  let _ = function(){
    // 如果_ 中的this不是指向 _,说明它不是对象,而是作为构造函数,我们直接返回它的实例。
    // 当链式调用的时候,已经是实例了,就不会再去new
    if(!(this instanceof _)){
      return new _()
    }
  }
  // 函数上的方法,new的实例是没有的。
  _.show = function(){
    console.log('show1')
  }
  _.prototype.show = function(){
    console.log('show2')
  }
  //  挂载到window上
  root._ = _
})(this)

但是上面的方法太死板了,等于是构造函数上面弄一套,原型对象上弄一套。 定义一个混入函数,_.mixin,遍历去添加方法到原型上即可。

(function(root){
  let _ = function(data){
    // 如果_ 中的this不是指向 _,说明它不是对象,而是作为构造函数,我们直接返回它的实例。
    // 当链式调用的时候,已经是实例了,就不会再去new
    if(!(this instanceof _)){
      return new _(data)
    }
    this.data = data
  }
  // 函数上的方法,原型上的实例是没有的。
  _.show = function(){
    console.log('show1')
  }
  // 定义函数,返回函数的所有属性,给mixin使用
  _.keyArr = function(target){
    let arr = []
    for(let key in target){
      arr.push(key)
    }
    return arr
  }
  // 功能拆分,通过回调函数的形式处理数组的每一个元素
  _.handleArr = function(arr,callback){
    for(let i =0;i<arr.length;i++){
      callback(arr[i])
    }
  }
  // 定义一个mixin函数,混入函数上的方法到原型对象上给实例用
  _.mixin = function(target){
    _.handleArr(_.keyArr(target),(item)=>{
      let fn = target[item]
      _.prototype[item] = function(){
        fn(this.data) 
      }
    })
  }
  _.mixin(_)
  //  挂载到window上
  root._ = _
})(this)

上面的代码mixin就是一个工具函数,会自动把构造函数上的所有方法都挂载到它的原型对象上。 这样我们以后扩展的时候,只需要给构造函数加属性即可。 这个思想就是用函数去管理函数,也是高阶函数的一个应用。 当然,真框架封装的时候会考虑更多的扩展性,包裹校验,参数的个数不同的扩展,我们可以通过简化来理解它的思想。