那些不起眼的 JS 小工具?

謙龍發表於2017-05-09

前言

原文連結

原始碼地址

今天想寫一篇關於下劃線這個庫中一些小工具函式的故事,我們都聽過一句話,一個成功的男人背後一定有一個了不起的女人(?,其實也不一定,也許還有男人呢),那麼一個經久不衰,為程式猿們所稱道的庫,框架的背後自然也有不少看起來不起眼,甚至你都懶得正眼瞧他的"小工具"存在。正是這些背後的無名英雄為類庫和框架的形成,貢獻了不可磨滅的力量。

那些不起眼的 JS 小工具?
工具圖片

第一篇文章說了undefined,那我們也從undefined開始。

_.isUndefined(obj)

判斷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複製程式碼

對於一個物件上不存在的屬性

對於一個沒有返回值的函式

對於宣告和卻沒有賦值的標量

對於直接賦值為undefined(非ie8以下)或者void 0

_.isUndefined都會返回true,其他情況全都是返回false

需要特別注意的是,有時候我們會這樣判斷一個變數是都存在,a == null
a == undefined都可以通難過判斷。

if (a == null) { 

}複製程式碼

但是_.isUndefined用的是三等強制判斷,所以null是通過不了的

_.isUndefined = function(obj) {
  return obj === void 0;
}複製程式碼

_.isNull(obj)

判斷obj等於null與否,是就返回true,反之false。

這個沒啥說的,只有obj輸入null,結果輸出才為true,因為內部判斷也是用的三等判斷,不僅值要相等,型別也要相同。

_.noConflict()

防止全域性變數衝突的一種常見解決方案,將的使用權交換給上一個佔用坑位的人。

示例


<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; // 並將_返回以供後續使用
  };複製程式碼

_.identity(value)

返回與傳入的引數value一樣的值

這個函式看起來沒有什麼軟用,但是在後面能夠起非常大的作用,也正體現了,工具雖小,能量卻大

我們先來簡單地看下它的應用,在後續的原始碼分析中遇到再仔細講解。

  1. 過濾一個陣列中為"真"的值
let arr = ['a', 'b', null, 'false', 0, 'c', '', false, {}]
let arr2 = arr.filter(_.identity) // ["a", "b", "false", "c", {}]複製程式碼
  1. 複製陣列
let arr = ['a', 'b', null, 'false', 0, 'c', '', false, {}]
let arr2 = arr.map(_.identity) // ["a", "b", null, "false", 0, "c", "", false, {}]複製程式碼

_.constant(value)

返回一個函式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;
    };
  };複製程式碼

_.noop()

一個空函式,啥也不幹,呼叫了就返回undefined給你,可以作為預設的回撥引數

又是一個看起來啥用都沒有的函式,然而事實真的是這樣嗎?請移步以下幾個連結

  1. What is the use of jQuery.noop() function?
  2. What is the JavaScript convention for no operation?

例子不用多,總結一下

1. 給一個變數賦值為一個空函式,在後續的呼叫中你不需要去檢測他是不是undefined

2. 為什麼不給需要的變數重新設定一個空函式? _.noop已經建立了一個函式空間,讓其他變數也指向這個函式,可以減少js中不必要的花銷

_.times(n, iteratee, context)

呼叫給定的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次,並將對應的每次執行的結果存在了陣列中返回。

_.random(min, max)

返回一個[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的數,那結果就有可能不能按照預期出現了

_.uniqueId(prefix)

生成唯一的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()

一個優化的方式來獲得一個當前時間的整數時間戳。

直接看原始碼

_.now = Date.now || function() { // 如果原生支援now就用原生的,否知自己實現一個
  return new Date().getTime();
};複製程式碼

結尾

暫時就介紹這些看起來並不起眼的工具函式,在以後的文章和原始碼分析中遇到其他的會陸續更新到這篇文章中來。寫一篇文章真夠耗費時間的,陸陸續續用了好幾個小時才寫這麼點。

不介意的話,在文章開頭的原始碼地址那裡點一個小星星吧?

不介意的話,在文章開頭的原始碼地址那裡點一個小星星吧?

不介意的話,在文章開頭的原始碼地址那裡點一個小星星吧?

相關文章