underscore 诞生记(二)—— 链式挪用与混入(mixin)

《underscore 诞生记(二)—— 链式挪用与混入(mixin)》

上篇文章报告了 underscore 的基础构造搭建,本文继承讲链式挪用与混入。

假如你还没看过第一篇文章,请点击 “underscore 诞生记(一)—— 基础构造搭建”

链式挪用

在 JQuery 中,我们常常使用到链式挪用,如:

$('.div')
  .css('color', 'red')
  .show();

那末在 underscore 中,是不是支撑链式挪用呢?答案是支撑的,只不过默许不开启链式挪用罢了。

想要完成链式挪用,一般我们会在支撑链式挪用的函数中返回对象自身:

let car = {
  run(name) {
    console.log(`${name}老司机开车啦喂!`);
    return this;
  },
  stop() {
    console.log('车停了');
  },
};

car.run('奔驰').stop();

// 奔驰老司机开车啦喂!
// 车停了

那末在每一个 _ 要领下都 return this , 明显不大文雅缺少可控性!尝试着写个通用要领 chain() 开启链式挪用。

_.chain = function(obj) {
  // 取得一个经underscore包裹后的实例
  var instance = _(obj);
  // 标识当前实例支撑链式挪用
  instance._chain = true;
  return instance;
};

// 小试牛刀
_.chain([1, 2, 3]);
/* 
{
    _chain: true,
    _wrapped: [1, 2, 3]
}
 */

返回的为一个实例对象,背面的要领推断 _chain 属性是不是为 true,为 true 的话再挪用一次 chain() 要领再返回本来实例即可。我们在之前用于给 prototype 复制要领的 each() 函数到场推断吧

var ArrayProto = Array.prototype;
var push = ArrayProto.push;
_.each(_.functions(_), function(name) {
  var func = _[name];
  _.prototype[name] = function() {
    var args = [this._wrapped];
    // args = [this._wrapped, arguments[0], arguments[1]...], 相当于用 this._wrapped 替代 obj 完成
    push.apply(args, arguments);
    return this._chain ? _(func.apply(_, args)).chain() : func.apply(_, args);
  };
});

有点冗杂,将 return this._chain ? _(func.apply(_, args)).chain() : func.apply(_, args); 革新下,

// 推断是不是须要链式挪用
var chainResult = function(instance, obj) {
  return instance._chain ? _(obj).chain() : obj;
};
var ArrayProto = Array.prototype;
var push = ArrayProto.push;
_.each(_.functions(_), function(name) {
  var func = _[name];
  _.prototype[name] = function() {
    var args = [this._wrapped];
    // args = [this._wrapped, arguments[0], arguments[1]...], 相当于用 this._wrapped 替代 obj 完成
    push.apply(args, arguments);
    return chainResult(this, func.apply(_, args));
  };
});

好了,碰运气结果:

_.chain([1, 2, 3])
  .each(function(item) {
    console.log(item);
  })
  .each(function(item) {
    console.log(item);
  });
// 1 2 3 1 2 3
// {_wrapped: [1,2,3], _chain: true}

混入(mixin)

underscore 很壮大,功用也很完全,但有时刻也不能满足一切人的需求。我们想建立一些要领,让它挂载在 _ 上,如许我们全局也能够挪用到这些要领,作为一款壮大的要领库,也应当供应这类接口,让用户自定增加要领,ok, let us do it !

我们先定义一个 mixin 要领

_.mixin = function(obj) {};

// `obj` 为一个相似 `_` 的对象。传入的这个对象,也须要遍历一次,而且复制要领于 prototype 属性上。细致代码以下:
_.mixin = function(obj) {
  _.each(_.functions(obj), function(name) {
    var func = (_[name] = obj[name]);
    _.prototype[name] = function() {
      var args = [this._wrapped];
      push.apply(args, arguments);
      // args = [this._wrapped, arguments[0], arguments[1]...], 相当于用 this._wrapped 替代 obj 完成
      return chainResult(this, func.apply(_, args));
    };
  });
  return _;
};

看到这里,你会发明,我们在要领的末了遍历赋值给_.prototype要领,实在就是一次mixin() 的挪用.

_.each(_.functions(_), function(name) {
  var func = _[name];
  _.prototype[name] = function() {
    var args = [this._wrapped];
    // args = [this._wrapped, arguments[0], arguments[1]...], 相当于用 this._wrapped 替代 obj 完成
    push.apply(args, arguments);
    return func.apply(_, args);
  };
});

// 简化为
_.mixin(_);

终究代码

(function() {
  // root 为挂载对象,为 self 或 global 或 this 或 {}
  var root =
    (typeof self == 'object' && self.self === self && self) ||
    (typeof global == 'object' && global.global === global && global) ||
    this ||
    {};

  var _ = function(obj) {
    // 假如传入的是实例后对象,返回它
    if (obj instanceof _) return obj;
    // 假如还没有实例化,new _(obj)
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };

  // 最大数值
  var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
  var ArrayProto = Array.prototype;
  var push = ArrayProto.push;
  // 推断是不是为数组
  var isArrayLike = function(collection) {
    var length = collection.length;
    return (
      typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX
    );
  };

  // 推断是不是须要链式挪用
  var chainResult = function(instance, obj) {
    return instance._chain ? _(obj).chain() : obj;
  };

  root._ = _;

  _.VERSION = '1.9.1'; // 给我们的 underscore 一个版本号吧

  /**
   * 字符串倒装
   */
  _.reverse = function(string) {
    return string
      .split('')
      .reverse()
      .join('');
  };

  /**
   * 推断是不是为 function
   */
  _.isFunction = function(obj) {
    return typeof obj == 'function' || false;
  };
  // 链式挪用要领
  _.chain = function(obj) {
    // 取得一个经underscore包裹后的实例
    var instance = _(obj);
    // 标识当前实例支撑链式挪用
    instance._chain = true;
    return instance;
  };
  /**
   * 猎取_的一切属性函数名
   */
  _.functions = function(obj) {
    var names = [];
    for (var key in obj) {
      if (_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
  };
  /**
   * 数组或对象遍历要领,并返回修改后的对象或数组
   * @param iteratee 回调函数
   * @param context 回调函数中this的指向
   */
  _.map = function(obj, iteratee, context) {
    var length = obj.length,
      results = Array(length);
    for (var index = 0; index < length; index++) {
      results[index] = iteratee.call(context, obj[index], index, obj);
    }

    return results;
  };

  /**
   * 数组或对象遍历要领
   */
  _.each = function(obj, callback) {
    var length,
      i = 0;

    if (isArrayLike(obj)) {
      // 数组
      length = obj.length;
      for (; i < length; i++) {
        //   这里隐式的挪用了一次 callback.call(obj[i], obj[i], i);
        if (callback.call(obj[i], obj[i], i) === false) {
          break;
        }
      }
    } else {
      // 对象
      for (i in obj) {
        if (callback.call(obj[i], obj[i], i) === false) {
          break;
        }
      }
    }

    return obj;
  };
  /*
   * 混入要领 mixin
   */
  _.mixin = function(obj) {
    _.each(_.functions(obj), function(name) {
      var func = (_[name] = obj[name]);
      _.prototype[name] = function() {
        var args = [this._wrapped];
        push.apply(args, arguments);
        // args = [this._wrapped, arguments[0], arguments[1]...], 相当于用 this._wrapped 替代 obj 完成
        return chainResult(this, func.apply(_, args));
      };
    });
    return _;
  };
  _.mixin(_);
})();

未完待续,静待下篇

前端进阶小书(advanced_front_end)

前端逐日一题(daily-question)

webpack4 搭建 Vue 运用(createVue)

《underscore 诞生记(二)—— 链式挪用与混入(mixin)》

    原文作者:suporka
    原文地址: https://segmentfault.com/a/1190000019027093
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞