underscore 源码解读

903 阅读7分钟

今天闲来无事。来解读一下underscore这个工具库中的部分方法。前不久师父推荐了underscore来学习,了解到里面有众多方法很有意思,比较适合同学们来研究的!(当前版本1.8.3版本)

一、判断是否是对象_.isObject()

这里所说的对象 包括functionobject

源码:

_.isObject = function(obj) {
 var type = typeof obj;
 return type === 'function' || type === 'object' && !!obj;
};

解析: 先用typeof进行判断,将判断结果做判断,如果结果等于function 或者判断结果类型为object 并且为真值 (!!意为强制转布尔值true / false

调用:

let obj={a:1,b:2};
let str="abcd";
let arr=[1,2,3]

_.isObject(obj)   // true;
_.isObject(str)   // false;
_.isObject(arr)   // false;

二、判断是否是Array _.isArray()

源码:

var nativeIsArray  = Array.isArray

_.isArray = nativeIsArray || function(obj) { 
   return toString.call(obj) === '[object Array]';
};

解析: Array.isArrayjs自带的array判断方法,先用原生的方法判断一次,如果原生方法不存在,再用toStringe.call()方法判断,如果为真值,则是数组,否则不是。

调用:

let arr=[1,2,3];     
let arr2='2132132'
let arr3={a:1,b:2}
console.log(_.isArray(arr),_.isArray(arr2),_.isArray(arr3))  // true,false,false

三、判断是否为Boolean 类型 _.isBoolean()

源码:

_.isBoolean = function(obj) {
  return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};

解析: 先判断传入值是否可以直接转成truefalse,如果不可以,再使用toString()判断结果是不是布尔

调用:

console.log(_.isBoolean(true))   //true;

console.log(_.isBoolean(13))     //false;

四、判断是否是undefined_.isUndefined()

源码:

_.isUndefined = function(obj) {
  return obj === void 0;
};

解析: undefined 并不等于undefined,而void 0 的返回结果一直都会是undefined ,所以作者使用它进行判断

调用:

console.log( _.isNull(undefined))    // true
console.log( _.isUndefined(void 0))  // true

//当前传入undefined 和 void 0 结果为true 其他为false

五、判断类型toString.call()

通过判断toString.call(参数)来根据结果来判断是哪种类型,以上的几个判断类型的多是沿用这个方法而来;

调用:

toString.call("123")      // "[object String]"      字符串

toString.call(123)        // "[object Number]"      数字

toString.call(null)       // "[object Null]"        null

toString.call({a:1})      // "[object Object]"      对象

toString.call([1,5,8])    // "[object Array]"       数组

toString.call(true)       // "[object Boolean]"     布尔

toString.call(/a/g)       // "[object RegExp]"      正则

toString.call(undefined)  // "[object undefined]"   undefined

toString.call(new Date()) // "[object Date]"        日期对象

解析:
这种判断使用的是Object.prototype上的原生toString()方法判断数据类型

六、判断key是否存在于obj_.has()

源码:
_.has = function(obj, key) {
    return obj != null && hasOwnProperty.call(obj, key);
};

解析: 先判断obj是否为null,并且调用hasOwnProperty的方法检测key 是否存在于obj 判断key值是否存在于obj中,用于对象中的判断,还可通过此方法检查其是不是同一个原型

调用:
let obj={a:1,b:2};
console.log(_.has(obj,"a"))   // true
console.log(_.has(obj,"c"))   // false

七、通过属性和对象返回属性值

_.property(key)(obj) _.propertyOf(obj)(key)

这两个方法都是可以返回当前objkey对应的值

_.property(key)(obj)


源码:
_.property = function(key) {
    return function(obj) {
        return obj == null ? void 0 : obj[key];
    };
};

解析: 当前方法传入两个值 一个是obj 一个是key 先判断Obj是否是存在,是不是null,如果是 返回undefined 如果不是返回当前objekey的值

调用:    
let obj={
     a:111,
     b:222
 };

console.log(_.property("a")(obj));  // 111

_.propertyOf(obj)(key)


源码:
_.propertyOf = function(obj) {
    return obj == null ? function(){} : function(key) {
        return obj[key];
    };
};

解析: 和上一个方法大同小异,当前判断obj是否是null,如果是返回一个空函数, 果不是拿到key,去objkey上去匹配

调用:    
let obj={
     a:111,
    b:222
};

console.log(_.property("a")(obj));  // 111

八、生成min,max之间的随机数

下面我们先来温习一下math()的方法吧!

    四舍五入:
        Math.round(2.1)  // 2

    向下取整:
        Math.floor(5.8)  // 5

    向上取整:
        Math.ceil(3.2)   // 4

    生成0-1之间随机数:
    
        Math.random()
 
源码:
_.random = function(min, max) {
    if (max == null) {
        max = min;
        min = 0;
    }
    return min + Math.floor(Math.random() * (max - min + 1));
};

调用:

console.log(_.random(2,9))    //生成一个随机整数

解析:

let min = 5; let max=10;

第一步:当前方法传入两个值,在方法内判断第二个值是否为空,如果没有max的值,就将min赋给max作为最大值,min则赋值为0;

第二步:直接进行下面的方法(Math.random()*(max-min+1))== 当前函数生成0(max-min+1))之间的数,带入例子中:就是生成了 :0 - 6之间的数字;

第三步: 那么执行完上面第一步,我们得到的时候0 - 6之间的数字,使用Math.floor的方法将得到的值向下取整,区间就是 0 - 5(包括5)

最后一步:带入例子中,区间变为 4 + 04 + 5,将最小值与拿到的值合并,符合要求在 min- max 之间。


九、判断是否是DOM 元素

源码:
_.isElement = function(obj) {
    return !!(obj && obj.nodeType === 1);
};

解析: 先确定是否传入了值,并且判断元素的nodeType的类型是否为1,然后用!!强制转为布尔值。


十、判断是否在最大值和最小值之间

_.isFinite = function(obj) {
   return isFinite(obj) && !isNaN(parseFloat(obj));
};

这里面的这个方法,其实现在单用isFinite()就可以判断出来,判断是否是一个有限的数字,即值在最大值和最小值之间的,返回布尔值:

console.log(Number.MAX_VALUE);    // 最大值
console.log(Number.MIN_VALUE);   // 最小值

例子:

isFinite(Number.MAX_VALUE); // true 

isFinite(Number.MIN_VALUE); // true

isFinite(Number.MAX_VALUE*100); // false

如果是加引号''的数字还可自动转换,输入最后结果 调用:

_.isFinite( "888")    // true
_.isFinite( "aaa")    // false

十一、获取当前时间的时间戳,毫秒(关于时间的各种方法我会单独出一篇,敬请期待)

_.now = Date.now || function() {
    return new Date().getTime();
};

调用:

console.log(_.now());   // 1499913487397

等同于:

console.log(Date.now());            // 1499913487397
console.log(new Date().getTime());   // 1499913487397

十二、 _.size(),如为参数为数组返回数组长度,如为对象返回键值对数量。

源码:

_.size = function(obj) {
    if (obj == null) return 0;
        return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};

解析: 当前方法根据传入的值来判断,如传入的值为数组,返回的为数组的长度,如传入值为对象,返回键值对的数量。 在最上层去筛选是否为空, if (obj == null) return 0;如为空则直接返回0,接着测试是否是数组isArrayLike(obj),如果验证是数组,直接返回本身的length即可,如果不是接下来使用对象,调用_.keys()的方法,方法返回一个由一个给定对象的自身可枚举属性组成的数组,等同于Object.keys()的方法实现效果,如下:

let obj = { foo: "bar", baz: 42 };
Object.keys(obj)        //  ["foo", "baz”]

拿到数组后,直接拿length 既是键值对的数量。

调用:

let arr = [1,2,3];
console.log( _.size(arr))   // 3

let obj = {a:1,b:2};
console.log(_.size(obj))   //  2

十三、 _.last(arr,n) 默认返回数组最后一个元素,如有第二个参数,则返回该数组后n个元素组成的数组。

  _.last = function(array, n, guard) {
    if (array == null) return void 0;

    // 如果没有第二个参数n,直接返回最后一个元素
    if (n == null || guard) return array[array.length - 1];

    // 如果传入参数 n,则返回 n 之后元素组成的数组
    return _.rest(array, Math.max(0, array.length - n));
  };

解析: 第一步首先排空,排除array为空的状态,直接返回undefined,接下来检查是否有第二个参数,如果没有,直接返回array的最后一个元素;接下来结合_.rest()的方法来拿到数组。

//返回剔除第n个元素后的数组副本
  _.rest = _.tail = _.drop = function(array, n, guard) {
    return slice.call(array, n == null || guard ? 1 : n);
  };

rest方法里使用了 slice.call(array,n) 来将数组截取,剔除array数组的第n个值前面的元素,剩下从第n个后面元素组成第数组。 在配合_.rest的方法的时候还调用来Math.max(0,array.length-n) 比出最大值,目的应该是用来排除传入的n==null的情况,如果为null,则去看第三个参数是不是存在,存在按1计算,不存在按第二个参数不存在计算。 调用:

1.只有一个参数时:
 console.log( _.last([1,2,3,8]))    // [8]

2.当有两个参数时:
 console.log( _.last([1,2,3,8],2))    // [3,8]
 
3.当第二个参数为null,有第三个参数时:
console.log( _.last([1,2,3,8],null,1))  // [8]
 
console.log( _.last([1,2,3,8],null,2))  // [8]

最后一个参数存在的话全部按照1来计算


最后:
        祝各位工作顺利!
                     -小菜鸟玖月( Christine )