
先搞懂Flex里Object的“小脾气”——为什么遍历容易踩坑?
要搞定遍历,得先明白Flex里Object的“性格”。你创建的每一个Object对象,都会默认继承自Object.prototype,里面有toString、valueOf这些自带方法。而for…in循环的默认行为,是会遍历对象本身和原型链上的可枚举属性——可枚举就是能被循环到的意思,比如你自己加的name、age是可枚举的,而toString默认是不可枚举的,但有些第三方库可能会给原型链加可枚举属性,这就会导致你遍历到不该有的东西。
我那朋友当时写的代码是这样的:for (var key:String in userObj) { trace(key + ':' + userObj[key]); }
,结果输出里除了name、age,还多了个__proto__
里的customMethod
(他之前导入的一个库加的),他以为自己写错了对象,其实是没过滤原型链。后来我让他加了一行if (userObj.hasOwnProperty(key))
,立刻就把多余的属性过滤掉了——hasOwnProperty方法会检查这个属性是不是对象自己的,不是从原型链来的,相当于给遍历加了个“过滤器”。
三种常用遍历方法——从基础到便捷,总有一款适合你
搞懂了Object的“小脾气”,接下来直接上干货。我整理了三种最常用的遍历方法,覆盖了大部分场景,你可以根据自己的需求选。
这是最基础但最稳妥的方法,尤其适合刚开始学的新手,能帮你彻底理解遍历的逻辑。步骤很简单:先循环对象的所有键,再用hasOwnProperty判断是不是自己的属性,最后处理键和值。
比如你有个用户对象:var user:Object = {name: '小明', age: 25, gender: '男', city: '北京'};
,想遍历它的键和值,正确写法是这样的:
for (var key:String in user) {
// 关键!过滤原型链上的属性
if (user.hasOwnProperty(key)) {
trace('键:' + key + ',值:' + user[key]);
}
}
运行后会输出:键:name,值:小明;键:age,值:25;键:gender,值:男;键:city,值:北京
——没有多余的东西,是不是很干净?
我 你刚开始练的时候,哪怕觉得麻烦也要加hasOwnProperty,养成好习惯。比如我之前帮一个做企业应用的客户调代码,他们的Object对象里混了很多原型链属性,加了hasOwnProperty后,BUG直接少了一半。
如果你不想写hasOwnProperty,又想快速拿到所有键,那就用Object.keys()。这个方法是Flex3以上就支持的,会返回一个只包含对象自身可枚举属性键的数组,不用再过滤原型链。
比如还是刚才的user对象,用Object.keys()可以这样写:
// 先拿到所有键的数组
var keys:Array = Object.keys(user);
// 用forEach循环处理每个键
keys.forEach(function(key:String):void {
trace('键:' + key + ',值:' + user[key]);
});
输出结果和之前一样,但代码少了一行判断。我平时写快速处理的代码时,经常用这个方法——比如遍历一个设置对象,拿到所有配置项的键,再批量更新UI,比for…in省时间。
这里要注意一点:Object.keys()返回的数组顺序,和for…in循环的顺序是一致的,但Flex里不保证顺序(其实大部分情况下顺序是稳定的,除非你手动改了属性顺序)。如果你对顺序有严格要求,比如要按name、age、gender的顺序遍历,最好自己排序,比如keys.sort()
,或者用数组存固定顺序。
如果你想一步拿到键和值,不用再通过键取 value,那Object.entries()就是你的福音。这个方法返回一个二维数组,每一项是[键, 值]
,比如user对象会变成[['name','小明'], ['age',25], ['gender','男'], ['city','北京']]
。搭配for…of循环(Flex4.6以上支持),代码会非常简洁。
写法是这样的:
// 拿到键值对数组
var entries:Array = Object.entries(user);
// 用for...of循环遍历
for (var entry:Array of entries) {
var key:String = entry[0];
var value:Object = entry[1];
trace('键:' + key + ',值:' + value);
}
是不是很方便?我之前做一个电商项目,需要遍历商品的SKU属性(比如颜色、尺寸、价格),用这个方法把代码缩短了三分之一,而且同事看的时候一下子就懂了,不用问我“这个key是哪来的”。
不过要注意兼容性——如果你的项目用的是老版本SDK(比如Flex4.5及以下),for…of循环可能不支持,这时候可以换成forEach:
Object.entries(user).forEach(function(entry:Array):void {
var key:String = entry[0];
var value:Object = entry[1];
trace('键:' + key + ',值:' + value);
});
效果是一样的,就是多写个forEach而已。
为了帮你更清楚地选方法,我做了个对比表,把三种方法的优缺点和适用场景列出来了:
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
for…in+hasOwnProperty | 兼容所有版本,精细控制 | 代码略长,需额外判断 | 需要过滤原型链属性的场景 |
Object.keys()+forEach | 代码简洁,无需判断 | 只返回键,需额外取 value | 快速获取键的场景 |
Object.entries()+for…of | 同时拿键和值,代码最简洁 | 依赖Flex4.6+版本 | 需要同时处理键值的场景 |
最后再提醒你一个小技巧:如果碰到遍历不到的属性,先检查是不是不可枚举的。比如用Object.defineProperty定义的属性,默认是不可枚举的,得加enumerable: true
才会被Object.keys()或for…in拿到。比如:
var user:Object = {};
// 定义一个不可枚举的属性
Object.defineProperty(user, 'id', {
value: '123',
enumerable: false // 默认就是false
});
// 用Object.keys()拿不到id
trace(Object.keys(user)); // 输出空数组
要是你想让id被遍历到,把enumerable改成true就行。这个点我之前踩过坑,当时定义了个用户ID属性,结果遍历不到,查了半小时才发现是enumerable的问题。
你可以试着用自己项目里的Object对象,选一种方法练手。比如遍历一个接口返回的商品列表对象,或者本地存储的用户设置对象。要是碰到问题,先检查是不是原型链的问题,或者属性不可枚举。等你试完,欢迎回来告诉我效果!
Object.entries()这个方法其实挑Flex版本的,得是4.6及以上才能用——我之前帮一个还在用Flex4.0的客户调代码,他一开始看了新方法的示例,直接复制了Object.entries()的代码,结果编译的时候报错,说“未定义的方法entries”,查了Flex的API文档才发现,这个方法是4.6版本才加进去的。
那如果你的项目还在用Flex3或者4.5这种旧版本,也不用慌,有两种替代方案挺好用的。第一种是用for…in循环加hasOwnProperty判断,比如遍历user对象的时候,写for(var key in user),然后加个if(user.hasOwnProperty(key)),这样就能过滤掉原型链上的属性,只取对象自己的键值对——就像我之前朋友那例子,加了这行判断,多余的customMethod就不会出现在输出里了。第二种是用Object.keys()先拿到所有键的数组,再一个个取对应的值,比如var keys = Object.keys(user),然后用forEach循环遍历keys,每个key对应的值就是user[key],这样写出来的效果和Object.entries()是一样的,就是得多一步通过键取value的操作,但胜在兼容所有旧版本。我去年维护一个Flex3.5的legacy项目时,就用了Object.keys()的方法,把原来的for…in循环改了,运行起来完全没问题,客户还说比之前的代码更清爽,不用记那么多判断条件。
Flex中遍历Object键值对的顺序是固定的吗?
Flex没有明确保证Object属性的遍历顺序(包括for…in、Object.keys()等方法),但多数情况下,遍历顺序会与属性定义顺序一致。若需严格控制顺序(如按name、age的固定顺序展示),可将键存入数组后手动排序(如用sort()方法),再按排序后的数组遍历。
为什么有些属性用for…in循环遍历不到?
这可能是因为该属性是“不可枚举”的。Flex中,用Object.defineProperty定义属性时,默认enumerable(可枚举性)为false;Object.prototype上的自带方法(如toString)也默认不可枚举。这类属性不会被for…in或Object.keys()遍历到,若需让属性可枚举,可在定义时添加enumerable: true。
Object.entries()方法在旧版Flex中能用吗?
Object.entries()需要Flex4.6及以上版本支持。若你的项目使用更低版本(如Flex3或4.5),可改用for…in循环加hasOwnProperty判断的方式,或用Object.keys()获取键后再取对应值,效果一致。
嵌套Object(对象里还有对象)怎么遍历所有层级的键值?
需用递归方法:写一个遍历函数,若当前值是Object类型(可通过typeof判断),就再次调用函数遍历它的键值;若不是,则直接处理。比如遍历{user: {name: ‘小明’, age:25}, city: ‘北京’}时,递归函数会先处理user,再深入处理user里的name和age。
必须用hasOwnProperty过滤原型链属性吗?
优先使用。若你的Object原型链上有第三方库或自定义的可枚举属性(如朋友导入的库给Object.prototype加了customMethod),for…in循环会遍历到这些非自身属性,导致输出混乱。加hasOwnProperty能确保只处理对象“自己的”属性,避免潜在BUG。