Dogtiti / Blogs

:sparkling_heart:一起记笔记呀~
8 stars 6 forks source link

Lodash 源码中的细节 #31

Open Dogtiti opened 5 years ago

Dogtiti commented 5 years ago

1. length = start > end ? 0 : ((end - start) >>> 0)

在 Lodash 的代码中,会经常看到这样的代码片段,需要根据传入的 Array 的初始和结束索引求得要操作的数据元素的长度。

那 >>> 0 有什么用处?

在 JS 中,Array.length 是一个 32 位无符号整型数字,而通过无符号位移运算 >>> 0,就是为了确保我们得到的正确的 length 值,它总是能得到一个 32-bit unsigned ints ... 比如:

-1 >>> 0    // 4294967295

0 >>> 0     // 0

'1' >>> 0   // 1

'1x' >>> 0  // 0

null >>> 0  // 0

而且,对于异常情况的包容也可以让我们减少一些类型判断和显示的强制类型转化的操作。

因此,在有用到 Array.length 的场景,可用 >>> 0 做参数防护。

2. arr instanceof Array

我们会用 instanceof 去判断实例与类之间的关系,但是这种关系可靠吗?

arr instanceof Array 为 true 肯定 arr 就是一个数组,但是为 false 是不是就表示 arr 肯定不是数组呢?

不能!在有多个 frame 的时候就不能...

let xArray = window.frames[0].Array; // origin from iframe's window
let arr = new xArray(1, 2, 3);

arr instanceof Array; // false

Array.isArray(arr); // true

虽然我们平时不常会有多个 frame 间这种级别的类的混用,但是以防万一

通常,在需要判断是否是数组时,可以使用 Array.isArray 或者它的 Polyfill:

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
} 

3. _.eq(value, other)

判断 value 和 other 是否(强)相等,在 ECMA 规范中对于相等其实是有明确的定义的,其中 NaN === NaN,但是在 JS 中,NaN 是不与任何值相等的,包括自身,而 Lodash 弥补了这部分的不足,那怎么判断 NaN === NaN 呢?

function eq(value, other) {
  return value === other || (value !== value && other !== other)
}

我们知道 NaN 是唯一个不与自身相等的值,因此可以通过这个特性分别得到 value 和 other 是否是 NaN,如果是,则两者相等,返回 true.

4. _.isNaN

如何判断是否是 NaN?

通过 isNaN 全局方法?

isNaN('x')       // true

isNaN(NaN)       // true

isNaN(0)         // false

全局方法 isNaN 是有坑的,它的 NaN 定义估计真的是 not a number, 而不是值得 NaN 这个s数值,也就是除了 NaN, 对于 不能转化为数字 的情况都会返回 true ... 其实这个问题已经在 ES6 中被改善啦,Number.isNaN 只会对 NaN 返回 true, 其他情况都是 false...

5. (-0).toString() === '0'?

-0 的 toString 竟然没有保留 -

但是,_.toString(-0) === '-0' ,如何做的?

// 一系列的前置检查和转化后...

const INFINITY = 1 / 0; // 手动 INFINITY
const res = `${value}`;

if (res === '0' && 1 / value === -INFINITY) {
  return '-0';
} else {
  return res;
}

6. JS 的世界里,谁不能转化为数字?

数字、数字字符串肯定是可以的...

对象?或者非数字的字符串?它们也能得到 NaN

Symbol !

Number('xx')          // NaN

Number(new Object())  // NaN

Number(Symbol())
// --> Uncaught TypeError: Cannot convert a Symbol value to a number

另外,Symbol 可以显示的转化为 String 和 Boolean, 但是,不能进行任何隐式转换,也就是不能对 Sybmol 进行运算,比如:

const symbol = Symbol();

1 + symbol       // TypeError

'1' + symbol     // TypeError

// 可显示转化为 布尔 和 字符串
Boolean(symbol)  // true

String(symbol)   // "Symbol()"

7. _.repeat 的优化手段

_.repeat([str = ''], [times = 1])
指定 string 重复 n 次输出,可以很简单的通过循环实现:

const repeat = (str = '', times = 1) => {
  let res = str;

  while (--times) {
    res += str;
  }

  return res;
}

repeat('6', 3); // 666

设想 repeat('6', 4) 按照上述实现需要循环 4 次,但是其实是可以通过两次操作就把最终结果拼接出来的,就相当于 repeat(repeat('6', 2), 2) ,所以这块是有优化空间的,来看优化后的算法:

const repeat = (str = '', times = 1) => {
    let result = '';

    if (!str || times < 1) {
        return result;
    }

    do {
        if (times % 2) {
            result += str;
        }

        times = Math.floor(times / 2);

        if (times) {
            str += str;
        }
    } while (times)

    return result;
}