JS对象添加getter与setter的5种方法

定义 getter 与 setter

1.通过对象初始化器在创建对象的时候指明也可以称为通过字面值创建对象时声明)

(function(){

varo = {

a:7,

get b(){returnthis.a +1;},//通过 get,set的 b,c方法间接性修改 a 属性

set c(x){this.a = x/2}

};

console.log(o.a);

console.log(o.b);

o.c =50;

console.log(o.a);

})();

在 chrome 中调试视图如下:

可以看到对象下多了 get 属性以及 set 属性

当然 get 语句与 set 语句可以声明多次用来对应多个 getter 和 setter

使用这种方法的好处是可以在声明属性的时候同时声明对应的 getter 和 setter

这里就有人问了,能不能将o 对象的 get 及 set 方法的方法名都改成 “a”,这样就可以直接通过“.”来访问方法直接操作

(function(){

varo = {

a:7,

get a(){returnthis.a +1;},//死循环

set a(x){this.a = x/2}

};

console.log(o.a);

console.log(o.b);

o.c =50;

console.log(o.a);

})();

打开 chrome 查看创建后的视图如下: 

可以看到这个时候的get与set方法已经和上面不同,但是是否真的能起作用呢,答案是否定的,当我们通过o.a调用的是get语句 声明的 a方法,进入到该方法后遇到this.a方法继续调用该方法形成死循环最终导致死循环报内存溢出错误。

新语法(ES6):暂时只有 firefox 支持,其他浏览器会报错

(function(){

varb ="bb";

varc ="cc";

varo = {

a:7,

get [b](){returnthis.a +1;},

set [c](x){this.a = x/2},

};

console.log(o.a);

console.log(o[b]);

o["cc"] =50;

console.log(o.a);

})();

打开 firefox 查看调试: 

输出结果如下:

2.使用 Object.create 方法

引用 MDN:

概述 Object.create() 方法创建一个拥有指定原型和若干个指定属性的对象。 语法 Object.create(proto, [ propertiesObject ])

我们都知道使用 Object.create 方法传递一个参数的时候可以创建一个以该参数为原型的对象。

第二个参数是可选项,是一个匿名的参数对象,该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(包扩数据描述符或存取描述符,具体解释看后面的内容 什么是属性描述符)。

通过属性描述符我们可以实现为新创建的对象添加 get 方法以及 set 方法

(function(){

varo =null;

o =Object.create(Object.prototype,//指定原型为 Object.prototype

{

bar:{

get:function(){

return10;

},

set:function(val){

console.log("Setting `o.bar` to ",val);

}

}

}//第二个参数

);

console.log(o.bar);

o.bar =12;

})();

在 chrome 中调试试图如下: 

可以看到新创建对象通用多了 get 以及 set 属性。

输出结果如下: 

上面这个例子并没有用来针对的 get 方法以及 set 方法使用的属性

(function(){

varo =null;

o =Object.create(Object.prototype,//指定原型为 Object.prototype

{

bar:{

get:function(){

returnthis.a;

},

set:function(val){

console.log("Setting `o.bar` to ",val);

this.a = val;

},

configurable:true

}

}//第二个参数

);

o.a =10;

console.log(o.bar);

o.bar =12;

console.log(o.bar);

})();

亦或:

(function(){

varo = {a:10};

o =Object.create(o,//指定原型为 o 这里实际可以理解为继承

{

bar:{

get:function(){

returnthis.a;

},

set:function(val){

console.log("Setting `o.bar` to ",val);

this.a = val;

},

configurable:true

}

}//第二个参数

);

console.log(o.bar);

o.bar =12;

console.log(o.bar);

})();

输出结果如下:

使用这种方式的好处是可配置性高,但初学者容易迷糊。

3.使用 Object.defineProperty 方法

引用 MDN:

概要 Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。 语法 Object.defineProperty(obj, prop, descriptor) 参数 obj 需要定义属性的对象。 prop 需被定义或修改的属性名。 descriptor 需被定义或修改的属性的描述符。

(function () {  var o = { a : 1}//声明一个对象,包含一个 a 属性,值为1

  Object.defineProperty(o,"b",{    get: function () {      return this.a;

    },    set : function (val) {      this.a = val;

    },    configurable : true

  }); 

  console.log(o.b);

  o.b = 2;  console.log(o.b);

})();

这个方法与前面两种的区别是:使用前面两种只能在声明定义的时候指定getter与setter,使用该方法可以随时的添加或修改。

如果说需要一次性批量添加 getter 与 setter 也是没问题的,使用如下方法:

4.使用 Object.defineProperties方法

MDN:

概述 Object.defineProperties() 方法在一个对象上添加或修改一个或者多个自有属性,并返回该对象。 语法 Object.defineProperties(obj, props) 参数 obj 将要被添加属性或修改属性的对象 props 该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置

不难看出用法与Object.defineProperty方法类似

(function(){

varobj = {a:1,b:"string"};

Object.defineProperties(obj,{

"A":{

get:function(){returnthis.a+1;},

set:function(val){this.a = val;}

},

"B":{

get:function(){returnthis.b+2;},

set:function(val){this.b = val}

}

});

console.log(obj.A);

console.log(obj.B);

obj.A =3;

obj.B ="hello";

console.log(obj.A);

console.log(obj.B);

})();

输出结果如下:

5.使用 Object.prototype.__defineGetter__ 以及 Object.prototype.__defineSetter__  方法

(function(){

varo = {a:1};

o.__defineGetter__("giveMeA",function(){

returnthis.a;

});

o.__defineSetter__("setMeNew",function(val){

this.a = val;

})

console.log(o.giveMeA);

o.setMeNew =2;

console.log(o.giveMeA);

})();

输出结果为1和2 查看 MDN 有如下说明: 

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

二、什么是属性描述符?

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属性用来声明欲修饰属性是否可以被枚举。

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

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

//使用默认值配置

(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"))

})();

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

推荐阅读更多精彩内容