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,對做者也是一種鼓勵。

相關文章
相關標籤/搜索