探讨:JavaScript ECAMScript5 新特性之get/set访问器


Posted in Javascript onMay 05, 2016

EcmaScript5简介

首先得先搞清楚ECMAScript是神马,我们知道JavaScript或者说LiveScript最开始是Netscape搞出来的,后来微软也跟进搞出了Jscript,ScriptEase也有自己的CENvi,这样就有了三个版本的浏览器Script各行其是,大家懂这个混乱的,于是乎标准化的问题被提上议事日程。1997年以JavaScript1.1为蓝本的建议被提交到欧洲计算机制造商协会( E uropean C omputer M anufacturers A ssociation),最后大家载歌载舞搞出了ECMA-262——一种名为ECMAScript的新脚本语言标准。第二年,ISO/IEC(国际标准化组织和国际电工委员会)也采用ECMAScript作为标准,此后天下太平,各大浏览器厂商以ECMAScript作为各自实现JavaScript的基础,当然只是基础,没完全按照来,否则我们也不会有那么多浏览器兼容性问题。

ECMAScript5是什么呢?顾名思义跟iPhone5一样是这个奇怪东东的第五个版本,我们现在常用的时ECMAScript3,相比前两个版本这个版本算是一门正真的编程语言而不是玩具了,变得很流行。

正文:

之前对get/set的理解一直有误,觉得get set 是对象属性方法。看了别人的博客也有很多疑问,今天系统的做了很多测试终于弄明白了。(自己通过看书和写demo测试的,如有不对欢迎大家批评指正)

get/set访问器不是对象的属性,而是属性的特性。大家一定要分清楚。特性只有内部才用,因此在javaScript中不能直接访问他们。为了表示特性是内部值用两队中括号括起来表示如[[Value]]。

1.先简单介绍一下属性的这些特性(这里是简单的背书)

(1)数据属性——包含一个数据值的位置。这个位置可以读入和写入值。

数据属性有描述其行为的四个特性:

[[Configurable]]:是否可配置

[[Enumerable]]:是否可枚举

 [[Writable]]:是否可读

[[Value]]: 属性值

(2)访问器属性属性——不包含数据值,包含一个getter和setter函数(这两个函数不是必须的)

访问器属性也有描述其行为的四个特性:

[[Configurable]]:是否可配置

[[Enumerable]]:是否可枚举

[[Get]]:在读取属性时调用的函数,默认是undefined

[[Set]]:在写入属性时调用的函数,默认是undefined

2.这里着重介绍[[Get]]/[[Set]]就是我们所说的get/set访问器

先说一个书上说的 get/set访问器行为特点:get/set访问器可以不用定义,不定义也可以读写属性值。也可以只定义一个。只定义get,则被描述的属性只可读,不可写。只定义set,则被描述的属性只可写,不可读。

(1)我们原来的get set方法是这样的:

function Foo(val){
var value=val;
this.getValue=function(){
return value;
};
this.setValue=function (val){
value=val;
};
}
var obj=new Foo("hello");
alert(obj.getValue());//"hello"
obj.setValue("hi");
alert(obj.getValue());//"hi"

以上代码只是利用闭包作用域实现的get set 方法,注意是方法即实例对象的属性方法,不是属性特性。如果不定义,将无法访问value值

function Foo(val){
var value=val;
/* this.getValue=function(){
return value;
};
this.setValue=function (val){
value=val;
};
*/
}
var obj=new Foo("hello");
alert( obj.value);//undefined

以下例子也是对象的属性方法,不是属性特性。

var obj={
name:"john",
get:function (){
return this.age;
}//只定义了get ,没有定义set,但是仍然可以读,写,name属性,即使这里是age
//这里这样定义的方法不会影响属性的get,set 特性。只是普通的对象属性
};
alert(obj.name);//john 可读
obj.name="jack";//可写
alert(obj.name);//jack

(2)作为访问器属性的特性的get/set访问器。

再说一遍不是对象的属性,他们 决定属性能否、怎么读写。如果不设置也行,就和平时的读写一样(属性可读可

写,读写访问的都是属性本身的值)

要改变属性的get /set 特性,有两种方式:

a.就是用Object.defineProperty()

var object={
_name:"Daisy" 
};
Object.defineProperty(object,"name",{//这里的方法名name,就表示定义了一个name属性(因此才能通过object.name访问),只定义了getter访问器,没有定义[[value]]值
get:function (){//只定义了get 特性,因此只能读不能写
return this._name;
}
});
alert(object.name);//"Daisy"
object.name="jack";//只定义了getter访问器,因此写入失效
alert(object.name);//"Daisy"

注意Object.defineProperty(object,pro,{})中的属性名一定要和object.pro访问的属性对应

b.就是用用 get set 关键字:

var object={
_name:"Daisy",
get name(){//这里的方法名name ,就表示定义了一个name属性(因此才能通过object.name访问),只定义了getter访问器,没有定义[[value]]值
return this._name;
}//get,set方法只是属性的特性 ,不是对象方法,决定属性能否、怎么读写
};
alert(object.name);// Daisy这里去掉下划线 方法就是Daisy ;加上就是undefined
object.name="jack";//只定义了getter访问器,因此只能读不能写
alert(object.name);//Daisy

以上两种方法等效。注意的是以上两种方法object对象当中都将有有两个属性:_name(有初始值) name(无初始值),通过浏览器控制台可以看到

探讨:JavaScript ECAMScript5 新特性之get/set访问器

那么这个name属性实在什么时候定义的呢?我们知道Object.defineProperty(object,pro,{})可以给对象定义一个新属性pro,既然get pro(){}/set pro(){}和Object.defineProperty(object,pro,{})等效,则也会定义一个新属性pro .这就是为什么object里面有两个属性的原因。

(3)在此篇文章中网络之美 JavaScript中Get和Set访问器的实现代码关于标准标准的Get和Set访问器的实现:引发的思考

我自己也写了一个一样的例子

function Foo(val){
this.value=val;//定义了value属性 并没有定义_value
}
Foo.prototype={
set value(val){//注意方法名和属性名相同,在prototype里定义了value属性
this._value=val;
},
get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
return this._value;
}
}; //访问器返回和设置的都是_name,这里并没有定义_name属性为什么也可以读可以写???? 

var obj=new Foo("hello"); 


alert(obj.value);//"hello" 


obj.value="yehoo"; 


alert(obj.value);//"yehoo"

为了解决以上这个疑问,做了很多测试,我们一一来看:

先看这个例子,在prototype里面只定义get 特性,在obj.value读value属性时,在实例里面寻找没有,然后在原型里面找到,调用的是原型的get方法,只能读不能写

function Foo(val){
this._value=val;//这里 的属性是带下划线的,初始化实例对象的_value属性,_value属性可读可写
}
Foo.prototype={
// set value(val){//注意方法名和属性名相同,在prototype里定义了value属性
// this._value=val;
// },
get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
return this._value;
}
}; 
var obj=new Foo("hello");
alert(obj.value);//hello 访问的是prototype里面的value 属性
obj.value="yehoo";//只定义了name 属性的get 特性,因此只能读不能写,写入失效
alert(obj.value);//hello

如果构造函数里面this._value 去掉下划线,在prototype里面定义的value属性,定义了get 特性。依然可以控制value属性的读写 。也就是说obj.value访问属性时,会调用get方法,先在对象本身寻找,如果没有,再到prototype寻找,如果都没有才算没有定义,默认的既可读又可写

function Foo(val){
this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效
}
Foo.prototype={
// set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性
// this._value=val;
//},
//value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah"
//只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值
get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
return this._value;
}
}; 
var obj=new Foo("hello");//"hello"没有写入成功
alert(obj.value);//undefined 
obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效
alert(obj.value);//undefined

为了证明上面例子是可读不可写的:手动写入_value:"hah",就可以读取value 但不能写入。

function Foo(val){
this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效
}
Foo.prototype={
// set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性
// this._value=val;
//},
_value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah"
//只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值
get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
return this._value;
}
}; 
var obj=new Foo("hello");//"hello"没有写入成功
alert(obj.value);//"hah" 
obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效
alert(obj.value);//"hah"

如果手动写入的是value:"hah",那么可以争取读取value的值吗?由于get方法返回的this._value并没有定义,obj.value读取value值调用get value(){}方法失效,但是value仍然不能写入。

function Foo(val){
this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效
}
Foo.prototype={
// set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性
// this._value=val;
//},
value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah"
//只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值
get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
return this._value;
}
}; 
var obj=new Foo("hello");//"hello"没有写入成功
alert(obj.value);//undefined 读取失效 因为只要obj.value就会调用get ,而get返回的是this._value,没有这个值,因此undefined
obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效
alert(obj.value);//undefined

再看这个例子,get set 都定义了,但是返回没有定义的this._value。可以发现value既可读又可写。去掉原型里面的get set方法,依然可读可写

function Foo(val){
this.value=val;
}
Foo.prototype={
set value(val){
this._value=val;
},
get value(){
return this._value;
}
}; 
var obj=new Foo("hello");
alert(obj.value);//hello 
obj.value="yehoo";
alert(obj.value);//yehoo 
function Foo(val){
this.value=val;
}
//和平时的操作是一样的了,就是回到了不定义get /set访问器特性的默认状态
var obj=new Foo("hello");
alert(obj.value);//hello 
obj.value="yehoo";
alert(obj.value);//yehoo

总结

只声明了get pro(){}属性 可读不可写;

只声明 set pro(){}属性可写不可读。

如果都不声明,属性可读可写;

如果都声明就按照,get set 定义的方法,读写;

如果都声明了,但是定义的读写方法不能正确读写,get/set失效。变成默认的可读可写

在prototype里面定义的value属性,定义了get 特性。依然可以控制value属性的读写 。也就是说obj.value访问属性时,会调用get方法,先在对象本身寻找,如果没有,再到prototype寻找,如果都没有才算没有定义,默认的既可读又可写。

补充:

不管是用get pro(){}/set pro (){} 

还是用Object.defineProperty(object,pro,{





 get:function (){





 return this._name;





 } });

pro不能和 return this. 后面的属性一样,不然会报下面的错误:(具体我也不知道为什么,好像是自身调用引起的栈溢出)

探讨:JavaScript ECAMScript5 新特性之get/set访问器

探讨:JavaScript ECAMScript5 新特性之get/set访问器

经大神指正,明白为什么这里报错:在get value(){}方法里返回 this.value,就会又去调用value的get 方法,因此陷入死循环,造成方法栈溢出。

Javascript 相关文章推荐
[原创]静态页面也可以实现预览 列表不同的显示方式
Oct 14 Javascript
javascript之ESC(第二类混淆)
May 06 Javascript
Javascript 跨域访问解决方案
Feb 14 Javascript
jquery 打开窗口返回值实现代码
Mar 04 Javascript
php图像生成函数之间的区别分析
Dec 06 Javascript
jQuery中end()方法用法实例
Jan 08 Javascript
浅析JavaScript中浏览器的兼容问题
Apr 19 Javascript
浅谈js中几种实用的跨域方法原理详解
Dec 02 Javascript
axios发送post请求,提交图片类型表单数据方法
Mar 16 Javascript
layui实现三级联动效果
Jul 26 Javascript
vue-cli和v-charts实现可视化图表过程解析
Oct 08 Javascript
vuex存储复杂参数(如对象数组等)刷新数据丢失的解决方法
Nov 05 Javascript
浅析JS异步加载进度条
May 05 #Javascript
AngularJS入门(用ng-repeat指令实现循环输出
May 05 #Javascript
图文详解Heap Sort堆排序算法及JavaScript的代码实现
May 04 #Javascript
一分钟理解js闭包
May 04 #Javascript
学JavaScript七大注意事项【必看】
May 04 #Javascript
Jquery ui datepicker设置日期范围,如只能隔3天【实现代码】
May 04 #Javascript
开启BootStrap学习之旅
May 04 #Javascript
You might like
php获取目标函数执行时间示例
2014/03/04 PHP
PHP文件读写操作相关函数总结
2014/11/18 PHP
CodeIgniter表单验证方法实例详解
2016/03/03 PHP
php实现36进制与10进制转换功能示例
2017/01/10 PHP
PHP使用递归算法无限遍历数组示例
2017/01/13 PHP
php json相关函数用法示例
2017/03/28 PHP
载入进度条 效果
2006/07/08 Javascript
九种原生js动画效果
2015/11/11 Javascript
JS组件Bootstrap Select2使用方法详解
2020/04/17 Javascript
Javascript 实现放大镜效果实例详解
2016/12/03 Javascript
修改ligerui 默认确认按钮的方法
2016/12/27 Javascript
详解VueJs前后端分离跨域问题
2017/05/24 Javascript
Javascript 实现匿名递归的实例代码
2017/05/25 Javascript
使用JavaScript根据图片获取条形码的方法
2017/07/04 Javascript
chorme 浏览器记住密码后input黄色背景处理方法(两种)
2017/11/22 Javascript
利用npm 安装删除模块的方法
2018/05/15 Javascript
layui当点击文本框时弹出选择框,显示选择内容的例子
2019/09/02 Javascript
layer.prompt输入层的例子
2019/09/24 Javascript
JavaScript实现表单验证功能
2020/12/09 Javascript
[20:39]DOTA2-DPC中国联赛 正赛开幕式 1月18日
2021/03/11 DOTA
[01:07:34]DOTA2-DPC中国联赛定级赛 RNG vs Aster BO3第二场 1月9日
2021/03/11 DOTA
Python批量创建迅雷任务及创建多个文件
2016/02/13 Python
Python按行读取文件的简单实现方法
2016/06/22 Python
深入了解Python中pop和remove的使用方法
2018/01/09 Python
Python数据处理numpy.median的实例讲解
2018/04/02 Python
Django高级编程之自定义Field实现多语言
2019/07/02 Python
pybind11在Windows下的使用教程
2019/07/04 Python
Django基础知识 web框架的本质详解
2019/07/18 Python
Python实现寻找回文数字过程解析
2020/06/09 Python
Python GUI库Tkiner使用方法代码示例
2020/11/27 Python
Trunki英国官网:儿童坐骑式行李箱
2017/05/30 全球购物
写自荐信有哪些不宜?
2013/10/17 职场文书
三方合作意向书范本
2015/05/09 职场文书
个人收入证明范本
2015/06/12 职场文书
《秦兵马俑》教学反思
2016/02/24 职场文书
Java 常见的限流算法详细分析并实现
2022/04/07 Java/Android