underscore 系列之防冲突与 Utility Functions

防冲突

underscore 使用 _ 做为函数的挂载对象,若是页面中已经存在了 _ 对象,underscore 就会覆盖该对象,举个例子:git

var _ = {value: 1 }

// 引入 underscore 后
console.log(_.value); // undefined
复制代码

因此 underscore 提供了 noConflict 功能,能够放弃 underscore 的控制变量 _,返回 underscore 对象的引用。github

var _ = {value: 1 }

// 引入 underscore 后

// 放弃 "_",使用 "$"
var $ = _.noConflict();

console.log(_.value); // 1

// 使用 underscore 的方法
$.each([1, 2, 3], alert);
复制代码

那么 noConflict 函数是如何实现的呢?数组

首先,在 underscore 执行的时候,会储存以前的 _ 对象,而后当执行 noConflict 函数的时候,再将以前储存的 _ 对象赋给全局对象,最后返回 underscore 对象。这样,咱们就能够利用返回的 underscore 对象使用 underscore 提供的各类方法。架构

// 源码一开始的时候便储存以前的 _ 对象
var previousUnderscore = root._;

_.noConflict = function() {
    root._ = previousUnderscore;
    return this;
};
复制代码

是的,就是这么简单。你能够轻松为你的函数库添加防冲突功能。dom

接下来咱们看 underscore 中的一些功能函数。ide

_.identity

_.identity = function(value) {
    return value;
};
复制代码

看起来匪夷所思的一个函数,传入一个值,而后返回该值,为何不直接使用该值呢?函数

还记得咱们在《underscore 系列以内部函数 cb 和 optimizeCb》中接触过这个函数吗?oop

若是咱们本身编写了一个 _.map 函数:ui

_.map = function(arr, iteratee){
    return arr.map(iteratee)
}
复制代码

然而当咱们这样使用 _.map([1, 2, 3]) 时便会报错,由于咱们没有传入 iteratee 函数,然而使用 underscore 却没有问题,结果是返回一个相同的新数组,缘由就在于当 iteratee 为 undefined 的时候,underscore 视为传入了 _.identity 函数。就至关于:this

_.map = function(arr, iteratee){
    if (!iteratee) iteratee = _.identity
    return arr.map(iteratee)
}
复制代码

简而言之,若是咱们想要复制一个数组:

var clonedArr = [1, 2, 3].map(_.identity) // [1, 2, 3]
复制代码

_.constant

_.constant = function(value) {
    return function() {
        return value;
    };
};
复制代码

该函数传入一个 value,而后返回一个返回该 value 的函数,这又有什么用呢?咱们来看个 demo:

var value = 1;
var getValue = _.constant(value);

value = 2;

getValue(); // 1
getValue(); // 1
复制代码

这很容易让人想到 ES6 的 const,我一开始觉得就是用来表示 ES6 的 const ,后来看了这个函数起源的 issue,才发现并不是如此,它其实像下面的 _.noop 函数同样能够做为默认函数使用。

举个例子:

_.select(collection, filterFunction || function() { return true; })
复制代码

咱们根据 filterFunction 筛选 collection 中符合条件的元素,若是没有传 filterFunction,咱们就返回全部的元素,若是有 _.constant 函数,咱们能够将其简化为:

_.select(collection, filterFunction || _.constant(true))
复制代码

尽管没有什么大的改变,可是语义更加明确。

_.noop

_.noop = function(){};
复制代码

一个空函数,看起来依旧没什么用……

noop 函数能够用于做为默认值,这样就能够省去是否存在的判断,举个例子:

// 不使用 noop
function a(value, callback){
    // 每次使用 callback 都要判断一次
    _.isFunction(callback) && callback()
}

// 使用 noop
function a(value, callback) {
    // 判断一次
    if(!_.isFunction(callback)) callback = _.noop;

    // 之后均可以直接使用
    callback()
}
复制代码

deepGet

var deepGet = function(obj, path) {
    var length = path.length;
    for (var i = 0; i < length; i++) {
        if (obj == null) return void 0;
        obj = obj[path[i]];
    }
    return length ? obj : void 0;
};
复制代码

deepGet 用于得到对象深层次的值。举个例子:

var obj = { 
    value: { 
        deepValue: 2
    } 
}

console.log(deepGet(obj, ['value', 'deepValue']))
复制代码

使用这个函数,能够避免深层次取值时,由于没有其中的一个属性,致使的报错。

shallowProperty

var shallowProperty = function(key) {
    return function(obj) {
      return obj == null ? void 0 : obj[key];
    };
};
复制代码

shallowProperty 也是用于获取对象的属性,也许你会好奇在开发中,直接使用. 不就能够获取对象的属性了,为何还要写成这样呢?咱们来举个例子:

// 获取 arr 全部元素的 name 属性
var arr = [
    {
        value: 1,
        name: 'Kevin'
    },
    {
        value: 2,
        name: 'Daisy'
    }
]

// 普通方式
var names = arr.map(function(item){
    return item.name;
})

// 使用 shallowProperty
var names = arr.map(shallowProperty('name'))
复制代码

_.property

_.property = function(path) {
    if (!_.isArray(path)) {
      return shallowProperty(path);
    }
    return function(obj) {
      return deepGet(obj, path);
    };
};
复制代码

_.property 结合了 deepGet 和 shallowProperty,能够获取元素深层次的值。上面一个例子也能够写成:

var names = arr.map(_.property('name'))
复制代码

_.propertyOf

_.propertyOf = function(obj) {
    if (obj == null) {
        return function(){};
    }
    return function(path) {
        return !Array.isArray(path) ? obj[path] : deepGet(obj, path);
    };
};
复制代码

_.property 返回一个函数,这个函数返回任何传入的对象的指定属性。

_.propertyOf_.property 相反。须要一个对象,并返回一个函数,这个函数将返回一个提供的属性的值。

咱们写个例子:

// 获取 person 对象的全部属性值
var person = {
    name: 'Kevin',
    age: '18'
};

// 普通方式
var values = Object.keys(person).map((key) => person[key]); // ["Kevin", "18"]

// 使用 _.propertyOf
var values = Object.keys(person).map(_.propertyOf(person)); // ["Kevin", "18"
复制代码

_.random

返回一个 min 和 max 之间的随机整数。若是你只传递一个参数,那么将返回 0 和这个参数之间的整数。

_.random = function(min, max) {
    if (max == null) {
      max = min;
      min = 0;
    }
    return min + Math.floor(Math.random() * (max - min + 1));
  };
复制代码

注意:该随机值有多是 min 或 max。

underscore 系列

underscore 系列目录地址:github.com/mqyqingfeng…

underscore 系列预计写八篇左右,重点介绍 underscore 中的代码架构、链式调用、内部函数、模板引擎等内容,旨在帮助你们阅读源码,以及写出本身的 undercore。

若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎 star,对做者也是一种鼓励。

相关文章
相关标签/搜索