# JavaScript 对象全面解析
## 目录
1. [对象创建方式](#对象创建方式)
2. [创建方式对比](#创建方式对比)
3. [对象原理](#对象原理)
4. [Getter/Setter](#gettersetter)
5. [对象自带方法](#对象自带方法)
6. [常用方法对比](#常用方法对比)
7. [总结](#总结)
---
对象创建方式
1. 对象字面量
const obj = {
name: 'John',
age: 30
};
2. new Object()
const obj = new Object();
obj.name = 'John';
3. 构造函数
function Person(name) {
this.name = name;
}
const john = new Person('John');
4. Object.create()
const protoObj = { x: 10 };
const newObj = Object.create(protoObj);
5. ES6 Class
class Person {
constructor(name) {
this.name = name;
}
}
const john = new Person('John');
6. 工厂函数
function createPerson(name) {
return {
name,
greet() {
console.log(`Hello ${this.name}`);
}
};
}
创建方式对比
| 方式 | 原型链 | 内存效率 | 私有属性支持 | 适用场景 |
|---|---|---|---|---|
| 对象字面量 | Object.prototype | 高 | ❌ | 简单数据存储 |
| new Object() | Object.prototype | 低 | ❌ | 不推荐使用 |
| 构造函数 | 自定义原型 | 中 | ❌ | 需要多个相似实例 |
| Object.create | 指定原型 | 高 | ❌ | 原型继承场景 |
| ES6 Class | 自定义原型 | 中 | ✔️(通过WeakMap) | 面向对象编程 |
| 工厂函数 | Object.prototype | 高 | ✔️(闭包) | 需要封装私有属性 |
对象原理
核心特性
-
原型链机制:通过
__proto__实现继承链 -
属性描述符:
Object.getOwnPropertyDescriptor(obj, 'prop') -
- configurable
- enumerable
- writable
- value
configurable
configurable 属性是一个布尔值,表示该属性是否可以被配置。如果 configurable 为 false,则该属性的描述符(除了 value 和 writable)不能被修改,且该属性不能被删除。
let obj = {};
Object.defineProperty(obj, 'name', {
value: '张三',
configurable: false
});
// 尝试修改属性描述符
Object.defineProperty(obj, 'name', {
value: '李四'
}); // 抛出错误,因为 configurable 为 false
// 尝试删除属性
delete obj.name; // 返回 false,因为 configurable 为 false
enumerable
enumerable 属性是一个布尔值,表示该属性是否可以在枚举操作(如 for...in 循环或 Object.keys())中被枚举。
let obj = {
name: '张三',
age: 25
};
Object.defineProperty(obj, 'email', {
value: 'zhangsan@example.com',
enumerable: false
});
for (let key in obj) {
console.log(key); // 输出:name, age
}
console.log(Object.keys(obj)); // 输出:['name', 'age']
writable
writable 属性是一个布尔值,表示该属性的值是否可以被修改。如果 writable 为 false,则该属性的值不能被修改。
let obj = {};
Object.defineProperty(obj, 'name', {
value: '张三',
writable: false
});
obj.name = '李四'; // 不会修改,因为 writable 为 false
console.log(obj.name); // 输出:张三
value
value 属性是该属性的值。对于数据属性,value 是实际存储的值;对于访问器属性,value 通常不适用。
let obj = {};
Object.defineProperty(obj, 'name', {
value: '张三'
});
console.log(obj.name); // 输出:张三
定义属性描述符
let obj = {};
Object.defineProperty(obj, 'name', {
value: '张三',
writable: false,
enumerable: true,
configurable: false
});
console.log(obj.name); // 输出:张三
obj.name = '李四'; // 不会修改,因为 writable 为 false
console.log(obj.name); // 输出:张三
for (let key in obj) {
console.log(key); // 输出:name
}
delete obj.name; // 返回 false,因为 configurable 为 false
3. this绑定:动态绑定,取决于调用方式(下面会挨个举例说明)
内存分配
- 基本类型:栈内存存储
- 引用类型:堆内存存储,栈存指针
Getter/Setter
定义方式
const obj = {
_value: 0,
get count() {
return this._value;
},
set count(val) {
this._value = val > 0 ? val : 0;
}
};
特性对比
| 特性 | Getter | Setter |
|---|---|---|
| 触发时机 | 访问属性时 | 设置属性时 |
| 返回值 | 必须有返回值 | 无要求 |
| 参数 | 无参数 | 接收赋值参数 |
| 主要用途 | 计算属性/访问控制 | 数据验证/副作用触发 |
对象自带方法
核心方法列表
| 方法 | 作用描述 | 返回值 | 是否修改原对象 |
|---|---|---|---|
| Object.assign(target, ...sources) | 合并对象属性 | 目标对象 | ✔️ |
| Object.keys(obj) | 获取可枚举属性键名数组 | Array | ❌ |
| Object.values(obj) | 获取可枚举属性值数组 | Array | ❌ |
| Object.entries(obj) | 获取键值对数组 | Array | ❌ |
| Object.defineProperty() | 定义/修改属性特性 | 修改后的对象 | ✔️ |
| Object.freeze() | 冻结对象(不可修改) | 冻结对象 | ✔️ |
| Object.seal() | 密封对象(不能增删属性) | 密封对象 | ✔️ |
| Object.create(proto) | 创建指定原型的对象 | 新对象 | ❌ |
| Object.hasOwnProperty() | 检查是否自有属性 | Boolean | ❌ |
| Object.is() | 增强型相等比较 | Boolean | ❌ |
常用方法示例
// 合并对象
const merged = Object.assign({}, {a:1}, {b:2}); // {a:1, b:2}
// 获取键值对
Object.entries({a:1, b:2}); // [['a',1], ['b',2]]
// 属性定义
Object.defineProperty(obj, 'prop', {
value: 42,
writable: false
});
常用方法对比
Object.assign vs 扩展运算符
| 特性 | Object.assign | ...扩展运算符 |
|---|---|---|
| 原型链属性 | 不复制 | 不复制 |
| 嵌套对象 | 浅拷贝 | 浅拷贝 |
| 数组合并 | 不可用 | 支持 |
| 性能 | 较好 | 更优 |
in操作符 vs hasOwnProperty
| 特性 | in操作符 | hasOwnProperty |
|---|---|---|
| 检测范围 | 原型链+自有 | 仅自有属性 |
| 符号属性 | 支持 | 支持 |
| 安全调用 | 需要try-catch | 直接调用 |
选择创建方式的建议
- 简单配置 ➔ 对象字面量
- 需要继承 ➔ Object.create()
- 类体系 ➔ ES6 Class
- 私有属性 ➔ 工厂函数 + 闭包
方法使用指南
- 数据合并:优先使用扩展运算符
... - 属性遍历:
Object.entries()+ 解构 - 属性保护:
Object.freeze()用于常量对象 - 精确比较:
Object.is(NaN, NaN)→ true
最佳实践:优先使用ES6+语法,合理选择对象创建方式,注意深浅拷贝的区别
# JavaScript 中 `this` 绑定的所有场景详解
## 目录
1. [全局上下文](#全局上下文)
2. [函数调用](#函数调用)
3. [对象方法](#对象方法)
4. [构造函数](#构造函数)
5. [箭头函数](#箭头函数)
6. [事件处理函数](#事件处理函数)
7. [call/apply/bind](#callapplybind)
8. [回调函数](#回调函数)
9. [模块作用域](#模块作用域)
10. [类中的 this](#类中的-this)
11. [严格模式](#严格模式)
12. [总结与最佳实践](#总结与最佳实践)
---
## 全局上下文
```javascript
console.log(this); // 浏览器中指向 window 对象 | Node.js 中指向 global 对象
函数调用
普通函数
function showThis() {
console.log(this); // 非严格模式:window/global | 严格模式:undefined
}
showThis();
嵌套函数
const obj = {
method() {
function inner() {
console.log(this); // 非严格模式:window/global | 严格模式:undefined
}
inner();
}
};
obj.method();
对象方法
const obj = {
name: 'Object',
logName() {
console.log(this.name); // 指向 obj 对象
}
};
obj.logName(); // "Object"
// 方法赋值给变量
const fn = obj.logName;
fn(); // 非严格模式:undefined(丢失 this 绑定)
构造函数
function Person(name) {
this.name = name; // this 指向新创建的实例
}
const john = new Person('John');
箭头函数
const obj = {
name: 'Obj',
logName: () => {
console.log(this.name); // 指向外层作用域的 this(此处为 window/global)
}
};
obj.logName(); // undefined(若全局没有 name 属性)
const outerThis = this;
const arrowFn = () => {
console.log(this === outerThis); // true(永远捕获定义时的 this)
};
事件处理函数
button.addEventListener('click', function() {
console.log(this); // 指向触发事件的元素(button)
});
// 使用箭头函数
button.addEventListener('click', () => {
console.log(this); // 指向定义时的外层 this(可能是 window)
});
call/apply/bind
function showThis() {
console.log(this.value);
}
const ctx = { value: 42 };
showThis.call(ctx); // 42
showThis.apply(ctx); // 42
const boundFn = showThis.bind(ctx);
boundFn(); // 42
回调函数
const obj = {
data: 'Hello',
handleData() {
setTimeout(function() {
console.log(this.data); // 非严格模式:undefined(this 指向 window)
}, 100);
}
};
obj.handleData();
// 解决方案
// 1. 使用箭头函数
setTimeout(() => console.log(this.data), 100); // 捕获外层 this
// 2. 保存 this
const self = this;
setTimeout(function() { console.log(self.data) }, 100);
// 3. 使用 bind
setTimeout(function() { console.log(this.data) }.bind(this), 100);
模块作用域
// ES6 模块中顶层 this 指向 undefined(严格模式)
console.log(this); // undefined
类中的 this
class Person {
constructor(name) {
this.name = name; // 指向实例
}
logName() {
console.log(this.name);
}
// 箭头函数方法
arrowLogName = () => {
console.log(this.name); // 始终绑定实例
};
}
const p = new Person('John');
const fn = p.logName;
fn(); // 错误:this 为 undefined(严格模式)
const arrowFn = p.arrowLogName;
arrowFn(); // 正确:"John"
严格模式
"use strict";
function test() {
console.log(this); // undefined
}
test();
总结与最佳实践
this 绑定优先级(从高到低)
- new 绑定:
new Constructor() - 显式绑定:
call/apply/bind - 隐式绑定:
obj.method() - 默认绑定:全局对象或
undefined(严格模式)
避坑指南
- 回调函数中的
this丢失:使用箭头函数或bind - 避免在对象方法中使用箭头函数(除非需要绑定外层
this) - 类方法推荐使用普通函数,需要绑定实例时使用箭头函数属性
- 严格模式下函数调用的
this为undefined
最佳实践
// 安全的对象方法定义
const obj = {
data: 'value',
handler() { /* 使用 this */ }
};
// 需要绑定实例时
class MyClass {
handler = () => {
console.log(this); // 始终指向实例
};
}
// 回调函数处理
button.addEventListener('click', this.handleClick.bind(this));
// 或
button.addEventListener('click', () => this.handleClick());
总结
JavaScript对象是JavaScript学习中重要的一环,和JavaScript数组一样方法诸多需要好好梳理和练习理解的,如弱缺失欢迎广大前端小伙伴建议和补充。每个人观点和出发点不一样但是要到达的终点却是一样的!!!如果想持续不断坚持学习前端知识也可以关注我**公众号:【鱼樱AI实验室】**学习更多的前端知识从0-1梳理总结。。。持续免费分享哦!!!拿来即用即练!!!