JavaScript——对象
对象基础认知
- JS数据类型划分
- 简单类型(基本类型):数字、字符串、布尔值、 null 、 undefined
特点:看似像对象、拥有方法,但本身不可变 - 引用类型(全都是对象):数组、函数、正则、普通对象等
特点:可变的键控集合(key-value),可自由增删属性
- 对象核心特性
- 对象是属性的容器,每个属性由属性名 + 属性值组成
- 属性名可以是任意字符串;属性值可以是任意类型(除 undefined )
- JS是无类(class-free)语言,无需提前定义结构,可动态新增属性
- 对象支持嵌套,天然适合表示树形、图形类复杂数据结构
- 原型链:JS特有继承机制,减少初始化时间与内存消耗
对象字面量
- 定义
对象字面量是用一对大括号 {} 创建对象的便捷语法,可直接定义多个 名/值 对。
- 语法规则
(1)属性名规则
- 合法JS标识符:可省略引号(如 first_name: “xxx” )
- 含 - 、空格等特殊字符:必须加引号(如 “first-name”: “xxx” )
(2) 属性值:可以是任意表达式,支持对象嵌套
示例:
var flight = {
airline: "Oceanic",
number: 815,
departure: { IATA: "SYD", time: "2004-09-22 14:55" }//嵌套
};
检索
(1)两种取值语法
- 点语法 . :仅支持合法标识符,可读性更强(优先使用)
flight.departure.IATA
- 中括号语法 [ ] :支持任意字符串属性名
stooge["first-name"]
(2)取值规则
1.访问不存在的属性,返回 undefined
2.短路运算符的两个经典用法
- || 给属性设置默认值
let status = flight.status || "unknown";
上面的语句可以理解为status 要么是 flight.status,要么是 “unknow”
因为 || 本身就代表 或者的意思
- && 规避 undefined 取值报错(防止 TypeError )
如果你检索到了一个undefined 的值就会抛出TypeError异常,而 && 就能避免错误
这就类似于Java访问到了一个空链表的next,此时的链表已经是空,再访问null的next,编译系统完全无法识别应该去往哪里,就会抛出异常
flight.equipment//它的取值类型是 undefined
flight.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) 两种场景示例
- 多个变量引用同一个对象
let x = stooge;
x.nickname = "Curly";
let nick = stooge.nickname
// x.nickname 和 nick 都指向 “Curly”
- 链式赋值:所有变量指向同一个空对象
var a = {}, b = {}, c = {}; // 三个独立空对象
a = b = c = {}; // a、b、c 都指向同一个空对象,此时a改变了 b也会改变
原型 prototype
(1)原型基础概念
- 所有字面量创建的对象,原型默认指向 Object.prototype
- 原型的核心作用:实现属性委托继承,子类对象可复用原型的属性,节省内存
(2) 手写原型继承: Object.beget (底层原理)
if (typeof Object.beget !== 'function') {
Object.beget = function (o) {
var F = function () {}; // 空构造函数,充当“桥梁”
F.prototype = o; // 将构造函数原型指向传入的原型对象
return new F(); // 返回新实例,实例原型自动绑定 o
};
}
(3)原型核心特性
- 更新时:原型连接不生效(写屏蔽读继承)
给子对象新增同名属性,只会修改自身,完全不影响原型对象
var another_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 (name in another_stooge) {
// 只遍历自身属性,排除原型继承的方法
if (typeof another_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
- 不能直接遍历普通对象(普通对象不是可迭代对象)
示例
const arr = [10, 20, 30];
for (let value of arr) {
console.log(value); // 输出:10 20 30
}
删除 delete
(1) delete 运算符规则
- 仅删除对象自身属性,完全不触碰原型链
- 删除后,若原型存在同名属性,会自动浮现原型的属性值
(2) 示例
delete another_stooge.nickname;
// 删除自身属性后,会读取原型 stooge 的 nickname
another_stooge.nickname;
减少全局变量污染
(1) 全局变量的弊端
全局变量过多,易出现命名冲突、代码耦合度高、灵活性变差。
(2) 解决方案:单一全局变量(命名空间)
- 只创建一个顶层全局对象,所有变量、对象都挂载到这个对象上
- 示例
let MYAPP = {}; // 唯一全局变量
MYAPP.stooge = { "first-name": "Joe" };
MYAPP.flight = { airline: "Oceanic" };
(3)优势
大幅降低命名冲突概率,代码结构清晰,可读性更强。
更多推荐
所有评论(0)