JavaScript——对象
目录
- 对象基础认知
- 对象字面量
- 检索
- 更新
- 引用
- 原型 prototype
- 反射
- 枚举 for
- 删除 delete
- 减少全局变量污染
对象基础认知
- JS数据类型划分
- 简单类型(基本类型):数字、字符串、布尔值、 null 、 undefined
特点:看似像对象、拥有方法,但本身不可变 - 引用类型(全都是对象):数组、函数、正则、普通对象等
特点:可变的键控集合(key-value),可自由增删属性
- 对象核心特性
- 对象是属性的容器,每个属性由属性名 + 属性值组成
- 属性名可以是任意字符串;属性值可以是任意类型(除 undefined )
- JS是无类(class-free)语言,无需提前定义结构,可动态新增属性
- 对象支持嵌套,天然适合表示树形、图形类复杂数据结构
- 原型链:JS特有继承机制,减少初始化时间与内存消耗
对象字面量
- 定义
对象字面量是用一对大括号 {}创建对象的便捷语法,可直接定义多个 名/值 对。
- 语法规则
(1)属性名规则
- 合法JS标识符:可省略引号(如 first_name: “xxx” )
- 含 - 、空格等特殊字符:必须加引号(如 “first-name”: “xxx” )
(2) 属性值:可以是任意表达式,支持对象嵌套
示例:
varflight={airline:"Oceanic",number:815,departure:{IATA:"SYD",time:"2004-09-22 14:55"}//嵌套};检索
(1)两种取值语法
- 点语法.:仅支持合法标识符,可读性更强(优先使用)
flight.departure.IATA- 中括号语法[ ]:支持任意字符串属性名
stooge["first-name"](2)取值规则
1.访问不存在的属性,返回 undefined
2.短路运算符的两个经典用法
- || 给属性设置默认值
letstatus=flight.status||"unknown";上面的语句可以理解为status 要么是 flight.status,要么是 “unknow”
因为 || 本身就代表 或者的意思
- && 规避 undefined 取值报错(防止 TypeError )
如果你检索到了一个undefined 的值就会抛出TypeError异常,而 && 就能避免错误
这就类似于Java访问到了一个空链表的next,此时的链表已经是空,再访问null的next,编译系统完全无法识别应该去往哪里,就会抛出异常
flight.equipment//它的取值类型是 undefinedflight.equipment.model// throw “TypeError”flight.equipment&&flight.equipment.model为什么 && 能避免报错呢?
本质上是让编译系统先判断 flight.equipment.model 的上一层 flight.equipment 是否为空,如果上一层已经为null了,那就不需要再执行下一个判断,继续运行下一条语句了
更新
(1)赋值规则
属性已存在:赋值直接覆盖原有值
属性不存在:赋值会动态新增该属性(JS对象可动态扩展)
(2) 示例
// 覆盖已有属性stooge['first-name']='Jerome';// 动态新增属性stooge.nickname='Curly';引用
(1)核心规则
对象通过引用传递,永远不会被拷贝,多个变量指向同一个对象时,修改任意一个变量,所有变量都会同步变化。
(2) 两种场景示例
- 多个变量引用同一个对象
letx=stooge;x.nickname="Curly";letnick=stooge.nickname// x.nickname 和 nick 都指向 “Curly”- 链式赋值:所有变量指向同一个空对象
vara={},b={},c={};// 三个独立空对象a=b=c={};// a、b、c 都指向同一个空对象,此时a改变了 b也会改变原型 prototype
(1)原型基础概念
- 所有字面量创建的对象,原型默认指向 Object.prototype
- 原型的核心作用:实现属性委托继承,子类对象可复用原型的属性,节省内存
(2) 手写原型继承: Object.beget (底层原理)
if(typeofObject.beget!=='function'){Object.beget=function(o){varF=function(){};// 空构造函数,充当“桥梁”F.prototype=o;// 将构造函数原型指向传入的原型对象returnnewF();// 返回新实例,实例原型自动绑定 o};}(3)原型核心特性
- 更新时:原型连接不生效(写屏蔽读继承)
给子对象新增同名属性,只会修改自身,完全不影响原型对象
varanother_stooge=Object.beget(stooge);another_stooge["first-name"]="Harry";// 仅修改自身,stooge 完全不变- 检索时:原型链向上委托
读取属性时,先查自身,没有就顺着原型链向上找,直到 Object.prototype ;全程找不到则返回 undefined - 原型动态性:给原型新增属性,所有基于该原型创建的对象会立刻继承该属性
stooge.profession='actor';another_stooge.profession;// 自动读取原型属性,结果为 'actor'反射
(1) 定义
反射:动态检测对象拥有哪些属性,核心工具为typeof和hasOwnProperty。
(2) typeof 运算符
作用:检测属性值的类型,但会把原型链上继承的方法(如 toString 、 constructor )一并识别为 function。
(3) hasOwnProperty
作用:判断属性是否为对象自身独有,完全不检查原型链
返回值: true = 自身属性; false = 继承属性 / 不存在
示例
flight.hasOwnProperty('number');// true(自身属性)flight.hasOwnProperty('constructor');// false(原型继承属性)枚举 for
(1) for…in 遍历对象
- 作用:遍历对象所有属性名(key)
- 致命缺陷:会遍历原型链上继承的属性、方法,属性遍历顺序不固定
- 标准写法:必须搭配 hasOwnProperty 过滤原型属性
for(nameinanother_stooge){// 只遍历自身属性,排除原型继承的方法if(typeofanother_stooge[name]!=='function'){document.writeln(name+': '+another_stooge[name]);}}(2)替代方案(推荐)
手动定义属性数组 + 普通 for 循环,顺序可控、不触碰原型链,更稳定可靠。
即:for(let i=0 ; i<nums.length ; i++)
这里再补充一个for…of
for…of (遍历值,专门给数组/可迭代对象用)
作用:ES6 新增,专门遍历数组、Map、Set、字符串等可迭代对象,拿值(value)。
特点:
- 遍历值本身,不是下标
- 完全不碰原型链,安全干净
- 可以配合 break 、 continue
- 不能直接遍历普通对象(普通对象不是可迭代对象)
示例
constarr=[10,20,30];for(letvalueofarr){console.log(value);// 输出:10 20 30}删除 delete
(1) delete 运算符规则
- 仅删除对象自身属性,完全不触碰原型链
- 删除后,若原型存在同名属性,会自动浮现原型的属性值
(2) 示例
deleteanother_stooge.nickname;// 删除自身属性后,会读取原型 stooge 的 nicknameanother_stooge.nickname;减少全局变量污染
(1) 全局变量的弊端
全局变量过多,易出现命名冲突、代码耦合度高、灵活性变差。
(2) 解决方案:单一全局变量(命名空间)
- 只创建一个顶层全局对象,所有变量、对象都挂载到这个对象上
- 示例
letMYAPP={};// 唯一全局变量MYAPP.stooge={"first-name":"Joe"};MYAPP.flight={airline:"Oceanic"};(3)优势
大幅降低命名冲突概率,代码结构清晰,可读性更强。
