深入解析 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. 内对象不受作用域限制,可灵活管理
相关推荐
浪裡遊2 小时前
Sass详解:功能特性、常用方法与最佳实践
开发语言·前端·javascript·css·vue.js·rust·sass
夏梦春蝉3 小时前
ES6从入门到精通:常用知识点
前端·javascript·es6
我想说一句3 小时前
当饼干遇上代码:一场HTTP与Cookie的奇幻漂流 🍪🌊
前端·javascript
南屿im3 小时前
基于 Promise 封装 Ajax 请求:从 XMLHttpRequest 到现代化异步处理
前端·javascript
杨进军3 小时前
前端线上问题的那些事儿
前端·javascript·前端框架
每天开心3 小时前
深入探索 React Hooks: useState 与 useEffect 的力量 🌟
前端·javascript·ai编程
卸任3 小时前
性能优化大作战:React.memo 在可编辑列表中的奇效
前端·javascript·react.js
WildBlue3 小时前
阮一峰闭包:JavaScript最优雅的"背包"魔法!✨
前端·javascript
Dream耀3 小时前
React Hooks 指南:useState 与 useEffect 的用法与技巧
前端·javascript·react.js
zhanshuo3 小时前
i18next + 原生JS 双引擎:打造前端多语言系统最佳实践
前端·javascript·css