每天一个Lodash源码解析

2023-05-16

每天一个Lodash源码解析

  • chunk()
    • 方法介绍
    • 自我实现
    • 源码分析
    • 代码对比
    • 知识点补充
      • 浮点数转化为浮点数
      • 数组创建方法区别
      • js中切割数组方法
  • slice()
    • 方法介绍
    • 自我实现
    • 源码分析
    • 代码对比
    • 知识点补充
      • ``>>>``移位运算
  • compact()
    • 方法介绍
    • 自我实现
    • 源码分析
    • 代码对比

chunk()

方法介绍

_.chunk(array, [size=1])

将数组(array)拆分成多个 size 长度的区块,并将这些区块组成一个新数组。 如果array 无法被分割成全部等长的区块,那么最后剩余的元素将组成一个区块。

参数

  • array (Array): 需要处理的数组
  • [size=1] (number): 每个数组区块的长度

返回

  • (Array): 返回一个包含拆分区块的新数组(注:相当于一个二维数组)。

例子

_.chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]
 
_.chunk(['a', 'b', 'c', 'd'], 3);
// => [['a', 'b', 'c'], ['d']]

自我实现

var chunk = function(arr, size) {
    //初始化长度还有size的值
    var l = arr.length
    if (size == undefined) size = 1
    //定义新数组和截取的始尾
    var newA = []
    var indexf = 0
    var indexe = size
    //处理特殊情况
    if (size == 0) {
        newA = []
        return newA;
    }
    //循环截取数组
    while (l > size) {
        var a = arr.slice(indexf, indexe)
        l = l - size
        indexf = indexf + size
        indexe = indexe + size
        newA.push(a)
    }
    //未被截取到的情况
    if (l > 0) {
        indexf = indexe - size
        indexe = arr.length
        var a = arr.slice(indexf, indexe)
        newA.push(a)
    }
    return newA;
}

源码分析

function chunk(array, size = 1) {//将size默认值设为1
	//判断size的值是否小于0,如果小于0,则size值取0
    size = Math.max(toInteger(size), 0)
    //判断数组的值是否为null,如果为null,数组长度为0
    const length = array == null ? 0 : array.length
    //如果数组长度为0或者size<1,返回空数组
    if (!length || size < 1) {
        return []
    }
    let index = 0//截取数组坐标
    let resIndex = 0//结果数组坐标
    //用数组长度除以size并向上取整,得到分块个数
    //新建一个长度为分块个数的新数组
    const result = new Array(Math.ceil(length / size))
	
	//循环截取数组
    while (index < length) {
        result[resIndex++] = slice(array, index, (index += size))
    }
    return result
}

代码对比

  • 源码相对来说,减少了很多声明,并少了很多冗余的代码,且具有很好的可读性
  • 对于数组接收到的值有很好的判断并处理

知识点补充

浮点数转化为浮点数

使用Math中的方法

  • Math.floor():保留整数
  • Math.ceil():向上取整
  • Math.round():四舍五入

使用|(位或运算符)将浮点数截断为整数。

  console.log(23.9 | 0); // Result: 23;n为正,向下舍入
  console.log(-23.9 | 0); //Result: -23;n为负,向上舍入

数组创建方法区别

使用new Array创建的数组,具有length属性,没有每个元素。

使用Array.from创建的数组,具有length属性,每个元素为undefined,因此后续可以使用filter或者map等方法。

array.from还可以转为类似数组的对象,...扩展运算符不能。

//方法一 new Array
console.log(new Array(2))
//方法二  Array.from
console.log(Array.from({
   length: 2
}))
//方法三 
let arr = [1, 2]
console.log(arr)
//方法四
console.log([...arr])

运行结果
在这里插入图片描述

js中切割数组方法

slice()
返回一个索引和另一个索引之间的数据(不改变原数组),
slice(start,end)有两个参数(start必需,end选填),都是索引,返回值不包括end

splice()
用来添加或者删除数组的数据,只返回被删除的数据,类型为数组(改变原数组)

slice()

方法介绍

_.slice(array, [start=0], [end=array.length])

裁剪数组array,从 start 位置开始到end结束,但不包括 end 本身的位置。

Note: 这个方法用于代替Array#slice 来确保数组正确返回。

参数

  • array (Array): 要裁剪数组。
  • [start=0] (number): 开始位置。
  • [end=array.length] (number): 结束位置。

返回

  • (Array): 返回 数组array 裁剪部分的新数组。

PS:

  • start 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。
  • end 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。

自我实现

var slice = function(array, start, end) {
	//定义数组
    let a = []
	//循环获取值
    for (var i = start; i < end; i++) {
        console.log(i)
        a.push(array[i])
    }
	return a
}

源码分析

function slice(array, start, end) {
	 //判断数组是否为null,如果为null,length的值为0,否则为数组长度
     let length = array == null ? 0 : array.length
     //如果length为0,则返回空数组
     if (!length) {
         return []
     }
     //判断start和end的值是否为空
     start = start == null ? 0 : start
     end = end === undefined ? length : end
	 //如果start小于0,判断-start是否大于length
	 //如果-start大于length,则取0,否则取length+start
     if (start < 0) {
         start = -start > length ? 0 : (length + start)
     }
     //判断end是否大于length,如果是取length,否则取end
     end = end > length ? length : end
     //如果end小于0,end加等于length
     if (end < 0) {
         end += length
     }
     //设置返回新数组的长度
     //如果start大于end,则取0,否则取end-start的结果,并确保其结果为正确的正整数
     length = start > end ? 0 : ((end - start) >>> 0)
     start >>>= 0
	 //循环取值
     let index = -1
     const result = new Array(length)
     while (++index < length) {
         result[index] = array[index + start]
     }
     return result
 }

代码对比

  • 相较于自我实现,对于传递过来的参数的的错误判断更为具体与正确
  • 循环变量采用代码直接编写,没有调用方法,加快速度

知识点补充

>>>移位运算

x >>> 0本质上就是保证x有意义(为数字类型),且为正整数,在有效的数组范围内(0 ~ 0xFFFFFFFF),且在无意义的情况下缺省值为0。

具体参考:https://segmentfault.com/a/1190000014613703

compact()

方法介绍

创建一个新数组,包含原数组中所有的非假值元素。例如false, null,0," ", undefined, 和 NaN都是被认为是“假值”。

参数

  • array(Array): 待处理的数组

返回

  • (Array): 返回过滤掉假值的新数组。

自我实现

var compact = function(array) {
	//判断数组是否为空
    if (array.length == 0) return []

    var newaArray = []
	
	//循环遍历判断值
    for (let i = 0; i < array.length; i++) {
    	//获取数组中值的类型
        var type = Object.prototype.toString.call(array[i])
        if (type == '[object Null]' || type == '[object Undefined]') {//如果为空Null或者Undefined就跳过
            continue;
        } else if (type == '[object Number]') {//如果为0或者NaN就跳过
            if (array[i] == 0 || isNaN(array[i])) continue;
            else newaArray.push(array[i])
        } else if (type == '[object Boolean]') {//如果为false就跳过
            if (array[i] == false) continue;
            else newaArray.push(array[i])
        } else if (type == '[object String]') {//如果为""就跳过
            if (array[i] == "") continue;
            else newaArray.push(array[i])
        } else {
            newaArray.push(array[i])
        }
    }
    return newaArray
}

源码分析

function compact(array) {
  	let resIndex = 0
  	const result = []
	//如果array为null,则返回空数组
  	if (array == null) {
  	  	return result
  	}
	遍历array数组,将数组中的每个元素转化为布尔值后判断是否为ture,false为虚假值,需要过滤
  	for (const value of array) {
    	if (value) {
      	result[resIndex++] = value
    	}
  	}
  	return result
}

代码对比

  • 相较于自我实现,源码的代码简洁明了,通过转化为布尔类型省略了很多判断
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

每天一个Lodash源码解析 的相关文章

随机推荐