[每次突破] 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 ? '存在' : '不存在' ); // 不存在
先说说这种方式的缺陷吧
如果:
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 obj1 = { name: 'Andy', age: 21 } const obj2 = { name: 'Alice' } console.log( obj1.age ? '存在' : '不存在' ); // 存在 console.log( obj2.age ? '存在' : '不存在' ); // 不存在
你会发现,这个描述符其实也是一个对象,它描述了这个属性的一些信息,比如说值(value),可重写(writable),还有这个(enumerable)可枚举 ...
这个enumerable定义了那个属性,是否可以被枚举,比如for...in循环时候可以读取这个属性,Object.keys()也是如此。
如果我重新定义一个属性为不可枚举的,那么Object.keys()就读取不到这个属性了。
const obj = { name: 'Baby', age: 0 } console.log( obj.age ? '存在' : '不存在' ); // 不存在
这里就涉及到了属性描述符的相关知识了,这个是原生JS语言非常重要的知识,而我发现很多我认识的前端程序员都缺乏这种原生JS的知识。
那么如果说你要判断的属性要求只要是自身的属性,不管可不可枚举呢?那么就不能用Object.keys()了。可以用接下来的方法。
方法 4:使用 Object.hasOwn() 或 hasOwnProperty()
Object.hasOwn() 和 hasOwnProperty() 有什么区别呢?
Object.hasOwn() 是 ES2022 加入的方法,用于检查对象是否自身具有某个属性,用于替代 hasOwnProperty 的促优处理。由于这是后来加入的方法,兼容性还是个问题,如果只考虑新版本的浏览器还是可以用的。
这两个方法判断的是 “自身的” 属性名,所以不管是不是可枚举,都能被读取。
const obj1 = { name: 'Andy', age: 21 } const obj2 = { name: 'Alice' } console.log( obj1?.age !== undefined ? '存在' : '不存在' ); // 存在 console.log( obj2?.age !== undefined ? '存在' : '不存在' ); // 不存在
为什么要用Object.hasOwn(),由于对象的hasOwnProperty()有可能被修改:
const obj = { name: 'Andy', age: undefined }
如果你要判断的属性名,不要求是自身的和可枚举,原型也行,那么就要用最后一个方法了。
方法 5: in 运算符
in 运算符用于判断一个属性是否存在于对象或其原型链中。
const obj = { name: 'Andy', age: 21 } console.log( Object.keys(obj).includes('age') ? '存在' : '不存在' ); // 存在
总结
这些方法种,没有说那个方法是正确的,那个错误的。并不是每个场景都适合同一种方法,根据情况遵守最佳实践,以增强代码可读性和安全性。
以上是[每次突破] JavaScript 如何判断属性是否存在的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undress AI Tool
免费脱衣服图片

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

PlacingtagsatthebottomofablogpostorwebpageservespracticalpurposesforSEO,userexperience,anddesign.1.IthelpswithSEObyallowingsearchenginestoaccesskeyword-relevanttagswithoutclutteringthemaincontent.2.Itimprovesuserexperiencebykeepingthefocusonthearticl

JavaScript中的日期和时间处理需注意以下几点:1.创建Date对象有多种方式,推荐使用ISO格式字符串以保证兼容性;2.获取和设置时间信息可用get和set方法,注意月份从0开始;3.手动格式化日期需拼接字符串,也可使用第三方库;4.处理时区问题建议使用支持时区的库,如Luxon。掌握这些要点能有效避免常见错误。

事件捕获和冒泡是DOM中事件传播的两个阶段,捕获是从顶层向下到目标元素,冒泡是从目标元素向上传播到顶层。1.事件捕获通过addEventListener的useCapture参数设为true实现;2.事件冒泡是默认行为,useCapture设为false或省略;3.可使用event.stopPropagation()阻止事件传播;4.冒泡支持事件委托,提高动态内容处理效率;5.捕获可用于提前拦截事件,如日志记录或错误处理。了解这两个阶段有助于精确控制JavaScript响应用户操作的时机和方式。

JavaScript的垃圾回收机制通过标记-清除算法自动管理内存,以减少内存泄漏风险。引擎从根对象出发遍历并标记活跃对象,未被标记的则被视为垃圾并被清除。例如,当对象不再被引用(如将变量设为null),它将在下一轮回收中被释放。常见的内存泄漏原因包括:①未清除的定时器或事件监听器;②闭包中对外部变量的引用;③全局变量持续持有大量数据。V8引擎通过分代回收、增量标记、并行/并发回收等策略优化回收效率,降低主线程阻塞时间。开发时应避免不必要的全局引用、及时解除对象关联,以提升性能与稳定性。

ES模块和CommonJS的主要区别在于加载方式和使用场景。1.CommonJS是同步加载,适用于Node.js服务器端环境;2.ES模块是异步加载,适用于浏览器等网络环境;3.语法上,ES模块使用import/export,且必须位于顶层作用域,而CommonJS使用require/module.exports,可在运行时动态调用;4.CommonJS广泛用于旧版Node.js及依赖它的库如Express,ES模块则适用于现代前端框架和Node.jsv14 ;5.虽然可混合使用,但容易引发问题

在Node.js中发起HTTP请求有三种常用方式:使用内置模块、axios和node-fetch。1.使用内置的http/https模块无需依赖,适合基础场景,但需手动处理数据拼接和错误监听,例如用https.get()获取数据或通过.write()发送POST请求;2.axios是基于Promise的第三方库,语法简洁且功能强大,支持async/await、自动JSON转换、拦截器等,推荐用于简化异步请求操作;3.node-fetch提供类似浏览器fetch的风格,基于Promise且语法简单

var、let和const的区别在于作用域、提升和重复声明。1.var是函数作用域,存在变量提升,允许重复声明;2.let是块级作用域,存在暂时性死区,不允许重复声明;3.const也是块级作用域,必须立即赋值,不可重新赋值,但可修改引用类型的内部值。优先使用const,需改变变量时用let,避免使用var。

操作DOM变慢的主要原因在于重排重绘成本高和访问效率低。优化方法包括:1.减少访问次数,缓存读取值;2.批量处理读写操作;3.合并修改,使用文档片段或隐藏元素;4.避免布局抖动,集中处理读写;5.使用框架或requestAnimationFrame异步更新。
