原文连接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(); };
暂时就介绍这些看起来并不起眼的工具函数,在之后的文章和源码分析中遇到其余的会陆续更新到这篇文章中来。写一篇文章真够耗费时间的,陆陆续续用了好几个小时才写这么点。
不介意的话,在文章开头的源码地址那里点一个小星星吧?
不介意的话,在文章开头的源码地址那里点一个小星星吧?
不介意的话,在文章开头的源码地址那里点一个小星星吧?