JS笔记(对象、函数、数组)
'# JS笔记(对象、函数、数组)
一、背景与问题
在JavaScript开发中,对象、函数和数组构成了程序的三大核心数据结构。理解它们的底层机制和使用规范,是构建高质量代码的基础。
1.1 核心挑战
- 对象的原型链继承机制
- 函数的闭包与this绑定
- 数组的内存管理与性能优化
- 跨平台兼容性问题(如ES5与ES6的差异)
1.2 常见误区
- 对原型链继承的误解(如认为构造函数直接创建实例)
- 函数参数传递的引用类型问题
- 数组方法的副作用(如mutating原数组)
二、基本原理
2.1 对象的底层机制
2.1.1 原型链结构
JavaScript对象通过原型链实现继承,每个对象都有一个__proto__属性指向原型对象。
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`);
};
const p = new Person("Alice");
p.sayHi(); // Hi, I'm Alice2.1.2 原型链查找机制
当访问对象属性时,会沿着原型链向上查找,直到找到或到达Object.prototype。
2.1.3 构造函数与实例关系
构造函数的prototype属性指向原型对象,而实例的__proto__指向构造函数的prototype。
2.1.4 禁用原型链的解决方案
Object.defineProperty(Person, 'prototype', {
writable: false
});2.2 函数的特殊性
2.2.1 函数对象特性
函数是对象,具有prototype属性,同时具有this绑定。
function add(a, b) {
return a + b;
}
console.log(add.prototype); // {}2.2.2 闭包的内存管理
闭包会捕获外层函数的变量,可能导致内存泄漏。
2.2.3 箭头函数的this绑定
箭头函数没有自己的this,继承自外层作用域。
const obj = {
name: "Alice",
sayHi: function() {
setTimeout(() => {
console.log(this.name); // Alice
}, 100);
}
};2.3 数组的内存模型
2.3.1 数组的底层实现
JavaScript数组是动态数组,基于哈希表实现,支持稀疏性。
const arr = [1, 2, 3];
arr[10] = 100; // 稀疏数组2.3.2 数组方法的性能差异
| 方法 | 时间复杂度 | 特点 |
|---|---|---|
push | O(1) | 可能引发扩容 |
pop | O(1) | 可能引发扩容 |
shift | O(n) | 头部删除 |
unshift | O(n) | 头部插入 |
map | O(n) | 不修改原数组 |
filter | O(n) | 不修改原数组 |
2.3.3 内存管理策略
- 使用
Array.from代替new Array()创建数组 - 避免使用
delete操作符(会留下稀疏性)
三、环境准备
# 安装Node.js环境
npm init -y
npm install --save-dev eslint typescript四、核心实现
4.1 对象的创建与继承
4.1.1 构造函数模式
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a noise`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = new Dog("Buddy", "Golden Retriever");
dog.speak(); // Buddy makes a noise4.1.2 ES6类继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
const dog = new Dog("Buddy", "Golden Retriever");
dog.speak(); // Buddy makes a noise4.2 函数的高级用法
4.2.1 闭包应用
function createCounter() {
let count = 0;
return {
increment: () => count++,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increment();
console.log(counter.getCount()); // 14.2.2 高阶函数应用
function applyOperation(arr, operation) {
return arr.map(operation);
}
const numbers = [1, 2, 3];
const squared = applyOperation(numbers, x => x * x);
console.log(squared); // [1, 4, 9]4.3 数组的高级操作
4.3.1 精确拷贝
const original = [1, 2, 3];
const copy = [...original]; // 浅拷贝
const deepCopy = JSON.parse(JSON.stringify(original)); // 深拷贝4.3.2 性能优化技巧
// 使用Array.from代替new Array
const arr1 = Array.from({length: 10000}, (_, i) => i);
const arr2 = new Array(10000).fill(0).map((_, i) => i);五、完整案例
5.1 用户数据处理系统
5.1.1 项目结构
user-system/
├── src/
│ ├── data/
│ │ └── users.json
│ ├── utils/
│ │ └── arrayUtils.js
│ ├── models/
│ │ └── User.js
│ └── main.js
├── package.json
└── README.md5.1.2 用户数据模型
// src/models/User.js
class User {
constructor(id, name, email, role) {
this.id = id;
this.name = name;
this.email = email;
this.role = role;
}
get isAdministrator() {
return this.role === 'admin';
}
}5.1.3 数组处理工具
// src/utils/arrayUtils.js
export function filterActiveUsers(users) {
return users.filter(user => user.isActive);
}
export function groupBy(users, key) {
return users.reduce((acc, user) => {
const value = user[key];
if (!acc[value]) {
acc[value] = [];
}
acc[value].push(user);
return acc;
}, {});
}5.1.4 主程序
// src/main.js
import { User } from './models/User.js';
import { filterActiveUsers, groupBy } from './utils/arrayUtils.js';
const users = [
new User(1, 'Alice', 'alice@example.com', 'admin', true),
new User(2, 'Bob', 'bob@example.com', 'user', false),
new User(3, 'Charlie', 'charlie@example.com', 'admin', true)
];
const activeUsers = filterActiveUsers(users);
const roleGroups = groupBy(activeUsers, 'role');
console.log(roleGroups);
// 输出: { admin: [User 1], user: [User 2] }六、源码解析
6.1 构造函数的原型链
function Person() {}
Person.prototype = {
constructor: Person,
sayHi: function() { console.log("Hi"); }
};
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true6.2 闭包的内存管理
function createCounter() {
let count = 0;
return {
increment: () => count++,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increment();
console.log(counter.getCount()); // 16.3 数组的内存优化
function optimizeArray(arr) {
const result = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
result[i] = arr[i];
}
return result;
}七、进阶使用
7.1 精确继承实现
function inherit(target, source) {
Object.keys(source).forEach(key => {
target[key] = source[key];
});
}7.2 高阶函数组合
function compose(...fns) {
return function (value) {
return fns.reduceRight((acc, fn) => fn(acc), value);
};
}
const double = x => x * 2;
const addOne = x => x + 1;
const composed = compose(double, addOne);
console.log(composed(3)); // 87.3 数组的内存优化策略
function optimizedArray() {
const arr = new Array(1000000);
for (let i = 0; i < arr.length; i++) {
arr[i] = i;
}
return arr;
}八、性能与工程实践
8.1 数组性能优化
- 使用
Array.from代替new Array - 避免使用
delete操作符 - 使用
TypedArray处理二进制数据
8.2 函数的内存管理
- 避免不必要的闭包
- 使用
WeakMap处理弱引用 - 使用
Symbol作为私有属性
8.3 对象的性能优化
- 使用
Object.freeze防止修改 - 使用
Proxy进行属性拦截 - 避免频繁修改原型链
九、常见问题与踩坑
9.1 常见错误
9.1.1 原型链污染
Object.prototype.__proto__ = { evil: true };9.1.2 闭包内存泄漏
function createClosure() {
const data = [1, 2, 3];
return () => console.log(data);
}9.1.3 数组方法副作用
const arr = [1, 2, 3];
arr.map(x => x * 2).forEach(console.log); // [2, 4, 6]9.2 解决方案
9.2.1 原型链污染防护
Object.defineProperty(Object.prototype, '__proto__', {
writable: false,
configurable: false
});9.2.2 闭包内存优化
function createClosure() {
const data = [1, 2, 3];
return () => {
const copy = data.slice();
console.log(copy);
};
}9.2.3 数组方法安全使用
const arr = [1, 2, 3];
const result = arr.map(x => x * 2);
console.log(result); // [2, 4, 6]十、最佳实践
10.1 对象使用规范
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 需要继承 | ES6类 | 更清晰的结构 |
| 需要动态属性 | Object.assign | 灵活的属性合并 |
| 避免污染 | 禁用原型链 | 防止意外覆盖 |
10.2 函数使用规范
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 需要闭包 | 使用箭头函数 | 简化this绑定 |
| 需要高阶函数 | 使用函数式编程 | 提高可读性 |
| 需要安全调用 | 使用Optional Chaining | 避免空指针 |
10.3 数组使用规范
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 需要性能 | 使用TypedArray | 更高效的内存管理 |
| 需要精确拷贝 | 使用JSON.parse/JSON.stringify | 简单的深拷贝 |
| 需要内存优化 | 使用Array.from | 更高效的内存分配 |
十一、总结
JavaScript的对象、函数和数组是构建现代Web应用的基石。理解它们的底层机制和使用规范,是编写高质量代码的关键。在实际开发中,需要根据具体场景选择合适的实现方式:对象用于封装数据和行为,函数用于封装逻辑,数组用于处理集合数据。通过合理使用闭包、原型链、数组方法等技术,可以构建出高效、可维护的代码。同时,要警惕常见的性能陷阱和安全风险,采用最佳实践来保证代码的健壮性。
评论已关闭