前言
在 JavaScript 开发中,我们常常需要判断在于一个对象中一个属性是否存在。
由于 JavaScript 是一个比较松散的语言,导致我们很难去做一些比较严格的事。就比如这个问题,“属性是否存在”在 JavaScript 里的定义出现了偏差,到底什么时候是存在,什么是算不存在。这没有对错之分,只是在不同的语境下,不同的开发需求下,“存在”的含义是不一样的。
这里我就说一说比较常用的5种判断方式,和它们存在的问题。
方法 1:布尔(Boolean)判定
这里举个例子:
const obj1 = { name: 'Andy', age: 21 }
const obj2 = { name: 'Alice' }
console.log( obj1.age ? '存在' : '不存在' ); // 存在
console.log( obj2.age ? '存在' : '不存在' ); // 不存在
先说说这种方式的缺陷吧
如果:
const obj = { name: 'Baby', age: 0 }
console.log( obj.age ? '存在' : '不存在' ); // 不存在
这种情况下,age
应是 “存在”,但是会被判断成“不存在”。
那么这种方式是不是就不能用呢?
这就要根据业务的开发需求了。
如果你知道在你的业务开发需求里,这个属性是不可能为0
,空字符串啊,NaN
啊,undefined
之类的,那么这个判定还是可行的。
如果你放在更加严格的环境,那么这个方式就有缺陷的。
方法 2: 可选链 (?.)
和 undefined
检查
还是先举例:
const obj1 = { name: 'Andy', age: 21 }
const obj2 = { name: 'Alice' }
console.log( obj1?.age !== undefined ? '存在' : '不存在' ); // 存在
console.log( obj2?.age !== undefined ? '存在' : '不存在' ); // 不存在
这个对比的逻辑就是,在JS语言里,一个不存在的值就是undefined
。
但是在一个对象里,也可能出现的这种情况:
const obj = {
name: 'Andy',
age: undefined
}
像这种情况,那么age
这个属性应该是“存在”呢,还是“不存在”?
这也就不好说了,还是要看你的具体的需求环境。
方法 3: 查找Object.keys()
如果说,我就要知道一个对象里有没有一个属性age
,我不管它的值是什么。
我们就可以用这个方式:
const obj = { name: 'Andy', age: 21 }
console.log( Object.keys(obj).includes('age') ? '存在' : '不存在' ); // 存在
使用Object.keys()
可以得到自身的可枚举属性名来进行判断。
这里有两个关键词,一个是“自身的”(own),一个是“可枚举”(enumerable)。
首先说,什么是“自身的”
比方说这里有一个对象,给对象加一个属性name
:
const obj = { name: 'Andy' }
// `name` 就是 obj 自身的属性
console.log( Object.keys(obj) ); // [ 'name' ]
那么什么不是自身的属性呢?这里就要扯到“原型”了。一个对象是有原型,在原型上加的属性就不是“自身的”属性了。
const obj = Object.create( { type: 'human' } );
obj.name = 'Andy';
console.log( obj.name ); // Andy
console.log( obj.type ); // human
console.log( Object.keys(obj) ); // [ 'name' ]
这个例子,你怎么判断type
是不是obj
的属性呢?这就不好说了对吧,你可以说是,也可以说不是。这就是语言宽松环境带来的问题。
但你需要知道的是,在这种情况下,Object.keys()
是拿不到原型上的属性。因为它只能读取“自身的”属性。
第二个是,“可枚举”
这里就要提到属性描述符了,我很多同事都不知道,在JS语言里,在一个对象里,每个属性都有一个描述符的。
怎么读取呢?我们打印看吧:
const obj = { name: 'Andy', age: 21 };
const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
console.log( descriptor );
// { value: 'Andy', writable: true, enumerable: true, configurable: true }
你会发现,这个描述符其实也是一个对象,它描述了这个属性的一些信息,比如说值(value),可重写(writable),还有这个(enumerable
)可枚举 ...
这个enumerable
定义了那个属性,是否可以被枚举,比如for...in
循环时候可以读取这个属性,Object.keys()
也是如此。
如果我重新定义一个属性为不可枚举的,那么Object.keys()
就读取不到这个属性了。
const obj = { name: 'Andy', age: 21} ;
Object.defineProperty(obj, 'age', {
enumerable: false
});
console.log( obj.age ); // 21
console.log( Object.keys(obj) ); // [ 'name' ]
这里就涉及到了属性描述符
的相关知识了,这个是原生JS语言非常重要的知识,而我发现很多我认识的前端程序员都缺乏这种原生JS的知识。
那么如果说你要判断的属性要求只要是自身的属性,不管可不可枚举呢?那么就不能用Object.keys()
了。可以用接下来的方法。
方法 4:使用 Object.hasOwn()
或 hasOwnProperty()
Object.hasOwn()
和 hasOwnProperty()
有什么区别呢?
Object.hasOwn()
是 ES2022 加入的方法,用于检查对象是否自身具有某个属性,用于替代 hasOwnProperty
的促优处理。由于这是后来加入的方法,兼容性还是个问题,如果只考虑新版本的浏览器还是可以用的。
这两个方法判断的是 “自身的” 属性名,所以不管是不是可枚举,都能被读取。
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'Andy',
enumerable: false // 不可被枚举
});
console.log( Object.hasOwn(obj, 'name') ? '存在' : '不存在' ); // 存在
console.log( obj.hasOwnProperty('name') ? '存在' : '不存在' ); // 存在
为什么要用Object.hasOwn()
,由于对象的hasOwnProperty()
有可能被修改:
const obj = {
name: 'Andy',
hasOwnProperty: ()=>false,
}
console.log( Object.hasOwn(obj, 'name') ? '存在' : '不存在' ); // 存在
console.log( obj.hasOwnProperty('name') ? '存在' : '不存在' ); // 不存在
如果你要判断的属性名,不要求是自身的和可枚举,原型也行,那么就要用最后一个方法了。
方法 5: in
运算符
in
运算符用于判断一个属性是否存在于对象或其原型链中。
const obj = Object.create( { name: 'Andy' } );
console.log( 'name' in obj ? '存在' : '不存在' ); // 存在
console.log( Object.hasOwn(obj, 'name') ? '存在' : '不存在' ); // 不存在
总结
这些方法种,没有说那个方法是正确的,那个错误的。并不是每个场景都适合同一种方法,根据情况遵守最佳实践,以增强代码可读性和安全性。
Top comments (0)