对象的一些理解

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

相关推荐
我命由我123455 分钟前
Element Plus - 在 el-select 的每个选项右侧添加按钮
前端·javascript·vue.js·前端框架·ecmascript·html5·js
weixin1997010801610 分钟前
衣联网商品详情页前端性能优化实战
前端·性能优化
技术钻石流13 分钟前
面向“传统程序员”的端到端 10x Vibe Coding 指南(大型需求) - 从面向业务开发转向面向“Agent 员工”开发
前端·后端·ai编程
codingWhat13 分钟前
Electron 入门实战:用一个加法计算器吃透 Electron 核心概念
前端·javascript·electron
张一凡9318 分钟前
easy-model 在任务管理应用中的实际应用
前端·react.js
专业流量卡18 分钟前
React useMemo 源码ai解析
前端
TechFind19 分钟前
AI Agent 开发完整教程:从零到上线的实战指南
java·javascript
进击的尘埃20 分钟前
把 800 行 `index.ts` 拆成 MCP 架构这件事,我踩了不少坑
javascript
Tzarevich27 分钟前
深入理解Event Loop:从原理图到代码实战,小白也能看懂的 JS 执行机制
前端·javascript·面试
毛骗导演34 分钟前
发送一句「你好」,为什么花掉了几千个 Token?——深读 OpenClaw 的 Context 注入机制
前端·架构