DEV Community

[yun]
[yun]

Posted on

[每次突破0.1] JavaScript 如何判断属性是否存在

前言

在 JavaScript 开发中,我们常常需要判断在于一个对象中一个属性是否存在。
由于 JavaScript 是一个比较松散的语言,导致我们很难去做一些比较严格的事。就比如这个问题,“属性是否存在”在 JavaScript 里的定义出现了偏差,到底什么时候是存在,什么是算不存在。这没有对错之分,只是在不同的语境下,不同的开发需求下,“存在”的含义是不一样的。
这里我就说一说比较常用的5种判断方式,和它们存在的问题。

方法 1:布尔(Boolean)判定

这里举个例子:

const obj1 = { name: 'Andy', age: 21 }
const obj2 = { name: 'Alice' }

console.log( obj1.age ? '存在' : '不存在' ); // 存在
console.log( obj2.age ? '存在' : '不存在' ); // 不存在
Enter fullscreen mode Exit fullscreen mode

先说说这种方式的缺陷吧
如果:

const obj = { name: 'Baby', age: 0 }

console.log( obj.age ? '存在' : '不存在' ); // 不存在
Enter fullscreen mode Exit fullscreen mode

这种情况下,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 ? '存在' : '不存在' ); // 不存在
Enter fullscreen mode Exit fullscreen mode

这个对比的逻辑就是,在JS语言里,一个不存在的值就是undefined

但是在一个对象里,也可能出现的这种情况:

const obj = {
    name: 'Andy',
    age: undefined
}
Enter fullscreen mode Exit fullscreen mode

像这种情况,那么age这个属性应该是“存在”呢,还是“不存在”?

这也就不好说了,还是要看你的具体的需求环境。

方法 3: 查找Object.keys()

如果说,我就要知道一个对象里有没有一个属性age,我不管它的值是什么。

我们就可以用这个方式:

const obj = { name: 'Andy', age: 21 }

console.log( Object.keys(obj).includes('age') ? '存在' : '不存在' ); // 存在
Enter fullscreen mode Exit fullscreen mode

使用Object.keys()可以得到自身的可枚举属性名来进行判断。

这里有两个关键词,一个是“自身的”(own),一个是“可枚举”(enumerable)。

首先说,什么是“自身的”

比方说这里有一个对象,给对象加一个属性name

const obj = { name: 'Andy' }
// `name` 就是 obj 自身的属性
console.log( Object.keys(obj) ); // [ 'name' ]
Enter fullscreen mode Exit fullscreen mode

那么什么不是自身的属性呢?这里就要扯到“原型”了。一个对象是有原型,在原型上加的属性就不是“自身的”属性了。

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' ]
Enter fullscreen mode Exit fullscreen mode

这个例子,你怎么判断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 }
Enter fullscreen mode Exit fullscreen mode

你会发现,这个描述符其实也是一个对象,它描述了这个属性的一些信息,比如说值(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' ]
Enter fullscreen mode Exit fullscreen mode

这里就涉及到了属性描述符的相关知识了,这个是原生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') ? '存在' : '不存在' ); // 存在
Enter fullscreen mode Exit fullscreen mode

为什么要用Object.hasOwn(),由于对象的hasOwnProperty()有可能被修改:

const obj = {
    name: 'Andy',
    hasOwnProperty: ()=>false,
}

console.log( Object.hasOwn(obj, 'name') ? '存在' : '不存在' ); // 存在
console.log( obj.hasOwnProperty('name') ? '存在' : '不存在' ); // 不存在
Enter fullscreen mode Exit fullscreen mode

如果你要判断的属性名,不要求是自身的和可枚举,原型也行,那么就要用最后一个方法了。

方法 5: in 运算符

in 运算符用于判断一个属性是否存在于对象或其原型链中。

const obj = Object.create( { name: 'Andy' } );

console.log( 'name' in obj ? '存在' : '不存在' ); // 存在
console.log( Object.hasOwn(obj, 'name') ? '存在' : '不存在' ); // 不存在
Enter fullscreen mode Exit fullscreen mode

总结

这些方法种,没有说那个方法是正确的,那个错误的。并不是每个场景都适合同一种方法,根据情况遵守最佳实践,以增强代码可读性和安全性。

Top comments (0)