原文連接javascript
源碼地址java
今天想寫一篇關於下劃線這個庫中一些小工具函數的故事,咱們都聽過一句話,一個成功的男人背後必定有一個了不得的女人(?,其實也不必定,也許還有男人呢),那麼一個經久不衰,爲程序猿們所稱道的庫,框架的背後天然也有很多看起來不起眼,甚至你都懶得正眼瞧他的"小工具"存在。正是這些背後的無名英雄爲類庫和框架的造成,貢獻了不可磨滅的力量。jquery
<!--more-->git
第一篇文章說了undefined,那咱們也從undefined開始。github
判斷obj等於undefined與否,是就返回true,反之false。數組
示例緩存
let a = null let b = window.b let c = () => {} let d = undefined let e = void 0 let f = 'qianlongo' let g console.log(_.isUndefined(a)) // false console.log(_.isUndefined(b)) // true console.log(_.isUndefined(c())) // true console.log(_.isUndefined(d)) // true console.log(_.isUndefined(e)) // true console.log(_.isUndefined(f)) // false console.log(_.isUndefined(g)) // true
對於一個對象上不存在的屬性閉包
對於一個沒有返回值的函數框架
對於聲明和卻沒有賦值的標量dom
對於直接賦值爲undefined(非ie8如下)或者void 0
_.isUndefined都會返回true,其餘狀況全都是返回false
須要特別注意的是,有時候咱們會這樣判斷一個變量是都存在,a = null
a = undefined
均可以通難過判斷。
if (a == null) { }
可是_.isUndefined用的是三等強制判斷,因此null是經過不了的
_.isUndefined = function(obj) { return obj === void 0; }
判斷obj等於null與否,是就返回true,反之false。
這個沒啥說的,只有obj輸入null
,結果輸出才爲true,由於內部判斷也是用的三等判斷,不只值要相等,類型也要相同。
防止全局變量衝突的一種常看法決方案,將_的使用權交換給上一個佔用_坑位的人。
示例
<script src="lodash.js"></script> <script src="underscore.js"></script> console.log(_)
碰見重名的事不新鮮對吧,全國有多少個小明啊,咱們從小到大課本里處處都是小明和小紅。
這裏後面引入的underscore.js把lodash.js給覆蓋了,由於兩個庫都想佔用全局的_,結果後來者居上。
若是不想lodash被覆蓋怎麼辦,總的有個先來後到啊。只須要調用noConflict方法便將佔着的_坑位從新歸還給了lodash,而以後咱們用my_便可訪問全部underscore.js的方法。
let my_ = _.noConflict()
接下來咱們看下源碼怎麼實現的
var previousUnderscore = _ // 在源碼的頂部,保存了前一個佔着_坑位的人 _.noConflict = function() { root._ = previousUnderscore; // 將_從新賦值給前一個佔着_坑位的人 return this; // 並將_返回以供後續使用 };
返回與傳入的參數value同樣的值
這個函數看起來沒有什麼軟用,可是在後面可以起很是大的做用,也正體現了,工具雖小,能量卻大
咱們先來簡單地看下它的應用,在後續的源碼分析中遇到再仔細講解。
過濾一個數組中爲"真"的值
let arr = ['a', 'b', null, 'false', 0, 'c', '', false, {}] let arr2 = arr.filter(_.identity) // ["a", "b", "false", "c", {}]
複製數組
let arr = ['a', 'b', null, 'false', 0, 'c', '', false, {}] let arr2 = arr.map(_.identity) // ["a", "b", null, "false", 0, "c", "", false, {}]
返回一個函數fn,fn執行以後再返回當初傳進來的value
咱們來看一段github上關於下劃線的一個issue,挺有意思的。也許咱們比較難列舉出這個函數的應用,可是至少下面這個例子是比較好的。
let age = 18 let cacheAge = _.constant(age) age += 10 console.log(cacheAge()) // 18
爲何能夠緩存住18,咱們看下源碼大概就知道了,源碼建立了常見的閉包,閉包常見的做用之一就是讓外面經過函數調用的形式去訪問內部的變量,以及在必定的生命週期內,緩存住變量。
_.constant = function(value) { return function() { return value; }; };
一個空函數,啥也不幹,調用了就返回undefined給你,能夠做爲默認的回調參數
又是一個看起來啥用都沒有的函數,然而事實真的是這樣嗎?請移步如下幾個連接
例子不用多,總結一下
1. 給一個變量賦值爲一個空函數,在後續的調用中你不須要去檢測他是否是undefined
2. 爲何不給須要的變量從新設置一個空函數? _.noop已經建立了一個函數空間,讓其餘變量也指向這個函數,能夠減小js中沒必要要的花銷
調用給定的iteratee迭代函數n次,iteratee每次都接收一個索引值index,最後返回一個數組,數組中存着這幾回iteratee的回調結果
示例
let count = 0 let result = _.times(6, (i) => { console.log(++count) return `hello:${i}` }) console.log(result) // ["hello:0", "hello:1", "hello:2", "hello:3", "hello:4", "hello:5"] console.log(count) // 6
能夠看到傳進去的函數執行執行了6次,並將對應的每次執行的結果存在了數組中返回。
返回一個[min, max]之間的隨機整數,若是沒有傳max,則區間是[0, min]
示例
let num1 = _.random(10, 20) // maybe 13 or other let num2 = _.random(10) // maybe 6 or other
源碼
_.random = function(min, max) { if (max == null) { // 若是隻有一個參數 max = min; // 就把第一個參數當最大值 min = 0; // 0做爲最小值 } return min + Math.floor(Math.random() * (max - min + 1)); // 試想咱們要求取[4, 10)之間的某個整數 // (min) 就是保證最小值能夠取到4 // (max - min + 1) => (10 - 4 + 1) => 7 // Math.random() * 7 => [0, 1) * 7 => [0, 7) // Math.floor([0, 7)) => 最小取0, 最大取6 // 最後變成 4 + [0, 6] => [4, 10] };
固然啦,若是你傳入非整數,或者max < min的數,那結果就有可能不能按照預期出現了
生成惟一的id,若是prefix不存在則直接將數字id返回,這個函數在給dom添加惟一的id的時候比較有用。
let pre = 'qianlongo' let id1 = _.uniqueId() // 1 let id2 = _.uniqueId(pre) // qianlongo2
源碼
var idCounter = 0; _.uniqueId = function(prefix) { var id = ++idCounter + ''; // 轉成字符串 return prefix ? prefix + id : id; };
一個優化的方式來得到一個當前時間的整數時間戳。
直接看源碼
_.now = Date.now || function() { // 若是原生支持now就用原生的,否知本身實現一個 return new Date().getTime(); };
暫時就介紹這些看起來並不起眼的工具函數,在之後的文章和源碼分析中遇到其餘的會陸續更新到這篇文章中來。寫一篇文章真夠耗費時間的,陸陸續續用了好幾個小時才寫這麼點。
不介意的話,在文章開頭的源碼地址那裏點一個小星星吧?
不介意的話,在文章開頭的源碼地址那裏點一個小星星吧?
不介意的話,在文章開頭的源碼地址那裏點一個小星星吧?