isEmpty方法,判断元素是否为空,直接看源码
import getTag from './.internal/getTag.js'
import isArguments from './isArguments.js'
import isArrayLike from './isArrayLike.js'
import isBuffer from './isBuffer.js'
import isPrototype from './.internal/isPrototype.js'
import isTypedArray from './isTypedArray.js'
/** Used to check objects for own properties. */
const hasOwnProperty = Object.prototype.hasOwnProperty
/**
* Checks if `value` is an empty object, collection, map, or set.
*
* Objects are considered empty if they have no own enumerable string keyed
* properties.
*
* Array-like values such as `arguments` objects, arrays, buffers, strings, or
* jQuery-like collections are considered empty if they have a `length` of `0`.
* Similarly, maps and sets are considered empty if they have a `size` of `0`.
*
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is empty, else `false`.
* @example
*
* isEmpty(null)
* // => true
*
* isEmpty(true)
* // => true
*
* isEmpty(1)
* // => true
*
* isEmpty([1, 2, 3])
* // => false
*
* isEmpty('abc')
* // => false
*
* isEmpty({ 'a': 1 })
* // => false
*/
function isEmpty(value) {
if (value == null) {
return true
}
if (isArrayLike(value) &&
(Array.isArray(value) || typeof value === 'string' || typeof value.splice === 'function' ||
isBuffer(value) || isTypedArray(value) || isArguments(value))) {
return !value.length
}
const tag = getTag(value)
if (tag == '[object Map]' || tag == '[object Set]') {
return !value.size
}
if (isPrototype(value)) {
return !Object.keys(value).length
}
for (const key in value) {
if (hasOwnProperty.call(value, key)) {
return false
}
}
return true
}
export default isEmpty
从源文件的注释可以看到,null,true,1会返回true,也就是会被判定为空,为什么1也返回true呢,直接看重要的 isEmpty方法
首先,null会返回true,没什么问题,直接从代码开头就能看到,isArrayLike(value)是什么意思呢,直接看isArrayLike的代码
isArrayLike
import isLength from './isLength.js'
/**
* Checks if `value` is array-like. A value is considered array-like if it's
* not a function and has a `value.length` that's an integer greater than or
* equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
*
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is array-like, else `false`.
* @example
*
* isArrayLike([1, 2, 3])
* // => true
*
* isArrayLike(document.body.children)
* // => true
*
* isArrayLike('abc')
* // => true
*
* isArrayLike(Function)
* // => false
*/
function isArrayLike(value) {
return value != null && typeof value !== 'function' && isLength(value.length)
}
export default isArrayLike
直接看注释中的例子就能知道大概,就是value是null,或则function,isLength(value.length)为false时,返回false,isLength函数就很简单了,这里就不放源码了,isLength(value.length)在value.length属性存在并且为正整数且小于js的最大精确整数时,为true
综上所述:数组、字符串、类数组等具有length属性的,满足isArrayLike(value)
什么是类数组呢,类数组定义:只要包含从零开始,且自然递增的整数作为健名,并且定义了length表示元素个数的对象。
回到isEmpty函数:
typeof value.splice === 'function'正常情况下只有数组才会为真,但是类数组对象添加了splice方法的话,也会判断为真,这里把具有length属性和splice属性的类数组放到之前判断,而没有走后面的对象方式,主要是快,提升效率isBuffer方法是判断元素是否是Buffer类型的数据(一般文件、图片等会用到Buffer)isTypedArray方法是判断目标元素是否是typedArray类型的数据(new Uint8Array)isArguments方法是判断元素是否是 类arguments对象
什么是类 arguments 对象,arguments是一个对应于传递给函数的参数的类数组对象,是所有函数中(箭头函数除外)都可用的局部变量
isArguments
import getTag from './.internal/getTag.js'
import isObjectLike from './isObjectLike.js'
/**
* Checks if `value` is likely an `arguments` object.
*
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`.
* @example
*
* isArguments(function() { return arguments }())
* // => true
*
* isArguments([1, 2, 3])
* // => false
*/
function isArguments(value) {
return isObjectLike(value) && getTag(value) == '[object Arguments]'
}
export default isArguments
isObjectLike函数很简单,就是使用typeOf判断元素的属性是否是object,且元素不为null,所以,数组,函数也会判定为true,getTag方法调用 Object.prototype.toString判定元素类型,可以区分数组函、数等数据结构。所以getTag(value) == '[object Arguments]就提取出 arguments 对象
所以,当元素是数组、字符串、Buffer对象、typedArray以及 类 arguments 对象时,会直接根据元素的length属性去判断是否为空,并返回结果
当元素不是上面的几种类型时,比如Map、Set类型
在js中,map和set并没有length属性,但是,相对的,具有size属性代表其长度,所以,lodash中,对map和set类型的元素,也是通过size属性去判断是否为空的
const tag = getTag(value)
if (tag == '[object Map]' || tag == '[object Set]') {
return !value.size
}
如果元素并不是上面两种情况所列举的类型呢,例如是一个原型对象
if (isPrototype(value)) {
return !Object.keys(value).length
}
可以看到,通过isPrototype去判断是否是一个原型对象,满足就通过Object.keys(value).length去判断元素是为为空
值得一提的是,这里是通过Object.keys(value).length去判断的,并没有通过for in,也就是说只判断元素自身的属性,不会去判断元素父类上的属性
最后一种情况,如果元素就是一个普通对象,并非原型对象
for (const key in value) {
if (hasOwnProperty.call(value, key)) {
return false
}
}
使用for in循环遍历得到元素的key,在通过Object.prototype.hasOwnProperty方法判断当前key是都是元素自身的属性(如果是继承来的,则会返回false),如果是元素自身的属性,则元素就不为空