1. JavaScript 核心概念深度解析
1.1 原型链机制与继承实现
原型链是JavaScript实现继承的核心机制。让我们通过一个实际案例来理解它的运作原理:
javascript复制function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a noise.`);
};
function Dog(name) {
Animal.call(this, name); // 调用父类构造函数
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = new Dog('Rex');
dog.speak(); // "Rex makes a noise."
在这个例子中,我们实现了以下原型链关系:
dog.__proto__→Dog.prototypeDog.prototype.__proto__→Animal.prototypeAnimal.prototype.__proto__→Object.prototypeObject.prototype.__proto__→null
关键点:当访问对象的属性或方法时,JavaScript会沿着原型链向上查找,直到找到该属性或到达原型链末端(null)。
1.2 作用域与闭包实战应用
闭包是JavaScript中极其重要的概念,它允许函数访问并记住其词法作用域,即使函数在其词法作用域之外执行。下面是一个实用的缓存函数实现:
javascript复制function createCache() {
const cache = {};
let hitCount = 0;
return {
get: (key) => {
hitCount++;
return cache[key];
},
set: (key, value) => {
cache[key] = value;
},
stats: () => `Cache hits: ${hitCount}`,
clear: () => {
for (const key in cache) {
delete cache[key];
}
hitCount = 0;
}
};
}
const myCache = createCache();
myCache.set('user', {name: 'Alice'});
console.log(myCache.get('user')); // {name: 'Alice'}
console.log(myCache.stats()); // "Cache hits: 1"
这个例子展示了闭包的几个关键特性:
- 内部函数可以访问外部函数的变量(cache和hitCount)
- 这些变量在外部函数执行完毕后仍然存在
- 可以通过返回的对象方法控制对这些变量的访问
1.3 this绑定规则全面解析
JavaScript中this的绑定规则有四种主要情况,我们通过实例来理解:
javascript复制// 1. 默认绑定(非严格模式)
function showThis() {
console.log(this);
}
showThis(); // 浏览器中指向window,Node.js中指向global
// 2. 隐式绑定
const person = {
name: 'Bob',
greet: function() {
console.log(`Hello, ${this.name}`);
}
};
person.greet(); // "Hello, Bob" - this指向调用对象
// 3. 显式绑定
function introduce(lang) {
console.log(`I speak ${lang}. My name is ${this.name}`);
}
const user = {name: 'Charlie'};
introduce.call(user, 'English'); // 使用call
introduce.apply(user, ['Spanish']); // 使用apply
const boundFunc = introduce.bind(user, 'French'); // 使用bind
boundFunc();
// 4. new绑定
function Person(name) {
this.name = name;
}
const alice = new Person('Alice');
console.log(alice.name); // "Alice"
注意事项:箭头函数不遵循这四种规则,它的this由外层作用域决定,且无法通过call/apply/bind修改。
1.4 事件循环与异步编程
现代JavaScript应用严重依赖异步编程,理解事件循环至关重要。我们来看一个综合示例:
javascript复制console.log('Script start');
setTimeout(() => {
console.log('setTimeout 1');
Promise.resolve().then(() => console.log('Promise in setTimeout 1'));
}, 0);
setTimeout(() => {
console.log('setTimeout 2');
Promise.resolve().then(() => console.log('Promise in setTimeout 2'));
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1');
Promise.resolve().then(() => console.log('Promise nested'));
});
Promise.resolve().then(() => console.log('Promise 2'));
console.log('Script end');
/* 输出顺序:
Script start
Script end
Promise 1
Promise 2
Promise nested
setTimeout 1
Promise in setTimeout 1
setTimeout 2
Promise in setTimeout 2
*/
执行过程解析:
- 执行同步代码(Script start/end)
- 执行微任务队列(Promise回调)
- 执行第一个宏任务(setTimeout 1)及其微任务
- 执行第二个宏任务(setTimeout 2)及其微任务
2. 网络协议核心知识
2.1 HTTPS安全机制详解
HTTPS通过TLS/SSL协议提供安全通信,其核心过程包括:
-
握手阶段:
- 客户端发送ClientHello(支持的加密算法、随机数等)
- 服务器响应ServerHello(选择的加密算法、随机数、证书)
- 客户端验证证书,生成预主密钥并用证书公钥加密发送
- 双方根据随机数和预主密钥生成会话密钥
-
加密通信:
- 使用对称加密算法(如AES)加密数据
- 使用消息认证码(MAC)确保数据完整性
- 定期更换会话密钥提高安全性
实际开发中,建议使用现代加密套件,如TLS 1.3支持的AES-256-GCM和ECDHE密钥交换。
2.2 HTTP状态码应用场景
状态码是HTTP响应的重要组成部分,以下是开发中常用的状态码及其适用场景:
| 状态码 | 含义 | 典型应用场景 |
|---|---|---|
| 200 | OK | 成功获取资源 |
| 201 | Created | 成功创建资源(POST请求) |
| 204 | No Content | 成功处理请求但无返回内容(如DELETE) |
| 301 | Moved Permanently | 永久重定向(域名迁移) |
| 302 | Found | 临时重定向(登录后跳转) |
| 304 | Not Modified | 资源未修改(缓存有效) |
| 400 | Bad Request | 请求参数错误 |
| 401 | Unauthorized | 未认证(需要登录) |
| 403 | Forbidden | 无权限访问 |
| 404 | Not Found | 资源不存在 |
| 429 | Too Many Requests | 请求过于频繁(限流) |
| 500 | Internal Server Error | 服务器内部错误 |
| 502 | Bad Gateway | 网关错误 |
| 503 | Service Unavailable | 服务不可用(维护中) |
2.3 HTTP方法安全性与幂等性
理解HTTP方法的安全性和幂等性对设计RESTful API至关重要:
| 方法 | 安全性 | 幂等性 | 典型用途 |
|---|---|---|---|
| GET | 是 | 是 | 获取资源 |
| HEAD | 是 | 是 | 获取资源元信息 |
| POST | 否 | 否 | 创建资源或触发处理 |
| PUT | 否 | 是 | 完整更新资源(全量替换) |
| PATCH | 否 | 否 | 部分更新资源 |
| DELETE | 否 | 是 | 删除资源 |
关键概念:
- 安全性:请求不应修改服务器状态(只读操作)
- 幂等性:多次相同请求与单次请求效果相同
3. 设计模式实现与优化
3.1 发布-订阅模式高级实现
在前面的基础实现上,我们可以增加更多实用功能:
javascript复制class EnhancedEventEmitter {
constructor() {
this.events = {};
this.maxListeners = 10; // 默认最大监听器数量
}
on(eventName, listener) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
// 检查监听器数量限制
if (this.events[eventName].length >= this.maxListeners) {
console.warn(`Possible memory leak detected. ${this.events[eventName].length} ${eventName} listeners added.`);
}
this.events[eventName].push(listener);
return this; // 支持链式调用
}
emit(eventName, ...args) {
const listeners = this.events[eventName];
if (!listeners || listeners.length === 0) return false;
// 创建副本避免修改影响
const handlers = listeners.slice();
handlers.forEach(listener => {
try {
listener.apply(this, args);
} catch (err) {
console.error(`Error in ${eventName} handler:`, err);
}
});
return true;
}
off(eventName, listener) {
if (!this.events[eventName]) return this;
if (!listener) {
// 如果没有提供具体监听器,移除所有
this.events[eventName] = [];
} else {
this.events[eventName] = this.events[eventName].filter(
l => l !== listener && l.original !== listener
);
}
return this;
}
once(eventName, listener) {
const onceWrapper = (...args) => {
listener.apply(this, args);
this.off(eventName, onceWrapper);
};
// 保存原始引用以便移除
onceWrapper.original = listener;
this.on(eventName, onceWrapper);
return this;
}
prependListener(eventName, listener) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].unshift(listener);
return this;
}
prependOnceListener(eventName, listener) {
const onceWrapper = (...args) => {
listener.apply(this, args);
this.off(eventName, onceWrapper);
};
onceWrapper.original = listener;
this.prependListener(eventName, onceWrapper);
return this;
}
listenerCount(eventName) {
const listeners = this.events[eventName];
return listeners ? listeners.length : 0;
}
setMaxListeners(n) {
this.maxListeners = n;
return this;
}
}
这个增强版实现了:
- 错误处理(避免单个监听器崩溃影响整体)
- 内存泄漏警告
- 链式调用支持
- 前置监听器
- 监听器计数
- 最大监听器限制
3.2 观察者模式与发布-订阅模式对比
虽然两者都用于处理对象间的一对多依赖关系,但有重要区别:
| 特性 | 观察者模式 | 发布-订阅模式 |
|---|---|---|
| 耦合度 | 较高(直接引用) | 较低(通过事件通道) |
| 关系 | 目标直接维护观察者列表 | 发布者和订阅者不知道彼此存在 |
| 通知方式 | 目标直接调用观察者方法 | 通过事件总线/通道传递消息 |
| 动态关系 | 运行时难以修改关系 | 可动态添加/移除订阅关系 |
| 典型实现 | 主题Subject + 观察者Observer | 事件发射器EventEmitter |
| 适用场景 | 组件间强关联 | 模块间松耦合通信 |
观察者模式示例:
javascript复制class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log('Received data:', data);
}
}
const subject = new Subject();
const observer1 = new Observer();
const observer2 = {
update: (data) => console.log('Observer2 got:', data)
};
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('Hello observers!');
4. 常见问题与性能优化
4.1 原型链相关陷阱
问题1:原型污染
javascript复制// 恶意代码可能修改原生原型
Array.prototype.push = function() {
console.log('Hacked!');
};
// 所有数组实例都会受影响
const arr = [1, 2, 3];
arr.push(4); // 输出"Hacked!"而不是实际添加元素
防御方案:
- 使用
Object.freeze()保护原生原型javascript复制Object.freeze(Array.prototype); - 使用ES6的
class语法而非直接操作原型 - 考虑使用
Object.create(null)创建无原型对象
问题2:原型链查找性能
过长的原型链会影响属性查找性能。解决方案:
- 尽量扁平化原型链
- 对于频繁访问的属性,可缓存到局部变量
- 使用
hasOwnProperty检查自有属性
4.2 闭包内存管理
闭包可能导致内存泄漏的常见场景:
javascript复制function setupHugeClosure() {
const largeData = new Array(1000000).fill('*'); // 大数组
return function() {
// 即使外部不需要largeData,它仍被保留
console.log('Closure executed');
};
}
const closure = setupHugeClosure();
// largeData无法被GC回收,即使我们不再需要它
优化方案:
- 明确释放不再需要的引用
javascript复制function cleanClosure() { closure = null; // 允许GC回收闭包环境 } - 避免在闭包中保留不需要的大对象
- 使用WeakMap/WeakSet存储非必需引用
4.3 事件循环性能优化
问题:阻塞事件循环
javascript复制function blockingOperation() {
// 同步阻塞操作
const end = Date.now() + 5000;
while (Date.now() < end) {}
console.log('Blocking operation done');
}
setTimeout(() => console.log('Timeout 1'), 0);
blockingOperation();
setTimeout(() => console.log('Timeout 2'), 0);
// 输出顺序:Blocking operation done → Timeout 1 → Timeout 2
// 两个timeout都被延迟执行
解决方案:
- 将CPU密集型任务拆分为小块
javascript复制async function nonBlockingOperation() { const chunkSize = 100; for (let i = 0; i < 1000; i += chunkSize) { // 处理一小块数据 processChunk(i, Math.min(i + chunkSize, 1000)); // 让出事件循环 await new Promise(resolve => setImmediate(resolve)); } } - 使用Worker线程处理CPU密集型任务
- 优化算法复杂度
4.4 HTTP性能优化实践
1. 连接复用(Keep-Alive)
- 启用HTTP持久连接减少TCP握手开销
- 合理设置keep-alive超时时间
2. 压缩与缓存
http复制Accept-Encoding: gzip, deflate, br
Cache-Control: max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
3. 资源合并与分片
- 合并小文件减少请求数(适合HTTP/1.1)
- HTTP/2下更适合分片(多路复用)
4. CDN加速
- 静态资源使用CDN分发
- 设置合适的缓存策略
5. 手写代码进阶练习
5.1 实现Promise核心功能
javascript复制class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(cb => cb(this.value));
};
const reject = (reason) => {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb(this.reason));
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
queueMicrotask(() => {
try {
if (typeof onFulfilled !== 'function') {
resolve(this.value);
} else {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
}
} catch (err) {
reject(err);
}
});
};
const handleRejected = () => {
queueMicrotask(() => {
try {
if (typeof onRejected !== 'function') {
reject(this.reason);
} else {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
}
} catch (err) {
reject(err);
}
});
};
if (this.state === 'fulfilled') {
handleFulfilled();
} else if (this.state === 'rejected') {
handleRejected();
} else {
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected'));
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
5.2 实现函数柯里化
javascript复制function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
// 使用示例
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6
5.3 实现深拷贝(处理循环引用)
javascript复制function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (hash.has(obj)) {
return hash.get(obj);
}
let clone;
if (obj instanceof Date) {
clone = new Date(obj);
} else if (obj instanceof RegExp) {
clone = new RegExp(obj);
} else if (Array.isArray(obj)) {
clone = [];
hash.set(obj, clone);
obj.forEach((item, index) => {
clone[index] = deepClone(item, hash);
});
} else {
clone = Object.create(Object.getPrototypeOf(obj));
hash.set(obj, clone);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
}
return clone;
}
// 测试循环引用
const obj = { a: 1 };
obj.self = obj;
const cloned = deepClone(obj);
console.log(cloned.self === cloned); // true