深入解析 JavaScript 对象操作

引言

在JavaScript 中,万物皆对象。理解对象的创建、属性操作、构造函数的使用,以及内存管理机制,对于编写高效、可靠的代码至关重要。这些核心概念不仅影响着代码的结构和性能,还决定了程序的行为和资源的利用效率。本文将深入探讨这些关键知识点,帮助开发者掌握 JavaScript 对象的本质和内存管理的奥秘。


创建对象的方式

  1. 字面量 ,示例如下
js 复制代码
var obj = { 
    name: "fz",
    age: 18,
}
  1. new Object() ,示例如下
js 复制代码
var obj = new Object()
obj.name = name
obj.age  = age
  1. new 自定义的构造函数 ,示例如下
js 复制代码
var obj2 = new Person()
function Person(name,age){
this.name = name
this.age  = age
}

访问对象属性和方法

  1. 点符号(.) :obj.name
  2. 方括号符号([]) :obj['age']
    需要注意的是,使用方括号符号时,括号内的内容是字符串(表示属性名)或者是一个变量(该变量存储着属性名的字符串)。 如果直接使用obj[age],则没有将age属性名作为字符串,而是作为变量。由于在全局作用域中并没有定义名为age的变量,因此会抛出age is not defined的错误。正确的方式是使用字符串'age',即obj['age']
    如果要使用变量,应先定义一个变量,并将属性名字符串赋值给它。
js 复制代码
var propertyName = 'age';
console.log(obj[propertyName]); // 输出: 18

对象属性操作的基本行为

在对象属性操作中,新增、修改和删除是三种基本行为。对象的新增 是为对象添加原本不存在的属性和值,修改 是更改对象中已存在属性的值,删除 是通过使用delete运算符从对象中移除指定属性及对应的值。

js 复制代码
 obj.sex = 'boy' // 新增
 obj['age'] = 19 // 修改
 delete obj.age // 删除

因此,我们可以总结对象中的键是唯一的,每个键对应一个值,不会重复,即:

  1. 同一对象中,不存在两个相同名称(key)的属性;
  2. 当我们给一个已经存在的属性名赋值时,实际上是修改操作,而不是新增;
  3. 如果使用一个已经存在的属性名进行新增操作,实际上会覆盖原有的值(即修改)。

构造函数

  • 当需要批量化创建对象时,使用构造函数。
  • 当一个函数被 new 调用时,我们就称之为构造函数。
示例 复制代码
function Car(color){
    this.name = 'su7-Ultra'
    this.height = '1400'
    this.lang = '4800'
    this.weight = 1500
    this.color = color
}
var car1 = new Car('orange'); // 实例化一个对象
var car2 = new Car('pink');

car1.name = '劳斯莱斯';

通过构造函数来创建的多个(实例化)对象之间是相互独立的。

示例1 复制代码
function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}

var p1 = new Person('zs',18,'男');
示例2 复制代码
function Person(name,age,sex){
    var _this = {}
    _this.name = name;
    _this.age = age;
    _this.sex = sex;
    return _this;
}

var p1 = Person('zs',18,'男');

对比上述两份代码,我们能够清晰了解 new 做了什么

  1. 创建了一个 this 对象;
  2. 执行构造函数中的代码,给 this 对象添加属性和方法;
  3. 返回 this 对象。

接着,我们思考一下下述代码是否合理?

dart 复制代码
var num = 123 // new Number(123)
num.a = 'aaa'
console.log(num + 1); //  输出:124
console.log(num.a); // 输出:undefined

var str = 'hello' // new String('hello')
console.log(str.length); // 输出 :5 | 没有人为在对象增加属性,length是字符串独有的属性(继承)

由于原始类型一定不能添加属性和方法,属性和方法是对象独有的 ,那么为什么上述代码没有报错?是因为V8引擎在执行代码赋值给变量时,会自动推断出其类型,并将其默认执行成一个对象。但由于用户定义的是一个原始类型,违背了用户设定的初衷,v8引擎就会想尽一切办法将其还原成原始类型,即移除对象身上添加的属性delete num.a

包装类

  • 用户定义的字面量,会被包装成对象 (比如: new Number());
  • 一个包装类的实例对象在参与运算时,会被自动拆箱成原始类型即读取内部的原始值(比如: number);
  • 因为 js 是弱类型的语言,所以只有在赋值语句时才会判断值的类型,当值被判定为原始类型时,就会自动将包装对象上添加的属性移除。

考点

ini 复制代码
var arr = [1,2,3,4,5] // new Array(1,2,3,4,5)
arr.length = 2
console.log(arr); // 输出:[1,2]

var str = 'abcd' // new String('abcd')
str.length = 2 
console.log(str.length); // 输出:4 

思考一下上述代码运行为什么有差异?

在 JavaScript 中,数组本质上是一种特殊对象,其长度(length)属性与数组元素的索引紧密相关,直接给数组的length属性赋值,会改变数组的长度。

字符串在 JavaScript 中是原始类型,但它也可以通过new String('abcd')被包装成对象,字符串对象的length属性是只读的,并且是从字符串的原型对象继承而来的。因此当尝试给字符串的length属性赋值时,不会改变字符串的实际长度。


拓展小知识

思考一下下述代码是否合理?

css 复制代码
const a = {
    b : 1
}
a.b = 2

答案是合理的。
a是一个变量,位于调用栈 的变量环境中,存储的是对象的引用地址{ b: 1 } 作为引用类型,存储在堆内存 中。const 保证的是变量绑定的不可变性 ,对于原始类型(如 number),值直接保存在栈中,不可修改;对于引用类型,变量存储的是引用地址const 保证的是这个地址不变,而非对象内部状态不变。

V8引擎的执行操作

原始类型数据存储在栈中,引用类型数据存储在堆中。使用const声明的变量,其值不能被重新赋值,但如果是引用类型,则可通过修改引用地址指向的内容来更新数据。

代码 引擎行为 内存区域
const a = ... 在栈中创建 a,存储对象的引用地址 调用栈
{ b: 1 } 在堆中分配对象空间,存储属性值 堆内存
a.b = 2 通过引用地址找到堆中对象,修改属性值 堆内存更新

显然,变量 a 存储的引用地址 #001 从未改变 ,因此不违反 const 约束。

V8 引擎的内存结构

在 JavaScript 执行过程中,V8 引擎将内存分为两个主要区域。

内存区域 存储内容 特点
调用栈 原始类型值(number, string, boolean 等)和引用地址 大小固定,访问快速
堆内存 引用类型值(对象、数组等) 大小动态可变,访问稍慢

对象存储在堆内存的原因

  1. 对象属性数量不确定,内存需求动态变化
  2. 栈内存空间有限且要求固定大小数据
  3. 堆内存适合存储较大的动态数据结构
  4. 内对象不受作用域限制,可灵活管理
相关推荐
doubleZ10 分钟前
使用Trae从零开发一个跳转顶部的Chrome插件
前端·javascript
用户25191624271113 分钟前
ES6之类:构造函数的语法糖
javascript·ecmascript 6
大码猴25 分钟前
是时候该用自动化工具玩玩12306了
前端·javascript·后端
来碗盐焗星球1 小时前
实时通信技术开发经历
前端·javascript
就是我1 小时前
【React Hook】深入浅出:10分钟理解useContext
前端·javascript·react.js
十年砍柴---小火苗1 小时前
vue的created和mounted区别
javascript·vue.js·ecmascript
rookiefishs1 小时前
javascript如何实现手势🤚🏻的检测与识别?🤨
javascript
lens941 小时前
Oxlint:干掉ESLint卡顿!前端火箭级代码检查器来了!
前端·javascript
RePeaT1 小时前
JavaScript切换元素显示隐藏的方法
前端·javascript
bitbitDown1 小时前
同事用了个@vue:mounted,我去官网找了半天没找到
前端·javascript·vue.js