JavaScript 中的枚举属性 enumeration

先把总结写在这里:

  1. JavaScript对象的属性可分为可枚举和不可枚举,它是由属性的 enumeration 值决定的,true 为可枚举,false为不可枚举
  2. 原生构造函数(Boolean, Number, String 等)的内置属性(例如 toSring、 constructor等) 和 引擎内置对象的属性(Math 的 random 等) 一般是不可枚举的,而自己定义的属性(不管是原型上的还是自身的)一般是可枚举的。
  3. 属性的枚举性会影响以下四个方法的结果
    1) for...in
    2 )Object.keys()
    3) JSON.stringify()
    4) Object.assign()

小结:

  • 这四个方法中,只有 for/in 是对所有可枚举属性(对象本身的可枚举属性,以及从其构造函数原型中继承的可枚举属性),而其他三种是对自身可枚举属性才有效。
  • 而四个方法中又只有 Object.assign() 对 Symbol 属性有效,其他三种对 Symbol 都会过滤掉。
    这里做一个其他的小总结:
  • 上面四个方法中,其中Object.assign()无法正确拷贝get属性和set属性,而 JSON.stringify()对对象的方法、undefined值、get 属性、set属性无效 。
  • 如果该参数不是对象,则会先转成对象,然后返回。
    由于undefined和null无法转成对象,所以如果它们作为(第一个)参数,就会报错。有这两种特性的方法是:
    Object.assign() 、 Object.setPrototypeOf()、Object.getPrototypeOf()



上面说了JavaScript对象的属性是否可枚举是 enumeration 的值决定的,那么enumeration是什么时候怎么去设置的呢?
enumeration 是 Object.defineProperty() 方法的一个参数,下面来看看怎么去设置 一个属性是否可枚举。

var person = {
    name:'zhou',
    age: '12',
    sex: 'girl'
}

Object.defineProperty(person,'age',{
    enumerable:true,//可以被枚举
});
Object.defineProperty(person,'sex',{
    enumerable:false,//可以被枚举
})

for(var k in person){
    console.log(person[k])//a,可以被枚举
}
//12
//zhou

如何设置是否可枚举-- enumerable

由上面的点可以看出:
1.Object.defineProperty(obj, prop, descriptor)方法有三个参数,每个参数各代表着
  第一个:目标属性所在的对象,
  第二个:目标属性,放在字符串里,
  第三个:目标属性的行为,放在对象里;
2.enumerable为true表示可枚举,enumerable为false表示不可枚举;
3.开发者自定义的对象person的所有属性都是可枚举的;

如何判断是否可枚举-- obj.propertyIsEnumerable()

在不知情的情况下,如何判断一个属性是否可枚举呢?obj.propertylsEnumerable()方法可以解决这个问题。

  1. 语法:obj.propertyIsEnumerable(prop)
  2. 定义:每个对象都有一个propertyIsEnumerable方法,表示指定的自身属性是否可枚举 (继承属性无效) ,返回一个布尔值(当对象没有该指定属性时也返回 false)。
  3. 用法:
    1)就上面的例子的使用
person.propertyIsEnumerable('name');//true
person.propertyIsEnumerable('age');//true
person.propertyIsEnumerable('sex');//false

2)引擎内置对象的属性不可枚举

var o = {};
var a = [];
o.prop = 'is enumerable';
a[0] = 'is enumerable';
o.propertyIsEnumerable('prop')  // true
a.propertyIsEnumerable(0);  // true


Math.propertyIsEnumerable('random') // false
Object.propertyIsEnumerable('constructor') // false

从 用法1 和 用法2 可以看出用户自定义属性是可枚举的(一般情况下自定义属性都是可枚举的,除非像 person.sex 经过属性描述符的修改),而浏览器内置对象如 Math 的属性是不可枚举的。

3)原生构造函数的内置属性不可枚举,如Boolean, Number, String, Array, Date, Function, RegExp, Error, Object

var num = new Number();
for(var prop in num) {
    console.log(prop);
}

输出为空。for in 访问的是所有可枚举属性,这里输出为空,说明Number中内置的属性是不可枚举的,例如 toString() 方法。

4)自定义属性一般为可枚举属性( 不管是自身属性还是原始上的继承属性 )
说明:

  • 开发者自定义的属性在一般情况下是可枚举的
  • for /in 对所可以访问到所有可枚举属性
  • obj.propertyIsEnumerable(prop) 只能访问到 对象自身可枚举属性(对象自身添加的、构造函数实例化的) 有效,对原始上的继承属性无效。
  • obj.hasOwnProperty(prop) 返回一个布尔值,指示对象自身属性中是否具有指定的属性
  • Object.getOwnPropertyNames(obj) 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
  • Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组。
//构造函数
function People() {
  this.hobby = 'eat';
}
People.prototype.showHooby = function() {};

//继承
function Coder() {
  this.love = function method() { return 'coder‘s love is codeing'; };
}
Coder.prototype = new People;
Coder.prototype.constructor = Coder;

//实例
var c = new Coder();
c.age = '24';
//打印出实例
console.dir(c)

我们来看看打印出来的实例对象,看看哪些属性是自身的,哪些属性是在原型上的


image.png

for/in 来访问 o 对象,看看哪些属性是可枚举的:

for(var prop in c){
    console.log(prop)
}
// love
// age
// hobby
// constructor
// showHooby 

自定义属性都可枚举,不管是自身属性还是原始上的继承属性

怎么过滤出自身可枚举属性呢?就可以用 obj.propertyIsEnumerable() , Object.getOwnPropertyNames(), obj.hasOwnProperty, Object.keys() 方法啦

// obj.propertyIsEnumerable()  --- 先找出*所有可枚举属性*,再找出自身的可枚举属性
for(var prop in c){
    if(c.propertyIsEnumerable(prop)) console.log(prop)
}
// love
// age


// 或者 obj.hasOwnProperty() --- 先找出*所有可枚举属性*,再找出自身属性
for(var prop in c){
    if(c.hasOwnProperty(prop)) console.log(prop)
}
// love
// age


// 或者 Object.getOwnPropertyNames() --- 先找出*所有自身属性*的属性名,再找出可枚举的
var cName = Object.getOwnPropertyNames(c);
for(var prop in cName){
    console.log(cName[prop])
}
// love
// age


// 或者 Object.keys() --- 直接找出自身可枚举属性
Object.keys(c) 
//["love", "age"]



参考链接:
MDN:Object
MDN: 属性的可枚举性和所有权

自己总结了大半天,然后看到阮大神比较全的一篇:
阮一峰:属性的可枚举与遍历

上面说到关于对象属性“枚举性”的多种方法,这里放放上一张这些属性微妙差别的对比图:


属性的可枚举性和所有权
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容