[[Get]]
对对象属性的访问看起来似乎是一件很简单的事
但事实上这里有个复杂的[[Get]]操作在里面(可以理解为函数调用[[Get]]() )
对象默认的内置[[Get]]操作首先是在对象中查找是否有名称相同的属性,有则返回这个属性的值
如果没有找到,则会遍历可能存在的[[Prototype]]链(也就是原型链,之后我会专门再出一系列博客来介绍这个)
如果无论如何都没找到,name就返回一个undefined值
如果你还记得我在写关于作用域的学习笔记的话,你可能会发现这里跟变量访问(RHS查询)有点不太一样
变量访问没找到的话会抛出一个ReferenceError异常,而对象属性的访问没找到则会返回一个undefined值
不过这里有个问题
如果属性设置的值就是undefined呢?
所以直接通过返回值无法判断这个属性是否存在,但是有另外的方法可以判断(in,hasOwnProperty(..),稍后会详细介绍)
[[Put]]
[[Put]]操作并不仅仅只是给对象设置或者创建一个属性
[[Put]]被触发时,实际行为取决于许多因素,但是最重要的因素是对象中是否已经存在这个属性
如果存在这个属性,[[Put]]会检查一下内容
- 属性是否是访问描述符(接下来会讲到)?如果是并存在setter就调用setter
- 属性的数据描述符中的writable参数
- 如果都不是,则将该值设定为属性的值
如果不存在的话,那么操作会更加复杂,这个我们在讲原型链的时候再详细讨论
访问描述符
属性不一定包含的是值
他们可能是具备getter/setter的"访问描述符"
在ES5中可以使用getter和setter来部分改写默认操作,但是只能应用在单个属性上
getter和setter都是隐藏函数,会做获取和设置函数值的时候调用
当年给一个属性定义setter和getter时,这个属性会被定义为访问描述符
而此时JavaScript将会忽略属性(数据描述符中)的value和writable特性,而关心set和get(还有configurable和enumerable特性)
var obj = {
get a() {
return 2;
}
}
obj.a = 10086
console.log(obj.a); //2
由于get和set同样是属性描述符(数据描述符和访问描述符)中,我们也可以用定义属性描述符的方式来定义他们
var obj = {
a: 1
}
Object.defineProperty(obj, "a", {
get: function() {
return 233
},
enumerable: true //确保a会出现啊属性列表中
});
obj.a = 111;
console.log(obj.a); //233
这两种方式都会在对象中创建一个不包含值的属性 a,对于这个属性a 的访问会自动调用一个隐藏函数,他的返回值会被当做属性访问的返回值
由于我们只定义了getter,所以对a的值进行设置时set会忽略赋值操作,不会抛出错误
setter方法的设置跟getter是一样的,不过他接受一个参数,也就是赋值操作传递进去的参数
var obj = {
a: 1
}
Object.defineProperty(obj, "a", {
get: function() {
return this._a_;
},
set: function(val) {
this._a_ = val + ",这是setter设置的"
}
});
obj.a = 111;
console.log(obj.a); //111,这是setter设置的
注 : 这个案例中的_a_只是一种惯例,其实在对象内创建了一个新的值,没有任何特殊的行为