lodash源码分析之isEmpty
本文为读 lodash 源码的第二百一十二篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
依赖
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'
《lodash源码分析之数据类型获取的兼容性》 《lodash源码分析之isArguments》 《lodash源码分析之isArrayLike》 《lodash源码分析之isBuffer》 《lodash源码分析之isPrototype》 《lodash源码分析之isTypedArray》
源码分析
isEmpty 用来判断 value 是否为空值。
源码如下:
const hasOwnProperty = Object.prototype.hasOwnProperty
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
}
null 及 undefined
if (value == null) {
return true
}
如果 null 是 undefined ,则返回 true。
类数组对象
类数组对象有很多,如果是类数组对象,则通过 length 来判断集合是否为空。
其中 Array.isArray 用来判断是否为数组;
typeof value === 'string' 用来判断是否为字符串;
typeof value.splice === 'function' 用来判断是否为 jquery-like 对象,jquery-like 对象会有这个 splice 方法;
isBuffer 用来判断是否为 Buffer ;
isTypedArray 用来判断是否为 TypedArray ;
isArguments 用来判断是否为 arguments 对象;
对于以上类型,都用 length 属性来判断是否为空。
Map及Set
const tag = getTag(value)
if (tag == '[object Map]' || tag == '[object Set]') {
return !value.size
}
如果是 Map 及 Set ,则用 size 来判断是否为空。
原型对象
if (isPrototype(value)) {
return !Object.keys(value).length
}
如果为原型对象,则判断自身可枚举属性是否为空。
一般性对象及其它类型
for (const key in value) {
if (hasOwnProperty.call(value, key)) {
return false
}
}
for...in 会遍历自身及原型链上的可枚举属性。
这里配合 Object.prototype.hasOwnProperty 使用表示只遍历自身可枚举属性。
咋一看,这不是和 Object.keys 的作用一样吗,为什么不直接和原型一样,使用 Object.keys 来判断呢,而且 Object.keys 的性能更好。
其实是因为 Object.keys 会对原始值抛出一个 TypeError 的错误,但是 for...in 不会抛出错误。这里使用 for...in 是为了避免错误的发生。
看 MDN 上 Object.keys 的 polyfill 就很清楚两者的区别。
if (!Object.keys) {
Object.keys = (function () {
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function (obj) {
if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
var result = [];
for (var prop in obj) {
if (hasOwnProperty.call(obj, prop)) result.push(prop);
}
if (hasDontEnumBug) {
for (var i=0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
}
return result;
}
})()
};
这个 polyfill 会有一些兼容性的代码。
参考资料
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:
作者:对角另一面