Appearance
JavaScript 高级概念
本文探讨JavaScript的高级特性,包括闭包、原型链、异步编程、设计模式等进阶主题。
闭包
闭包是指函数和其周围的词法环境的组合,使函数可以访问其外部作用域中的变量。
javascript
function createCounter() {
let count = 0; // 私有变量
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getValue: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getValue()); // 1闭包的应用场景
- 数据封装和私有变量
javascript
function createPerson(name) {
// 私有变量
let age = 0;
return {
getName: function() {
return name;
},
getAge: function() {
return age;
},
setAge: function(newAge) {
if (newAge > 0 && newAge < 120) {
age = newAge;
}
}
};
}
const person = createPerson("Alice");
person.setAge(25);
console.log(person.getName()); // "Alice"
console.log(person.getAge()); // 25- 函数工厂
javascript
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplyBy(2);
const triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15- 模块模式
javascript
const calculator = (function() {
// 私有变量和函数
let result = 0;
function validateNumber(num) {
return typeof num === 'number' && !isNaN(num);
}
// 公共API
return {
add: function(num) {
if (validateNumber(num)) {
result += num;
}
return this;
},
subtract: function(num) {
if (validateNumber(num)) {
result -= num;
}
return this;
},
getResult: function() {
return result;
}
};
})();
calculator.add(5).subtract(2).add(10);
console.log(calculator.getResult()); // 13原型和原型链
JavaScript中的对象通过原型链实现继承。每个对象都有一个内部链接指向另一个对象,称为它的原型。
javascript
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上添加方法
Person.prototype.greet = function() {
return `Hello, my name is ${this.name}`;
};
// 创建实例
const alice = new Person("Alice", 25);
console.log(alice.greet()); // "Hello, my name is Alice"
// 原型链
console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null原型继承
javascript
// 父构造函数
function Animal(name) {
this.name = name;
}
Animal.prototype.makeSound = function() {
return "Some generic sound";
};
// 子构造函数
function Dog(name, breed) {
// 调用父构造函数
Animal.call(this, name);
this.breed = breed;
}
// 设置原型链
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复constructor属性
// 添加/覆盖方法
Dog.prototype.makeSound = function() {
return "Woof!";
};
Dog.prototype.getBreed = function() {
return this.breed;
};
// 创建实例
const rex = new Dog("Rex", "German Shepherd");
console.log(rex.name); // "Rex"
console.log(rex.makeSound()); // "Woof!"
console.log(rex.getBreed()); // "German Shepherd"类与原型
ES6的类语法本质上是原型继承的语法糖。
javascript
// ES6类语法
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
return "Some generic sound";
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
makeSound() {
return "Woof!";
}
getBreed() {
return this.breed;
}
}
// 创建实例
const max = new Dog("Max", "Beagle");
console.log(max.name); // "Max"
console.log(max.makeSound()); // "Woof!"
console.log(max.getBreed()); // "Beagle"高阶函数
高阶函数是指接受函数作为参数或返回函数的函数。
javascript
// 接受函数作为参数
function operate(a, b, operation) {
return operation(a, b);
}
const add = (x, y) => x + y;
const multiply = (x, y) => x * y;
console.log(operate(5, 3, add)); // 8
console.log(operate(5, 3, multiply)); // 15
// 返回函数
function createValidator(regex) {
return function(value) {
return regex.test(value);
};
}
const isEmail = createValidator(/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)*\.[a-z]{2,}$/i);
const isPhone = createValidator(/^\d{10}$/);
console.log(isEmail("test@example.com")); // true
console.log(isPhone("1234567890")); // true常用的高阶函数
javascript
const numbers = [1, 2, 3, 4, 5];
// map - 转换数组元素
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter - 过滤数组元素
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
// reduce - 将数组归约为单个值
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 15
// forEach - 遍历数组元素
numbers.forEach(num => console.log(num));
// some - 检查是否至少有一个元素满足条件
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
// every - 检查是否所有元素都满足条件
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true异步编程
回调函数
javascript
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: "Product" };
callback(null, data);
}, 1000);
}
fetchData((error, data) => {
if (error) {
console.error("Error:", error);
} else {
console.log("Data:", data);
}
});Promise
javascript
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve({ id: 1, name: "Product" });
} else {
reject(new Error("Failed to fetch data"));
}
}, 1000);
});
}
fetchData()
.then(data => {
console.log("Data:", data);
return processData(data);
})
.then(result => {
console.log("Result:", result);
})
.catch(error => {
console.error("Error:", error);
});
function processData(data) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ ...data, processed: true });
}, 500);
});
}Async/Await
javascript
async function fetchAndProcessData() {
try {
const data = await fetchData();
console.log("Data:", data);
const result = await processData(data);
console.log("Result:", result);
return result;
} catch (error) {
console.error("Error:", error);
throw error;
}
}
fetchAndProcessData()
.then(finalResult => {
console.log("Final result:", finalResult);
})
.catch(error => {
console.error("Caught error:", error);
});设计模式
单例模式
javascript
const Singleton = (function() {
let instance;
function createInstance() {
return {
name: "Singleton Instance",
method() {
return "Method called";
}
};
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true工厂模式
javascript
function createUser(type) {
const types = {
admin: AdminUser,
regular: RegularUser,
guest: GuestUser
};
const UserType = types[type] || RegularUser;
return new UserType();
}
function AdminUser() {
this.role = "admin";
this.permissions = ["read", "write", "delete"];
}
function RegularUser() {
this.role = "regular";
this.permissions = ["read", "write"];
}
function GuestUser() {
this.role = "guest";
this.permissions = ["read"];
}
const admin = createUser("admin");
const regular = createUser("regular");
const guest = createUser("guest");
console.log(admin.permissions); // ["read", "write", "delete"]
console.log(regular.permissions); // ["read", "write"]
console.log(guest.permissions); // ["read"]观察者模式
javascript
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return this;
}
off(event, listener) {
if (!this.events[event]) return this;
this.events[event] = this.events[event].filter(l => l !== listener);
return this;
}
emit(event, ...args) {
if (!this.events[event]) return false;
this.events[event].forEach(listener => {
listener.apply(this, args);
});
return true;
}
once(event, listener) {
const onceWrapper = (...args) => {
listener.apply(this, args);
this.off(event, onceWrapper);
};
this.on(event, onceWrapper);
return this;
}
}
const emitter = new EventEmitter();
function onMessage(message) {
console.log("Message received:", message);
}
emitter.on("message", onMessage);
emitter.emit("message", "Hello World"); // "Message received: Hello World"
emitter.once("special", message => {
console.log("Special message:", message);
});
emitter.emit("special", "One time"); // "Special message: One time"
emitter.emit("special", "Won't show"); // 不会触发模块模式
javascript
const ShoppingCart = (function() {
// 私有变量
let items = [];
let total = 0;
// 私有方法
function calculateTotal() {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// 公共API
return {
addItem: function(item) {
items.push(item);
total = calculateTotal();
},
removeItem: function(id) {
items = items.filter(item => item.id !== id);
total = calculateTotal();
},
getItems: function() {
return [...items]; // 返回副本,防止外部修改
},
getTotal: function() {
return total;
},
clearCart: function() {
items = [];
total = 0;
}
};
})();
ShoppingCart.addItem({ id: 1, name: "Product 1", price: 10, quantity: 2 });
ShoppingCart.addItem({ id: 2, name: "Product 2", price: 15, quantity: 1 });
console.log(ShoppingCart.getItems()); // [{id: 1, ...}, {id: 2, ...}]
console.log(ShoppingCart.getTotal()); // 35
ShoppingCart.removeItem(1);
console.log(ShoppingCart.getTotal()); // 15函数式编程
纯函数
javascript
// 纯函数 - 相同输入总是产生相同输出,没有副作用
function add(a, b) {
return a + b;
}
// 非纯函数 - 依赖外部状态
let total = 0;
function addToTotal(value) {
total += value; // 副作用:修改外部变量
return total;
}不可变性
javascript
// 不修改原始数据
const originalArray = [1, 2, 3];
// 不好的做法 - 修改原始数组
originalArray.push(4); // 有副作用
// 好的做法 - 创建新数组
const newArray = [...originalArray, 4]; // 无副作用
// 对象不可变更新
const user = { name: "Alice", age: 25 };
const updatedUser = { ...user, age: 26 }; // 创建新对象而不是修改原对象函数组合
javascript
// 简单函数
const double = x => x * 2;
const increment = x => x + 1;
const square = x => x * x;
// 函数组合
function compose(...fns) {
return function(x) {
return fns.reduceRight((value, fn) => fn(value), x);
};
}
const transformValue = compose(square, increment, double);
// 等价于 square(increment(double(x)))
console.log(transformValue(3)); // 49 (square(increment(double(3))) = square(increment(6)) = square(7) = 49)柯里化
javascript
// 普通函数
function add(a, b, c) {
return a + b + c;
}
// 柯里化函数
function curryAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
// 使用箭头函数简化
const curryAddArrow = a => b => c => a + b + c;
console.log(add(1, 2, 3)); // 6
console.log(curryAdd(1)(2)(3)); // 6
console.log(curryAddArrow(1)(2)(3)); // 6
// 通用柯里化函数
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
}
};
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
console.log(curriedAdd(1, 2, 3)); // 6内存管理与垃圾回收
javascript
// 内存泄漏示例 - 闭包引用
function createLeak() {
const largeData = new Array(1000000).fill('x'); // 大量数据
return function() {
// 这个函数引用了largeData,即使函数本身不使用它
console.log("Function called");
// 如果不小心引用了largeData,它将保留在内存中
// console.log(largeData[0]);
};
}
const leakyFunction = createLeak(); // largeData在内存中保留
// 避免内存泄漏
function createNonLeak() {
const largeData = new Array(1000000).fill('x');
const result = processData(largeData);
return function() {
console.log("Result:", result);
// 不再引用largeData,它可以被垃圾回收
};
}
function processData(data) {
return data.length;
}
const nonLeakyFunction = createNonLeak(); // largeData可以被垃圾回收元编程
Proxy
javascript
const user = {
firstName: "John",
lastName: "Doe",
age: 30
};
const userProxy = new Proxy(user, {
get(target, property) {
console.log(`Getting property: ${property}`);
if (property === 'fullName') {
return `${target.firstName} ${target.lastName}`;
}
return target[property];
},
set(target, property, value) {
console.log(`Setting property: ${property} to ${value}`);
if (property === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
if (property === 'age' && (value < 0 || value > 120)) {
throw new RangeError('Age must be between 0 and 120');
}
target[property] = value;
return true; // 表示设置成功
}
});
console.log(userProxy.firstName); // "Getting property: firstName" "John"
console.log(userProxy.fullName); // "Getting property: fullName" "John Doe"
userProxy.age = 31; // "Setting property: age to 31"
// userProxy.age = -5; // RangeError: Age must be between 0 and 120
// userProxy.age = "thirty"; // TypeError: Age must be a numberReflect
javascript
const user = {
name: "Alice",
age: 25,
greet() {
return `Hello, my name is ${this.name}`;
}
};
// 获取属性
console.log(Reflect.get(user, 'name')); // "Alice"
// 设置属性
Reflect.set(user, 'age', 26);
console.log(user.age); // 26
// 检查属性是否存在
console.log(Reflect.has(user, 'name')); // true
console.log(Reflect.has(user, 'salary')); // false
// 删除属性
Reflect.deleteProperty(user, 'age');
console.log(user.age); // undefined
// 调用方法
console.log(Reflect.apply(user.greet, user, [])); // "Hello, my name is Alice"
// 创建新对象
const newUser = Reflect.construct(function(name) {
this.name = name;
}, ["Bob"]);
console.log(newUser.name); // "Bob"Symbol
javascript
// 使用Symbol定义特殊方法
const SIZE = Symbol('size');
class Collection {
constructor() {
this[SIZE] = 0;
this.items = {};
}
add(item) {
this.items[this[SIZE]] = item;
this[SIZE]++;
}
get size() {
return this[SIZE];
}
}
const collection = new Collection();
collection.add("item 1");
collection.add("item 2");
console.log(collection.size); // 2
console.log(Object.keys(collection)); // ["items"] (SIZE不可见)
// 内置Symbol
const iterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};
for (const value of iterable) {
console.log(value); // 1, 2, 3
}Web API 和浏览器环境
DOM 操作
javascript
// 选择元素
const element = document.getElementById('myElement');
const elements = document.querySelectorAll('.myClass');
// 修改内容和属性
element.textContent = 'New text';
element.innerHTML = '<strong>Bold text</strong>';
element.setAttribute('data-custom', 'value');
// 样式操作
element.style.color = 'red';
element.style.backgroundColor = '#f0f0f0';
element.classList.add('highlight');
element.classList.remove('hidden');
element.classList.toggle('active');
// 创建和添加元素
const newElement = document.createElement('div');
newElement.textContent = 'New element';
document.body.appendChild(newElement);
// 事件处理
element.addEventListener('click', function(event) {
console.log('Element clicked', event);
});
// 事件委托
document.getElementById('parent').addEventListener('click', function(event) {
if (event.target.matches('.child')) {
console.log('Child element clicked', event.target);
}
});浏览器存储
javascript
// localStorage - 持久存储
localStorage.setItem('username', 'john_doe');
const username = localStorage.getItem('username');
console.log(username); // "john_doe"
localStorage.removeItem('username');
localStorage.clear(); // 清除所有数据
// sessionStorage - 会话存储
sessionStorage.setItem('tempData', JSON.stringify({ id: 123 }));
const data = JSON.parse(sessionStorage.getItem('tempData'));
console.log(data.id); // 123
// Cookies
document.cookie = "name=John; expires=Fri, 31 Dec 2023 23:59:59 GMT; path=/";
// 读取cookies
function getCookie(name) {
const cookies = document.cookie.split('; ');
for (const cookie of cookies) {
const [cookieName, cookieValue] = cookie.split('=');
if (cookieName === name) {
return decodeURIComponent(cookieValue);
}
}
return null;
}
console.log(getCookie('name')); // "John"网络请求
javascript
// Fetch API
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data received:', data);
})
.catch(error => {
console.error('Fetch error:', error);
});
// POST请求
fetch('https://api.example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'John',
email: 'john@example.com'
})
})
.then(response => response.json())
.then(data => console.log('Response:', data));
// async/await 与 Fetch
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}性能优化
防抖和节流
javascript
// 防抖 - 延迟执行,重置计时器
function debounce(func, delay) {
let timeoutId;
return function(...args) {
const context = this;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
// 使用防抖
const debouncedSearch = debounce(function(query) {
console.log('Searching for:', query);
// 执行搜索操作
}, 300);
// 在输入框中使用
// inputElement.addEventListener('input', e => debouncedSearch(e.target.value));
// 节流 - 按固定时间间隔执行
function throttle(func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
// 使用节流
const throttledScroll = throttle(function() {
console.log('Scroll event throttled');
// 执行滚动处理
}, 500);
// 在滚动事件中使用
// window.addEventListener('scroll', throttledScroll);懒加载
javascript
// 图片懒加载
function lazyLoadImages() {
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
}
// 组件懒加载
function lazyLoadComponent(componentId, url) {
const placeholder = document.getElementById(componentId);
const componentObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
fetch(url)
.then(response => response.text())
.then(html => {
placeholder.innerHTML = html;
observer.unobserve(placeholder);
});
}
});
});
componentObserver.observe(placeholder);
}Web Workers
javascript
// 主线程代码
function startWorker() {
const worker = new Worker('worker.js');
worker.addEventListener('message', event => {
console.log('Result from worker:', event.data);
});
worker.addEventListener('error', error => {
console.error('Worker error:', error);
});
// 发送数据给Worker
worker.postMessage({
numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
operation: 'sum'
});
}
// worker.js 内容
/*
self.addEventListener('message', event => {
const { numbers, operation } = event.data;
let result;
switch (operation) {
case 'sum':
result = numbers.reduce((sum, num) => sum + num, 0);
break;
case 'average':
result = numbers.reduce((sum, num) => sum + num, 0) / numbers.length;
break;
default:
throw new Error(`Unknown operation: ${operation}`);
}
// 发送结果回主线程
self.postMessage(result);
});
*/