对象的一些理解

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算法就会检查下面的内容

  1. 属性是否是访问描述符?如果是并且存在setter就调用setter
  2. 属性的数据描述符中writable是否为false,如果是,在非严格模式下静默失败,在严格模式下抛出TypeError异常
  3. 如果都不是,将该值设置为属性的值

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的访问描述符

相关推荐
掘金者阿豪34 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen1 小时前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员2 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger3 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4533 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css