1.复制对象(深拷贝、浅拷贝)
复制对象比想象中的要复杂
css
function anotherFunction(){}
var anotherObject = {
c:true
}
var anotherArray = []
var myObject = {
a:2,
b:anotherObject,
c:anotherArray,
d:anotherFunction
}
anotherArray.push(anotherObject,myObject)
首先,我们判断他是浅拷贝还是深拷贝。对于浅拷贝来说,复制出的新对象a的值就是2,和旧对象的2不是同一个(基本数据类型没有深浅拷贝),但是新对象中b、c、d三个属性只是三个引用,他们和旧对象的b、c、d引用的对象是一样的,这导致他们会互相影响。对于深拷贝来说,除了复制myObject,还会复制anotherObject、anotherArray。这时问题就来了,anotherArray引用了anotherObject、myObject。这样就会由于循环导致死循环。
在很长时间里这个问题都没有明确答案。但是对于JSON安全的对象来说(也就是说可以被序列化为一个JSON并且可以根据这个字符串解析出一个结构和值完全一样的对象),有一种巧妙的复制方法
javascript
var newObj = JSON.parse(JSON.stringify(someObj))
这只是安全的JSON对象,而不安全的JSON值包括undefined、function、symbol和包含循环引用(对象之间相互引用,形成一个无限循环)的对象 JSON.stringify()在对象中遇到underfined、function和symbol时会自动将其忽略,在数组中则会返回null
浅拷贝问题就少得多了,因此ES6定义了Object.assign()方法来实现浅拷贝。第一个参数是目标对象,之后的参数可以是一个或者多个源对象。它会遍历一个或多个源对象的所有可枚举的自有键,并把他们复制(使用=操作符赋值)到目标对象,最终返回目标对象
less
var newObj = Object.assign({},myObject)
2.属性描述符
从ES5开始,所有的属性都具备了属性描述符
javascript
var obj = {
a:2
}
Object.getOwnPropertyDescriptor(obj,'a')
//{ value: 2,
// writable: true,
// enumerable: true,
// configurable: true
//}
可以看到这个普通的对象属性对应的属性描述符(也叫数据描述符),writable(可写),enumerable(可枚举),configurable(可配置)。在创建属性时属性描述符会使用默认值,我们可以通过Object.defineProperty()来添加一个新属性或者修改一个已有属性(如果他是configurable)并对特性进行设置
php
Object.defineProperty(obj,'a',{
value:3,
writable:true,
configurable:true,
enumerable:true
})
3.Get
当我们执行obj.a时,是进行了一次属性访问。他的运行逻辑是:首先在obj对象中查找是否有相同名称的属性,如果有的话就返回这个属性的值,如果obj上没有这个属性值,就会去查找原型链,直到找到了原型链的尽头,如果没有这个属性就会返回undefined
4.Put(赋值操作)
当我们使用Obj.a = 2时,就会触发对象的put操作(即赋值操作)如果对象中存在a这个属性,那么put算法就会检查下面的内容
- 属性是否是访问描述符?如果是并且存在setter就调用setter
- 属性的数据描述符中writable是否为false,如果是,在非严格模式下静默失败,在严格模式下抛出TypeError异常
- 如果都不是,将该值设置为属性的值
5.Getter和Setter
对象默认的Put操作和Get操作分别可以控制属性值的设置和获取。
在ES5中可以使用getter和setter改写默认操作,但是只能应用在单个属性上。getter是一个隐藏函数,会在获取属性值时调用。setter也是一个隐藏函数,会在设置属性值时调用。
当你给属性定义getter、setter或者两者都有时,这个属性会被定义为"访问描述符"(与数据描述符相对)。对于访问描述符来说,JavaScript会忽略他们的value和writable特性,取而代之关心的是set和get(还有configurable和enumerable)
kotlin
var myObject = {
// 给a定义一个getter
get a(){
return aa
},
set a(val){
this.aa = val * 2
}
}
Object.defineProperty(
myObject,
'b',
{
get:function(){return this.bb * 2},
set:function(val){this.bb = val},
enumerable:true
}
)
无论是对象文字中的get a(){},还是在defineProperty()中显式定义,二者都会创建一个包含值的属性,对于这个属性的访问会自动调用一个隐藏函数,他的返回值会被当成属性访问的返回值。
因此,对象中属性不一定包含值--他们可能是具备getter和setter的访问描述符