get/set访问器不是对象的属性,而是属性的特性。请看《对象属性的特性一文》
这里着重介绍[[Get]]/[[Set]]就是我们所说的get/set访问器
先说一个书上说的 get/set访问器行为特点:get/set访问器可以不用定义,不定义也可以读写属性值。也可以只定义一个。只定义get,则被描述的属性只可读,不可写。只定义set,则被描述的属性只可写,不可读。
要改变属性的get /set 特性,有两种方式:
a.就是用Object.defineProperty()
1
2
3
4
5
6
7
8
9
10
11
|
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 关键字:
1
2
3
4
5
6
7
8
9
|
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
|
以上两种方法等效。注意的是以上两种方法objec象当中都将有有两个属性:_name(有初始值) name(无初始值),通过浏览器控制台可以看到
那么这个name属性实在什么时候定义的呢?我们知道Object.defineProperty(object,pro,{})可以给对象定义一个新属性pro,既然get pro(){}/set pro(){}和Object.defineProperty(object,pro,{})等效,则也会定义一个新属性pro .这就是为什么object里面有两个属性的原因。
(3)在此篇文章中网络之美 JavaScript中Get和Set访问器的实现代码关于标准标准的Get和Set访问器的实现:引发的思考
我自己也写了一个一样的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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方法,只能读不能写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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寻找,如果都没有才算没有定义,默认的既可读又可写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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 但不能写入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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仍然不能写入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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方法,依然可读可写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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寻找,如果都没有才算没有定义,默认的既可读又可写。
补充:
1
2
3
4
5
6
7
8
|
不管是用get pro(){}/set pro (){}
还是用Object.defineProperty(object,pro,{
get:
function
(){
return
this
._name;
} });
|
pro不能和 return this. 后面的属性一样,不然会报错,在get value(){}方法里返回 this.value,就会又去调用value的get 方法,因此陷入死循环,造成方法栈溢出。