所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

JavaScript中的对象操作详解:超实用增删改查与遍历技巧全解析

JavaScript中的对象操作详解:超实用增删改查与遍历技巧全解析 一

文章目录CloseOpen

作为JavaScript的“核心数据结构”,对象操作是每个开发者绕不开的基础——但基础不代表简单。很多人看似会用,实则没搞懂背后的规则:比如“属性的 enumerable 标志”会影响遍历结果,“引用类型赋值”会导致改一个对象牵一发而动全身,甚至连“如何快速克隆对象”都有很多隐藏的坑。

这篇文章就把对象操作的“底层逻辑+实用技巧”揉成了干货:从增删改查的最简写法(比如用Object.assign批量加属性、用delete的正确姿势),到遍历方法的选择指南(for…in要过滤原型链、Object.keys适合拿键名、Object.entries直接拆键值对),再到新手必避的5个陷阱(比如混淆对象引用、忽略属性的可配置性),全给你讲透。

不管你是刚入门的新手,还是想补牢基础的开发者,读完这篇都能“精准拿捏”对象操作——不用再靠“试错”写代码,每一步都有明确的逻辑支撑。

你有没有过写JavaScript时,想给对象加个属性,结果要么报错要么没效果?或者遍历对象时,突然冒出一堆原型链上的奇怪属性?我之前帮朋友改他的电商项目代码时,就遇到过这种情况——他用for...in遍历商品对象,结果把Object.prototype上的toString方法也遍历出来了,导致购物车计算逻辑全乱了。后来我帮他换成Object.keysforEach,才解决问题。其实对象操作看着简单,里面的小细节特别多,今天就把我踩过的坑、 的技巧全告诉你,不管是增删改查还是遍历,看完就能直接用。

对象增删改查:别再用“试错法”写代码了

我之前带过一个实习生,他写对象操作的代码全靠“试”——加属性先用点语法,报错了再换方括号;删属性直接用delete,不管能不能删掉。结果他写的用户信息修改功能,经常出现“属性改了但没保存”“删除的属性还在”的问题。后来我告诉他,对象的增删改查不是“碰运气”,每个操作都有明确的规则,只要搞懂这些规则,就能避免90%的错误。

添加属性:点语法和方括号到底怎么选?

很多人刚开始写对象的时候,都会纠结“obj.a”和“obj['a']”有什么区别——不都是加属性吗?其实区别大了。点语法要求属性名是“合法标识符”,也就是不能用数字开头、不能有空格或特殊字符(比如横杠、感叹号)。比如你想加一个“user-name”的属性,用obj.user-name肯定报错(因为减号会被当成运算符),但用obj['user-name']就没问题。我之前做一个表单提交功能时,用户输入的字段名里有“phone-number”“email-address”这种带横杠的,就是用方括号解决的。

还有一种情况是用变量当属性名,比如你有一个变量key='age',想给对象加这个属性,用obj.key会变成加一个叫“key”的属性,而不是“age”,这时候必须用obj[key]。比如我做的动态表单项目里,根据用户选择的字段类型,动态添加属性,就是用变量加方括号实现的:const field = 'gender'; obj[field] = 'male'

批量添加属性的时候,别一个个写“obj.a=1;obj.b=2”,太麻烦了——用Object.assign就好。比如你要合并用户的基础信息和扩展信息,直接写Object.assign(user, basicInfo, extraInfo),就能把basicInfoextraInfo的属性全加到user对象里。我平时做用户中心功能时,经常用这个方法合并从接口拿到的多份数据,比循环快多了,而且兼容性也不错(除了IE8以下,现在基本不用考虑了)。

删除属性:delete不是“万能删”,这些情况要注意

你是不是觉得“delete obj.prop”就能删掉属性?其实不是——delete只能删对象的自有属性(也就是直接定义在对象上的属性,不是原型链上的),而且如果属性是不可配置的(configurable: false),delete会返回false,删不掉。我之前遇到过一个情况:用Object.defineProperty给对象加了个“id”属性,设置了configurable: false,结果后来想删的时候,delete obj.id返回false,属性还在。后来查MDN才知道,不可配置的属性是删不掉的(除非用Object.defineProperty修改configurable,但那又绕回去了)。

还有一种常见的错误:用“obj.prop = undefined”代替delete。比如你想“删掉”user.age,写user.age=undefined,结果遍历的时候,age属性还在,只是值是undefined。我之前帮同事查bug时,就遇到过这种情况——他用Object.keys遍历用户对象,结果拿到了age属性,导致统计用户年龄分布时,把undefined也算进去了,结果报表全错了。后来改成delete user.age,才解决问题。记住:undefined是值,不是删除属性;delete才是真的删除属性。

那什么时候用delete?只要你想彻底移除对象的某个自有属性,就用delete。比如用户注销时,要删掉他的敏感信息(比如密码、身份证号),就用delete user.password; delete user.idNumber,这样这些属性就彻底从对象里消失了,遍历的时候也不会出现。

修改属性:别光改值,还要懂“属性描述符”

很多人修改属性的时候,只知道改值(比如obj.a=1改成obj.a=2),但其实属性还有三个重要的“描述符”:writable(能否修改值)、enumerable(能否被遍历到)、configurable(能否删除或修改描述符)。我之前做库存管理系统时,商品的SKU属性就是用Object.defineProperty设置的:Object.defineProperty(goods, 'sku', { value: '123456', writable: false, enumerable: false })。这样设置后,sku的值不能改(writable: false),也不能被遍历到(enumerable: false),完美保护了商品的唯一标识——毕竟SKU一旦生成,就不能改,也不需要被遍历到。

再比如,你想让对象的某个属性“只能读不能写”,就把writable设为false。比如const config = {}; Object.defineProperty(config, 'apiUrl', { value: 'https://api.example.com', writable: false });这样再改config.apiUrl='https://test.example.com'就没用了(严格模式下会报错)。我之前做的接口请求工具里,配置项就是这么设置的,防止误修改导致请求地址错误。

还有enumerable,如果你不想让某个属性被遍历到,比如对象的内部状态,就把enumerable设为false。比如const cache = { data: [1,2,3], _expire: 1699999999 }; Object.defineProperty(cache, '_expire', { enumerable: false });这样用Object.keys(cache)只能拿到['data'],不会拿到_expire,避免遍历的时候处理无关的属性。

对象遍历:选对方法,比“瞎试”高效10倍

我之前帮朋友改他的电商项目代码时,最头疼的就是遍历对象——他用for...in遍历商品对象,结果把Object.prototype上的toStringvalueOf方法都遍历出来了,导致购物车计算逻辑全乱了。后来我告诉他,遍历对象的关键不是“怎么遍历”,而是“选对遍历方法”,不同的方法对应不同的场景,选对了就能避免90%的坑。

for…in:别再直接用了,先过滤原型链

for...in是最“古老”的遍历方法,它会遍历对象所有可枚举属性,包括原型链上的。比如你给Object.prototype加了个方法:Object.prototype.myMethod = function() {},然后用for...in遍历任何对象,都会拿到myMethod这个属性。我之前遇到过一个极端情况:一个第三方库修改了Object.prototype,导致我所有用for...in的遍历都出问题了,后来花了半天时间才找到原因。

for...in就不能用了吗?也不是——只要加个hasOwnProperty判断就行。比如:for (let key in obj) { if (obj.hasOwnProperty(key)) { // 处理逻辑 } }。这样就能过滤掉原型链上的属性,只遍历自有属性。但我还是不推荐常用for...in,因为写起来麻烦,而且性能不如后面的方法。

Object.keys/values/entries:最常用的“安全遍历”方法

如果你想“安全遍历”对象的自有可枚举属性,优先用Object.keysObject.valuesObject.entries——这三个方法都是ES5之后加的,兼容性很好(IE9及以上支持),而且不会遍历原型链。

  • Object.keys(obj):返回对象自有可枚举属性的键名数组,比如obj={a:1,b:2}Object.keys(obj)就是['a','b']。我平时用它来遍历键名,比如:Object.keys(user).forEach(key => { console.log(用户的${key}是${user[key]}) });
  • Object.values(obj):返回对象自有可枚举属性的值数组,比如obj={a:1,b:2}Object.values(obj)就是[1,2]。我做统计的时候常用它,比如计算商品的总价格:const total = Object.values(goods).reduce((sum, price) => sum + price, 0);
  • Object.entries(obj):返回对象自有可枚举属性的键值对数组,比如obj={a:1,b:2}Object.entries(obj)就是[['a',1],['b',2]]。这个方法特别适合需要同时拿键和值的场景,比如转URL参数:const params = Object.entries(searchObj).map(([key, value]) => ${key}=${encodeURIComponent(value)}).join('&');我之前做搜索功能时,就是用这个方法把搜索条件对象转成URL参数的,比手动拼接方便多了。
  • 我平时写代码的优先级是:需要键值对用Object.entries,只需要值用Object.values,需要键名用Object.keysforEach——这三个方法覆盖了90%的遍历场景,而且从来没踩过坑。

    Object.getOwnPropertyNames:连不可枚举属性都能拿到

    有时候你需要遍历对象的所有自有属性,包括不可枚举的,这时候就用Object.getOwnPropertyNames(obj)。比如obj={a:1}Object.defineProperty(obj, 'b', { value:2, enumerable:false })Object.getOwnPropertyNames(obj)会返回['a','b']——不管b是不是可枚举的。我之前调试一个复杂对象时,用这个方法拿到了所有属性,包括那些“隐藏”的不可枚举属性,帮我快速找到了问题所在(比如一个不可枚举的属性被错误地设置了值)。

    不过要注意,Object.getOwnPropertyNames不包括Symbol属性(比如Symbol('id')),如果要遍历Symbol属性,得用Object.getOwnPropertySymbols(obj)。比如const id = Symbol('id'); const obj = { [id]: 123 }; Object.getOwnPropertySymbols(obj)会返回[id]。这个方法用得比较少,但有时候处理Symbol属性时特别有用。

    为了让你更清楚不同遍历方法的区别,我整理了一个表格:

    方法 遍历范围 是否包含不可枚举属性 是否过滤原型链 适用场景
    for…in 自有+原型链可枚举属性 否(需手动过滤) 需遍历所有可枚举属性(少用)
    Object.keys 自有可枚举属性(键名) 需要键名的安全遍历
    Object.values 自有可枚举属性(值) 只需要值的统计场景
    Object.entries 自有可枚举属性(键值对) 需要键值对的场景(常用)
    Object.getOwnPropertyNames 所有自有属性(键名) 需要不可枚举属性的调试场景

    我想跟你说:对象操作是JavaScript的基础,但基础不代表简单。我见过很多开发者写了好几年代码,还在对象操作上踩坑——比如用错遍历方法、删不掉属性、加属性报错。其实只要搞懂每个操作的规则,再加上一些实用技巧,就能轻松搞定对象操作。比如我现在写对象代码,不管是增删改查还是遍历,都能快速写出正确的代码,而且很少调试——因为我把这些技巧变成了“肌肉记忆”。

    你平时写对象操作的时候,有没有遇到过什么奇怪的问题?比如删不掉的属性、遍历出奇怪的东西?欢迎在评论区告诉我,我帮你分析分析——毕竟踩过的坑多了,解决问题的经验也多了~


    本文常见问题(FAQ)

    对象加属性时,点语法和方括号有什么区别?该怎么选?

    点语法和方括号的核心区别在于属性名的规则和灵活性。点语法要求属性名是“合法标识符”——不能用数字开头、不能有空格或特殊字符(比如“user-name”里的横杠),否则会报错;而方括号完全支持这些特殊情况,比如想给对象加“phone-number”这样的属性,用obj[‘phone-number’]就不会有问题。

    如果用变量当属性名(比如你有个变量key=’age’),点语法会直接把“key”当属性名,而方括号会用变量的值(也就是“age”),这时候必须用方括号。我之前做动态表单时,用户输入的字段名带横杠或用变量,全靠方括号解决的。

    为什么用delete删对象属性有时候删不掉?

    delete不是“万能删”,它只能删对象的“自有属性”,而且如果属性是“不可配置”的(比如用Object.defineProperty设置了configurable:false),delete会返回false,根本删不掉。我之前给商品对象加“sku”属性时设了不可配置,后来想删就没成功。

    还有人喜欢用obj.prop=undefined代替delete,这其实是错的——undefined只是把属性值改成了未定义,但属性本身还在,遍历的时候会被拿到(比如统计用户年龄时,undefined会干扰结果)。真要彻底删属性,还是得用delete,但要先确认属性是可配置的自有属性。

    用for…in遍历对象时,为什么会出现原型链上的属性?怎么解决?

    for…in的“天性”就是遍历对象所有“可枚举属性”,包括原型链上的——比如第三方库修改了Object.prototype加了个方法,你用for…in遍历任何对象都会拿到这个方法。我之前帮朋友改电商代码时,他用for…in遍历商品对象,结果把Object.prototype上的toString也遍历出来了,导致购物车逻辑全乱。

    解决方法很简单:遍历的时候加个hasOwnProperty判断。比如for(let key in obj){if(obj.hasOwnProperty(key)){//处理逻辑}},这样就能过滤掉原型链上的属性,只遍历对象自己的。

    Object.keys和for…in遍历对象的区别是什么?

    Object.keys比for…in“安全”得多——它只遍历对象的“自有可枚举属性”,自动过滤了原型链上的内容,不用手动写hasOwnProperty判断。比如之前电商项目的问题,换成Object.keys加forEach就彻底解决了。

    而for…in会“连带着”遍历原型链上的可枚举属性,要是不处理很容易出问题。如果只是想遍历对象自己的属性,优先用Object.keys;如果确实需要遍历原型链上的内容,再用for…in加判断。

    想遍历对象的所有自有属性(包括不可枚举的),该用什么方法?

    用Object.getOwnPropertyNames就行——它能拿到对象所有“自有属性”,不管是不是可枚举的。我之前调试一个复杂对象时,用这个方法拿到了一个“隐藏”的不可枚举属性,快速找到了问题(那个属性被错误设了值)。

    不过要注意,Object.getOwnPropertyNames不包括Symbol属性(比如Symbol(‘id’)),如果要遍历Symbol属性,得用Object.getOwnPropertySymbols。比如对象里有个Symbol(‘id’)属性,用Object.getOwnPropertySymbols才能拿到。

    原文链接:https://www.mayiym.com/51179.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码