如何让对象属性不可配置或枚举

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何让对象属性不可配置或枚举相关的知识,希望对你有一定的参考价值。

参考技术A 一、什么是属性描述符?

MDN:

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。

数据描述符是一个拥有可写或不可写值的属性。

存取描述符是由一对 getter-setter 函数功能来描述的属性。

描述符必须是两种形式之一;不能同时是两者。

数据描述符和存取描述符均具有以下可选键值:

value 与属性相关的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。 

writable true 当且仅当可能用赋值运算符改变与属性相关的值。默认为 false。

存取描述符同时具有以下可选键值:

get

一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。方法将返回用作属性的值。默认为 undefined。 set 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将收到作为唯一参数的新值分配给属性。默认为 undefined。

以上是摘自MDN的解释,看起来是很晦涩的,具体什么意思呢: 首先我们从以上解释知道该匿名参数对象有个很好听的名字叫属性描述符,属性描述符又分成两大块:数据描述符以及存取描述符(其实只是一个外号,给指定的属性集合起个外号)。

数据描述符包括两个属性 :value属性以及writable属性,第一个属性用来声明当前欲修饰的属性的值,第二个属性用来声明当前对象是否可写即是否可以修改

存取描述符就包括get与set属性用来声明欲修饰的象属性的getter及setter

属性描述符内部,数据描述符与存取描述符只能存在其中之一,但是不论使用哪个描述符都可以同时设置configurable属性以及enumerable属性。

configurable属性用来声明欲修饰的属性是否能够配置,仅有当其值为true时,被修饰的属性才有可能能够被删除,或者重新配置。

enumerable属性用来声明欲修饰属性是否可以被枚举。

知道了什么是属性描述符,我们就可以开始着手创建一些对象并开始配置其属性

顺便给大家推荐一个裙,它的前面是 537,中间是631,最后就是 707。想要学习前端的小伙伴可以加入我们一起学习,互相帮助。群里每天晚上都有大神免费直播上课,如果不是想学习的小伙伴就不要加啦。

二、创建属性不可配置不可枚举的对象

//使用默认值配置

(function()

varobj = ;//声明一个空对象

Object.defineProperty(obj,"key",

value:"static"

//没有设置 enumerable 使用默认值 false

//没有 configurable 使用默认值 false

//没有 writable 使用默认值 false

);

console.log(obj.key);//输出 “static”

obj.key ="new"//尝试修改其值,修改将失败,因为 writable 为 false

console.log(obj.key);//输出 “static”

obj.a =1;//动态添加一个属性

for(variteminobj)//遍历所有 obj 的可枚举属性

console.log(item);

//只输出一个 “a” 因为 “key”的 enumerable为 false

)();

//显示配置 等价于上面

(function()

varobj = ;

Object.defineProperty(obj,"key",

enumerable:false,

configurable:false,

writable:false,

value:"static"

)

)();

//等价配置

(function()

varo = ;

o.a =1;

//等价于

Object.defineProperty(o,"a",value:1,

writable:true,

configurable:true,

enumerable:true);

Object.defineProperty(o,"a",value:1);

//等价于

Object.defineProperty(o,"a",value:1,

writable:false,

configurable:false,

enumerable:false);

)();

三、Enumerable 特性

属性特性enumerable决定属性是否能被for...in循环或Object.keys方法遍历得到

(function()

varo = ;

Object.defineProperty(o,"a",value:1,enumerable:true);

Object.defineProperty(o,"b",value:2,enumerable:false);

Object.defineProperty(o,"c",value:2);//enumerable default to false

o.d =4;//如果直接赋值的方式创建对象的属性,则这个属性的 enumerable 为 true

for(varitemino)//遍历所有可枚举属性包括继承的属性

console.log(item);



console.log(Object.keys(o));//获取 o 对象的所有可遍历属性不包括继承的属性

console.log(o.propertyIsEnumerable('a'));//true

console.log(o.propertyIsEnumerable('b'));//false

console.log(o.propertyIsEnumerable('c'));//false

)();

输出结果如下:

四、Configurable 特性

(function()

varo = ;

Object.defineProperty(o,"a",get:function()return1;,

configurable:false );

//enumerable 默认为 false,

//value 默认为 undefined,

//writable 默认为 false,

//set 默认为 undefined

//抛出异常,因为最开始定义了 configurable 为 false,故后期无法对其进行再配置

Object.defineProperty(o,"a",configurable:true );

//抛出异常,因为最开始定义了 configurable 为 false,故后期无法对其进行再配置,enumerable 的原值为 false

Object.defineProperty(o,"a",enumerable:true );

//抛出异常,因为最开始定义了 configurable 为 false,set的原值为 undefined

Object.defineProperty(o,"a",set:function(val) );

//抛出异常,因为最开始定义了 configurable 为 false,故无法进行覆盖,尽管想用一样的来覆盖

Object.defineProperty(o,"a",get:function()return1);

//抛出异常,因为最开始定义了 configurable 为 false,故无法将其进行重新配置把属性描述符从存取描述符改为数据描述符

Object.defineProperty(o,"a",value:12);

console.log(o.a);//输出1

deleteo.a;//想要删除属性,将失败

console.log(o.a);//输出1

)();

五、提高及扩展

1.属性描述符中容易被误导的地方之writable与configurable

(function()

varo = ;

Object.defineProperties(o,

"a": 

value:1,

writable:true,//可写

configurable:false//不可配置

//enumerable 默认为 false 不可枚举

,

"b":

get:function()

returnthis.a;

,

configurable:false



);

console.log(o.a);//1

o.a =2;//修改值成功,writable 为 true

console.log(o.a);//2

Object.defineProperty(o,"a",value:3);//同样为修改值成功

console.log(o.a);//3

//将其属性 b 的属性描述符从存取描述符重新配置为数据描述符

Object.defineProperty(o,"b",value:3);//抛出异常,因为 configurable 为 false

)();

2.通过上面的学习,我们都知道传递属性描述符参数时,是定义一个匿名的对象,里面包含属性描述符内容,若每定义一次便要创建一个匿名对象传入,将会造成内存浪费。故优化如下:

(function()

varobj = ;

//回收同一对象,即减少内存浪费

functionwithValue(value)

vard = withValue.d ||(

withValue.d = 

enumerable:false,

configurable:false,

writable:false,

value:null



);

d.value = value;

returnd;



Object.defineProperty(obj,"key",withValue("static"))

)();

最后给大家推荐一个裙,它的前面是 537,中间是631,最后就是 707。想要学习前端的小伙伴可以加入我们一起学习,互相帮助。群里每天晚上都有大神免费直播上课,如果不是想学习的小伙伴就不要加啦。

对象枚举属性

枚举属性

可枚举属性

在对象属性中,除了检测对象的属性是否存在,常常还会需要遍历对象的属性,通常使用for/in循环来遍历对象中所有可枚举的属性,包括自有属性和继承属性,但是对象继承的内置属性方法不可枚举。

var o = {
      x:1,
      y:2,
 };
 o.name = \'xiaoming\';
 console.log(o.propertyIsEnumerable("toString"));  //false(不可枚举)

 //for in 循环可以在循环体中遍历对象中所有可枚举的属性
 for(a in o)  console.log(a);   //x  y  name

在上面的例子中,toString为对象o继承的内置属性方法,为不可枚举属性,不能遍历出来。现在有很多的工具库给object.prototype添加了新的方法或者属性,这些方法和属性可以被所有对象继承并使用,然而在ECMAScript 5标准之前,这些新的方法不能定义为不可枚举的,即可通过for/in循环出来。为了避免这种情况,我们需要过滤掉这些属性和方法。

for(a in o){
     if(!o.hasOwnProperty(a)) continue;  //跳过继承属性
     console.log(a);    //x  y  name
} for(a in o){ if(typeof o[a] ===\'function\') continue; //跳过方法 }

对象属性排序

在使用for/in遍历一个对象的属性的时候,属性名出现的顺序是不确定,如果想要确保属性以特定的顺序出现,最好的办法是避免使用for/in语句,而是创建一个数组,在其中以正确的顺序包含属性名。

var i;
var o = {
  \'middle-name\':\'枚\',
  \'last-name\':\'举\',
  \'first-name\':\'可\'
};
var message = [\'first-name\',\'middle-name\',\'last-name\'];
for(i=0,j=message.length;i<j;i++){
  console.log(message[i] + \':\' + o[message[i]]);
}

用来枚举属性的对象工具函数

var p = {
  name:\'limei\',
  age:23,
  height:180,
  like:{
    food:\'水果\',
    sports:\'跑步\',
  }
};
var o = {
  x:1,
  y:2,
}
o.name = \'xiaoming\';

下面所有的函数都是建立在上面两个对象的基础上实现的:

1,把p中的可枚举属性复制到o中,并返回o,如果两者含有同名属性,则覆盖o中的属性。


function extend(o,p){
  for(prop in p){         //遍历p中的属性
    o[prop] = p[prop];    //将属性添加到o中
  }
  return o;
}
var a = extend(o,p);
console.log(a);

2,把p中的可枚举属性复制到o中,并返回o,如果两者含有同名属性,o中的属性不受影响。


function merge(o,p){
  for(prop in p){                    //遍历p中属性
    if(o.hasOwnProperty[prop]) continue;    //过滤掉o中已经存在的属性
    o[prop] = p[prop];              //将属性添加到o中
  }
  return o;
}
var a = merge(o,p);
console.log(a);

 

3,如果o中的属性在p中没有同名属性,则从o中删除这个属性,并返回o。

function restrict(o,p){
  for(prop in o){                         //遍历o中的所有属性
    if(! (prop in p)) delete o[prop];   //如果该属性在p中不存在,则从o中删除
  }
  return o;
}
var a = restrict(o,p);
console.log(a);


4,如果o中的属性在p中存在同名属性,则从o中删除这个属性,并返回o。

function subtract(o,p){
  for(prop in p){        //遍历p中的所有属性
    delete o[prop];    //从o中删除(删除一个不存在的属性不会报错)
  }
  return o;
}
var a = subtract(o,p);
console.log(a);

以上是关于如何让对象属性不可配置或枚举的主要内容,如果未能解决你的问题,请参考以下文章

js 属性

使用deepMerge库深度合并两个对象可枚举属性

是否可以获得对象的不可枚举的继承属性名称?

为啥这些属性不可枚举?

对象枚举属性

对象的可配置属性和可写属性之间的区别