Skip to content

定义

JavaScript(缩写:JS)是一门完备的动态编程语言。当应用于 HTML 文档时,可为网站提供动态交互特性。由布兰登·艾克(Brendan Eich,Mozilla 项目、Mozilla 基金会和 Mozilla 公司的联合创始人)发明。

1. 什么是js

JavaScript 的应用场合极其广泛,简单到幻灯片、照片库、浮动布局和响应按钮点击,复杂到游戏、2D/3D 动画、大型数据库驱动程序等等。

JavaScript 相当简洁,却非常灵活。开发者们基于 JavaScript 核心编写了大量实用工具,可以使 开发工作事半功倍。其中包括:

  • 浏览器应用程序接口(API)—— 浏览器内置的 API 提供了丰富的功能,比如:动态创建 HTML 和设置 CSS 样式、从用户的摄像头采集处理视频流、生成 3D 图像与音频样本等等。
  • 第三方 API —— 让开发者可以在自己的站点中整合其它内容提供者(Twitter、Facebook 等)提供的功能。
  • 第三方框架和库 —— 用来快速构建网站和应用。

2. 快速入门

2.1 变量

变量 (en-US) 是存储值的容器。要声明一个变量,先输入关键字 let 或 var,然后输入合适的名称:

js
let myVariable = 'Hello World'

2.2 数据类型

注意变量可以有不同的数据类型 :

  • 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol
  • 引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。

数据类型

变量解释示例
String字符串(一串文本):字符串的值必须用引号(单双均可,必须成对)扩起来。let myVariable = '李雷';
Number数字:无需引号。let myVariable = 10;
Boolean布尔值(真 / 假): true/false 是 JS 里的特殊关键字,无需引号。let myVariable = true;
Array数组:用于在单一引用中存储多个值的结构。let myVariable = [1, '李雷', '韩梅梅', 10];
Object对象:JavaScript 里一切皆对象,一切皆可储存在变量里。这一点要牢记于心。let myVariable = document.querySelector('h1');

2.3 运算符

运算符 (en-US) 是一类数学符号,可以根据两个值(或变量)产生结果。以下表格中介绍了一些最简单的运算符,可以在浏览器控制台里尝试一下后面的示例。

运算符解释符号示例
将两个数字相加,或拼接两个字符串。+6 + 9;
"Hello " + "world!";
减、乘、除这些运算符操作与基础算术一致。只是乘法写作星号,除法写作斜杠。-, *, /9 - 3;
8 * 2;
9 / 3;
赋值运算符为变量赋值(你之前已经见过这个符号了)=let myVariable = '李雷';
等于测试两个值是否相等,并返回一个 true/false (布尔)值。===let myVariable = 3;
myVariable === 4; // false
不等于和等于运算符相反,测试两个值是否不相等,并返回一个 true/false (布尔)值。!==let myVariable = 3;
myVariable !== 3; // false
取非返回逻辑相反的值,比如当前值为真,则返回 false。!let myVariable = 3;
!(myVariable === 3); // false

2.4 条件语句

条件语句是一种代码结构,用来测试表达式的真假,并根据测试结果运行不同的代码。一个常用的条件语句是 if ... else

js
// if...else
let num = 10;
if (num > 0) {
  alert('正数');
} else {
  alert('负数');
}
// switch...case
const day = new Date().getDate()
switch (day) {
    case 0, 6:
        alert('周末');
    default:
        alert('工作日');
}

2.5 函数

函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。

document.querySelectoralert 是浏览器内置的函数。下面代码演示的是如何自定义函数:

js
function multiply(num1, num2) {
    return num1 * num2;
}

2.6 事件

事件能为网页添加真实的交互能力。它可以捕捉浏览器操作并运行一些代码做为响应。最简单的事件是点击事件,鼠标的点击操作会触发该事件。

html
<button id="btn1">click</button>

<script>
    const btn1 = document.getElementById('btn1')
    // 只会触发最后定义的
    btn1.onclick = () => {
        console.log('clicked me')
    }
    // 添加多少个,会触发多少次
    btn1.addEventListener('click', () => {
        console.log('clicked me')
    })
</script>

3. 事件循环

3.1 定义

Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

3.2 微任务和宏任务

在JavaScript中,任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)。

  • MacroTask(宏任务)setTimeout、setInterval、I/O、UI Rendering。
  • MicroTask(微任务) Process.nextTick(Node独有)、Promise、Object.observe(废弃)、MutationObserver

3.3 执行过程

  • 同步代码,调用栈执行后直接出栈
  • 异步代码,放到Web API中,等待时机,等合适的时候放入回调队列(callbackQueue),等到调用栈空时eventLoop开始工作,轮询
  • 微任务执行时机比宏任务要早

3.4 整体流程

  1. 先清空call stack中的同步代码
  2. 执行微任务队列中的微任务
  3. 尝试DOM渲染
  4. 触发Event Loop反复询问callbackQueue中是否有要执行的语句,有则放入call back继续执行 整体流程

4. 原型

4.1 构造函数

构造函数模式的目的就是为了创建一个自定义类,并且创建这个类的实例。构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立的。

构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用

js
function Person(name, age, gender) {
    this.name = name
    this.age = age
    this.gender = gender
    
    this.getInfo = function () {
        return `${name} is a ${age} years old ${gender}`
    }
}
Person.prototype.getName = function () {
    return this.name
}
const p1 = new Person('lzugis', 18, 'boy')
console.log(p1.getInfo())

4.2 原型

在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。 原型

4.3 __proto__和constructor

每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。

js
 function Person() {}
 var person = new Person()
 console.log(person.__proto__ === Person.prototype)//true
 console.log(Person.prototype.constructor===Person)//true
 console.log(Object.getPrototypeOf(person) === Person.prototype) // true

4.4 原型链

在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。

举例说明:person → Person → Object ,普通人继承人类,人类继承对象类

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。

我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true 原型链

5. es6

5.1 概念

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

5.2 变量声明:const和let

js
// let - 块级作用域,可重新赋值
let count = 0;
count = 1;

// const - 块级作用域,不可重新赋值
const PI = 3.14159;
// PI = 3.14; // Error: Assignment to constant variable

// 块级作用域示例
function testScope() {
    if (true) {
        let blockLet = 'block scope';
        const blockConst = 'block scope';
        var blockVar = 'function scope';
    }
    
    // console.log(blockLet); // ReferenceError
    // console.log(blockConst); // ReferenceError
    console.log(blockVar); // 'function scope'
}

5.3 模板字符串

js
const name = 'John';
const age = 30;

// 基本用法
const message = `My name is ${name} and I'm ${age} years old.`;

// 多行字符串
const template = `
    <div class="user">
        <h2>${name}</h2>
        <p>Age: ${age}</p>
    </div>
`;

// 表达式计算
const price = 100;
const tax = 0.08;
const total = `Total: $${(price * (1 + tax)).toFixed(2)}`;

5.4 箭头函数

js
// 传统函数
function add(a, b) {
    return a + b;
}

// 箭头函数
const addArrow = (a, b) => a + b;

// 多行箭头函数
const multiply = (a, b) => {
    const result = a * b;
    return result;
};

// this 绑定差异
const person = {
    name: 'John',
    // 传统函数:this指向调用者
    sayHello: function() {
        console.log(`Hello, I'm ${this.name}`);
    },
    // 箭头函数:this指向定义时上下文
    sayHelloArrow: () => {
        console.log(`Hello, I'm ${this.name}`); // undefined
    }
};

5.5 函数参数默认值

js
// 默认参数
function greet(name = 'Guest', message = 'Welcome') {
    return `${message}, ${name}!`;
}

// 解构赋值默认值
function createUser({ name = 'Anonymous', age = 0, city = 'Unknown' } = {}) {
    return { name, age, city };
}

// 使用示例
console.log(greet()); // "Welcome, Guest!"
console.log(greet('John', 'Hello')); // "Hello, John!"
console.log(createUser({ name: 'Alice', age: 25 })); // { name: 'Alice', age: 25, city: 'Unknown' }

5.6 Spread / Rest 操作符

js
// Spread - 展开数组/对象
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

const obj1 = { name: 'John', age: 30 };
const obj2 = { ...obj1, city: 'New York' }; // { name: 'John', age: 30, city: 'New York' }

// Rest - 收集参数
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

// 数组解构中的Rest
const [first, ...rest] = [1, 2, 3, 4, 5]; // first=1, rest=[2,3,4,5]

5.7 对象和数组解构

js
// 数组解构
const colors = ['red', 'green', 'blue'];
const [primary, secondary, tertiary] = colors;

// 跳过元素
const [, , third] = colors; // third = 'blue'

// 对象解构
const user = {
    name: 'John',
    age: 30,
    address: {
        city: 'New York',
        country: 'USA'
    }
};

const { name, age } = user;
const { name: userName, age: userAge } = user; // 重命名

// 嵌套解构
const { address: { city, country } } = user;

// 函数参数解构
function displayUser({ name, age = 0, city = 'Unknown' }) {
    console.log(`${name}, ${age}, lives in ${city}`);
}

5.8 for...of 和 for...in

js
const arr = ['a', 'b', 'c'];

// for...of - 遍历值
for (const value of arr) {
    console.log(value); // 'a', 'b', 'c'
}

// for...in - 遍历索引/属性名
for (const index in arr) {
    console.log(index); // '0', '1', '2'
}

// 对象遍历
const obj = { name: 'John', age: 30 };

for (const key in obj) {
    console.log(key, obj[key]);
}

// 迭代器
const str = 'hello';
for (const char of str) {
    console.log(char); // 'h', 'e', 'l', 'l', 'o'
}

5.9 ES6中的类

js
// 类定义
class Person {
    // 构造函数
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    // 实例方法
    sayHello() {
        return `Hello, I'm ${this.name}`;
    }

    // Getter
    get info() {
        return `${this.name} is ${this.age} years old`;
    }

    // Setter
    set age(newAge) {
        if (newAge > 0) {
            this._age = newAge;
        }
    }

    get age() {
        return this._age;
    }

    // 静态方法
    static createAdult(name) {
        return new Person(name, 18);
    }
}

// 继承
class Employee extends Person {
    constructor(name, age, jobTitle) {
        super(name, age); // 调用父类构造函数
        this.jobTitle = jobTitle;
    }

    // 重写方法
    sayHello() {
        return `${super.sayHello()} and I'm a ${this.jobTitle}`;
    }

    // 新方法
    work() {
        return `${this.name} is working as a ${this.jobTitle}`;
    }
}

// 使用
const john = new Person('John', 30);
const employee = new Employee('Jane', 25, 'Developer');

console.log(john.sayHello()); // "Hello, I'm John"
console.log(employee.work()); // "Jane is working as a Developer"

5.10 其他ES6特性

js
// Symbol - 唯一标识符
const id = Symbol('description');
const user = {
    name: 'John',
    [id]: 12345
};

// 模块导入导出
// math.js
export const PI = 3.14159;
export function add(a, b) {
    return a + b;
}

// main.js
import { PI, add } from './math.js';

// Promise - 异步处理
const fetchData = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Data loaded');
        }, 1000);
    });
};

// Map 和 Set
const map = new Map();
map.set('name', 'John');
map.set(1, 'number key');

const set = new Set([1, 2, 3, 3, 4]); // 自动去重

6. 异步编程

6.1 Promise

js
// 创建Promise
const promise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('Operation successful');
        } else {
            reject('Operation failed');
        }
    }, 1000);
});

// 使用Promise
promise
    .then(result => console.log(result))
    .catch(error => console.error(error))
    .finally(() => console.log('Operation completed'));

// Promise链
function fetchUser(id) {
    return fetch(`/api/users/${id}`)
        .then(response => response.json());
}

function fetchUserPosts(userId) {
    return fetch(`/api/users/${userId}/posts`)
        .then(response => response.json());
}

// 链式调用
fetchUser(1)
    .then(user => fetchUserPosts(user.id))
    .then(posts => console.log(posts))
    .catch(error => console.error(error));

6.2 Async/Await

js
// async函数声明
async function fetchUserData(userId) {
    try {
        const user = await fetch(`/api/users/${userId}`);
        const userData = await user.json();
        
        const posts = await fetch(`/api/users/${userId}/posts`);
        const userPosts = await posts.json();
        
        return { user: userData, posts: userPosts };
    } catch (error) {
        console.error('Error fetching data:', error);
        throw error;
    }
}

// 箭头async函数
const fetchData = async () => {
    const response = await fetch('/api/data');
    return response.json();
};

// 并行执行
async function fetchMultipleData() {
    const [users, posts, comments] = await Promise.all([
        fetch('/api/users').then(r => r.json()),
        fetch('/api/posts').then(r => r.json()),
        fetch('/api/comments').then(r => r.json())
    ]);
    
    return { users, posts, comments };
}

// 错误处理
async function robustFetch(url, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url);
            return await response.json();
        } catch (error) {
            if (i === retries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
        }
    }
}

6.3 高级异步模式

js
// Promise.all - 所有Promise成功才成功
function fetchAllData() {
    return Promise.all([
        fetch('/api/users'),
        fetch('/api/posts'),
        fetch('/api/comments')
    ]);
}

// Promise.allSettled - 无论成功失败都返回结果
function fetchAllDataWithStatus() {
    return Promise.allSettled([
        fetch('/api/users'),
        fetch('/api/posts'),
        fetch('/api/comments')
    ]).then(results => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`Request ${index} succeeded:`, result.value);
            } else {
                console.log(`Request ${index} failed:`, result.reason);
            }
        });
    });
}

// Promise.race - 返回最快完成的Promise
function fetchWithTimeout(url, timeout = 5000) {
    return Promise.race([
        fetch(url),
        new Promise((_, reject) => 
            setTimeout(() => reject(new Error('Timeout')), timeout)
        )
    ]);
}

// 串行执行
async function processSequentially(items) {
    const results = [];
    for (const item of items) {
        const result = await processItem(item);
        results.push(result);
    }
    return results;
}

7. 函数式编程

7.1 高阶函数

js
// map - 转换数组
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]

const users = [
    { name: 'John', age: 30 },
    { name: 'Jane', age: 25 }
];
const names = users.map(user => user.name); // ['John', 'Jane']

// filter - 过滤数组
const evenNumbers = numbers.filter(n => n % 2 === 0); // [2, 4]
const adults = users.filter(user => user.age >= 18);

// reduce - 累积计算
const sum = numbers.reduce((acc, n) => acc + n, 0); // 15
const userNamesString = users.reduce((acc, user) => 
    acc + user.name + ', ', '').slice(0, -2); // "John, Jane"

// 组合使用
const totalAgeOfAdults = users
    .filter(user => user.age >= 18)
    .reduce((total, user) => total + user.age, 0);

7.2 函数组合

js
// 函数组合
const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);

// 使用示例
const addOne = x => x + 1;
const multiplyByTwo = x => x * 2;
const toString = x => x.toString();

const addOneThenMultiplyByTwo = pipe(addOne, multiplyByTwo); // (x) => (x + 1) * 2
const result = addOneThenMultiplyByTwo(5); // 12

// 柯里化
const curry = (fn) => {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        }
        return (...nextArgs) => curried(...args, ...nextArgs);
    };
};

const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6

7.3 实用函数式工具

js
// 记忆化
const memoize = (fn) => {
    const cache = new Map();
    return (...args) => {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };
};

const fibonacci = memoize(n => {
    if (n < 2) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
});

// 函数节流
const throttle = (fn, delay) => {
    let lastCall = 0;
    return (...args) => {
        const now = Date.now();
        if (now - lastCall >= delay) {
            lastCall = now;
            return fn(...args);
        }
    };
};

// 函数防抖
const debounce = (fn, delay) => {
    let timeoutId;
    return (...args) => {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn(...args), delay);
    };
};

// 偏函数
const partial = (fn, ...presetArgs) => {
    return (...laterArgs) => fn(...presetArgs, ...laterArgs);
};

const greet = (greeting, name) => `${greeting}, ${name}!`;
const sayHello = partial(greet, 'Hello');
console.log(sayHello('John')); // "Hello, John!"

8. 现代JavaScript特性

8.1 ES2020+ 新特性

js
// 可选链操作符 (?.)
const user = {
    name: 'John',
    address: {
        street: 'Main St'
    }
};

const street = user?.address?.street; // 'Main St'
const zipCode = user?.address?.zipCode; // undefined
const phone = user?.contact?.phone; // undefined

// 空值合并操作符 (??)
const name = user.name ?? 'Anonymous'; // 'John'
const age = user.age ?? 0; // 0 (不会因为age=0而使用默认值)

// BigInt
const bigNumber = 123456789012345678901234567890n;
const anotherBigNumber = BigInt("123456789012345678901234567890");

// String.prototype.matchAll
const regex = /t(e)(st(\d?))/g;
const str = 'test1test2';
for (const match of str.matchAll(regex)) {
    console.log(match);
}

// globalThis
const globalObj = globalThis; // 浏览器中是window,Node.js中是global

8.2 ES2021+ 新特性

js
// String.prototype.replaceAll
const message = 'Hello world, hello universe';
const newMessage = message.replaceAll('hello', 'hi'); // "Hi world, hi universe"

// Promise.any
const promises = [
    Promise.reject('Error 1'),
    Promise.reject('Error 2'),
    Promise.resolve('Success')
];

Promise.any(promises)
    .then(result => console.log(result)) // "Success"
    .catch(error => console.log(error)); // 所有Promise都失败时才执行

// 逻辑赋值操作符
let x = { name: 'John' };

// x.name &&= 'Jane'; // 相当于 x.name && (x.name = 'Jane')
// x.age ||= 25;      // 相当于 x.age || (x.age = 25)
// x.city ??= 'NYC';  // 相当于 x.city ?? (x.city = 'NYC')

// 数组groupBy (ES2023)
const people = [
    { name: 'Alice', age: 25 },
    { name: 'Bob', age: 30 },
    { name: 'Charlie', age: 25 }
];

// const groupedByAge = Object.groupBy(people, person => person.age);
// 结果: { 25: [{...}, {...}], 30: [{...}] }

8.3 数组新方法

js
// Array.prototype.flat / flatMap
const nestedArray = [1, [2, 3], [4, [5, 6]]];
const flatArray = nestedArray.flat(2); // [1, 2, 3, 4, 5, 6]

const sentences = ['Hello world', 'How are you', 'Goodbye'];
const words = sentences.flatMap(sentence => sentence.split(' '));
// ['Hello', 'world', 'How', 'are', 'you', 'Goodbye']

// Array.prototype.at
const arr = [10, 20, 30, 40, 50];
console.log(arr.at(0));   // 10
console.log(arr.at(-1));  // 50 (从末尾开始)
console.log(arr.at(-2));  // 40

// Array.prototype.findLast / findLastIndex
const numbers = [1, 3, 5, 2, 4, 6];
const lastEven = numbers.findLast(n => n % 2 === 0); // 6
const lastEvenIndex = numbers.findLastIndex(n => n % 2 === 0); // 5

9. 错误处理与调试

9.1 错误处理模式

js
// 自定义错误类
class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = 'ValidationError';
        this.field = field;
    }
}

class NetworkError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.name = 'NetworkError';
        this.statusCode = statusCode;
    }
}

// 错误处理装饰器
const withErrorHandling = (fn) => {
    return (...args) => {
        try {
            return fn(...args);
        } catch (error) {
            console.error(`Error in ${fn.name}:`, error.message);
            // 可以在这里添加错误报告逻辑
            throw error;
        }
    };
};

// Result模式 - 不使用异常
class Result {
    constructor(success, value = null, error = null) {
        this.success = success;
        this.value = value;
        this.error = error;
    }
    
    static success(value) {
        return new Result(true, value);
    }
    
    static failure(error) {
        return new Result(false, null, error);
    }
    
    map(fn) {
        return this.success ? Result.success(fn(this.value)) : this;
    }
    
    flatMap(fn) {
        return this.success ? fn(this.value) : this;
    }
}

// 使用示例
function divide(a, b) {
    if (b === 0) {
        return Result.failure(new Error('Division by zero'));
    }
    return Result.success(a / b);
}

const result = divide(10, 2)
    .map(x => x * 2)
    .flatMap(x => divide(x, 5));

9.2 调试技巧

js
// console方法
const user = { name: 'John', age: 30, address: { city: 'NYC' } };

console.log(user); // 基本日志
console.info(user); // 信息日志
console.warn(user); // 警告日志
console.error(user); // 错误日志

console.table([user, { name: 'Jane', age: 25 }]); // 表格显示
console.group('User Details');
console.log('Name:', user.name);
console.log('Age:', user.age);
console.groupEnd();

// 断言调试
const assert = (condition, message) => {
    if (!condition) {
        throw new Error(`Assertion failed: ${message}`);
    }
};

// 性能测量
const measureTime = (fn, label) => {
    console.time(label);
    const result = fn();
    console.timeEnd(label);
    return result;
};

// 调试工具函数
const debug = (process.env.NODE_ENV === 'development') 
    ? (...args) => console.log('[DEBUG]', ...args)
    : () => {};

const trace = (label) => {
    console.trace(`Trace: ${label}`);
};

10. 性能优化

10.1 内存管理

js
// 避免内存泄漏
class EventManager {
    constructor() {
        this.listeners = new Map();
    }
    
    addListener(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, new Set());
        }
        this.listeners.get(event).add(callback);
    }
    
    removeListener(event, callback) {
        if (this.listeners.has(event)) {
            this.listeners.get(event).delete(callback);
        }
    }
    
    emit(event, ...args) {
        if (this.listeners.has(event)) {
            this.listeners.get(event).forEach(callback => callback(...args));
        }
    }
    
    // 清理所有监听器
    cleanup() {
        this.listeners.clear();
    }
}

// WeakMap和WeakSet的使用
const weakMap = new WeakMap();
const weakSet = new WeakSet();

// 当对象被垃圾回收时,WeakMap中的对应条目也会被清理
const obj = { data: 'some data' };
weakMap.set(obj, 'metadata');

// 避免闭包内存泄漏
function createLeakFreeFunction() {
    const largeData = new Array(1000000).fill('data');
    
    return function useData(index) {
        // 只访问需要的数据,不保持对largeData的引用
        return largeData[index];
    };
}

10.2 执行优化

js
// 函数去抖和节流的应用场景
const searchInput = document.getElementById('search');

// 搜索去抖 - 避免频繁请求
const debouncedSearch = debounce((query) => {
    fetch(`/api/search?q=${query}`)
        .then(response => response.json())
        .then(results => displayResults(results));
}, 300);

searchInput.addEventListener('input', (e) => {
    debouncedSearch(e.target.value);
});

// 滚动节流 - 优化性能
const throttledScroll = throttle(() => {
    updateScrollPosition();
}, 100);

window.addEventListener('scroll', throttledScroll);

// 批量DOM操作
const updateDOM = (() => {
    const updates = [];
    let rafId = null;
    
    return (element, property, value) => {
        updates.push({ element, property, value });
        
        if (!rafId) {
            rafId = requestAnimationFrame(() => {
                updates.forEach(({ element, property, value }) => {
                    element[property] = value;
                });
                updates.length = 0;
                rafId = null;
            });
        }
    };
})();

// 虚拟滚动示例
class VirtualList {
    constructor(container, itemHeight, renderItem) {
        this.container = container;
        this.itemHeight = itemHeight;
        this.renderItem = renderItem;
        this.visibleItems = [];
        this.scrollTop = 0;
        
        this.container.addEventListener('scroll', this.handleScroll.bind(this));
    }
    
    setItems(items) {
        this.items = items;
        this.updateVisibleItems();
    }
    
    handleScroll() {
        this.scrollTop = this.container.scrollTop;
        this.updateVisibleItems();
    }
    
    updateVisibleItems() {
        const containerHeight = this.container.clientHeight;
        const startIndex = Math.floor(this.scrollTop / this.itemHeight);
        const endIndex = Math.min(
            startIndex + Math.ceil(containerHeight / this.itemHeight) + 1,
            this.items.length
        );
        
        // 只渲染可见的元素
        this.container.innerHTML = '';
        for (let i = startIndex; i < endIndex; i++) {
            const item = this.renderItem(this.items[i], i);
            item.style.position = 'absolute';
            item.style.top = `${i * this.itemHeight}px`;
            item.style.height = `${this.itemHeight}px`;
            this.container.appendChild(item);
        }
    }
}

10.3 代码分割与懒加载

js
// 动态导入
async function loadModule() {
    try {
        const module = await import('./heavy-module.js');
        module.doSomething();
    } catch (error) {
        console.error('Module loading failed:', error);
    }
}

// 条件加载
async function loadFeature() {
    if ('serviceWorker' in navigator) {
        const { registerServiceWorker } = await import('./sw.js');
        registerServiceWorker();
    }
}

// React中的懒加载模式示例
class LazyComponent {
    constructor() {
        this.component = null;
        this.loading = false;
    }
    
    async load() {
        if (!this.component && !this.loading) {
            this.loading = true;
            try {
                const module = await import('./Component.js');
                this.component = module.default;
            } finally {
                this.loading = false;
            }
        }
        return this.component;
    }
    
    async render(container) {
        const Component = await this.load();
        if (Component) {
            new Component(container);
        }
    }
}

11. 模块化开发

11.1 ES6模块

js
// math.js - 导出
export const PI = 3.14159;

export function add(a, b) {
    return a + b;
}

export function subtract(a, b) {
    return a - b;
}

// 默认导出
export default function multiply(a, b) {
    return a * b;
}

// main.js - 导入
import multiply, { add, subtract, PI } from './math.js';
import * as math from './math.js'; // 导入所有

// 动态导入
async function useMathModule() {
    const math = await import('./math.js');
    console.log(math.add(2, 3));
}

11.2 模块设计模式

js
// 单例模块
const AppConfig = (() => {
    let instance = null;
    
    class Config {
        constructor() {
            this.settings = {};
        }
        
        set(key, value) {
            this.settings[key] = value;
        }
        
        get(key) {
            return this.settings[key];
        }
        
        getAll() {
            return { ...this.settings };
        }
    }
    
    return {
        getInstance: () => {
            if (!instance) {
                instance = new Config();
            }
            return instance;
        }
    };
})();

// 观察者模式
class EventEmitter {
    constructor() {
        this.events = new Map();
    }
    
    on(event, callback) {
        if (!this.events.has(event)) {
            this.events.set(event, new Set());
        }
        this.events.get(event).add(callback);
    }
    
    off(event, callback) {
        if (this.events.has(event)) {
            this.events.get(event).delete(callback);
        }
    }
    
    emit(event, ...args) {
        if (this.events.has(event)) {
            this.events.get(event).forEach(callback => callback(...args));
        }
    }
}

// 状态管理器
class Store {
    constructor(initialState = {}) {
        this.state = { ...initialState };
        this.listeners = new Set();
    }
    
    setState(updates) {
        this.state = { ...this.state, ...updates };
        this.notify();
    }
    
    getState() {
        return { ...this.state };
    }
    
    subscribe(listener) {
        this.listeners.add(listener);
        return () => this.listeners.delete(listener);
    }
    
    notify() {
        this.listeners.forEach(listener => listener(this.state));
    }
}

12. 测试与质量保证

12.1 单元测试

js
// 简单的测试框架
class TestRunner {
    constructor() {
        this.tests = [];
        this.results = {
            passed: 0,
            failed: 0,
            errors: []
        };
    }
    
    test(name, fn) {
        this.tests.push({ name, fn });
    }
    
    async run() {
        console.log('Running tests...');
        
        for (const test of this.tests) {
            try {
                await test.fn();
                console.log(`✓ ${test.name}`);
                this.results.passed++;
            } catch (error) {
                console.log(`✗ ${test.name}`);
                console.log(`  Error: ${error.message}`);
                this.results.failed++;
                this.results.errors.push({ test: test.name, error });
            }
        }
        
        console.log(`
Results: ${this.results.passed} passed, ${this.results.failed} failed`);
        return this.results;
    }
}

// 断言函数
function assert(condition, message = 'Assertion failed') {
    if (!condition) {
        throw new Error(message);
    }
}

function assertEqual(actual, expected, message) {
    assert(actual === expected, 
        message || `Expected ${expected}, but got ${actual}`);
}

function assertThrows(fn, message) {
    let threw = false;
    try {
        fn();
    } catch (error) {
        threw = true;
    }
    assert(threw, message || 'Expected function to throw');
}

// 测试示例
const runner = new TestRunner();

runner.test('Array.prototype.map works correctly', () => {
    const arr = [1, 2, 3];
    const result = arr.map(x => x * 2);
    assertEqual(result.length, 3);
    assertEqual(result[0], 2);
    assertEqual(result[1], 4);
    assertEqual(result[2], 6);
});

runner.test('Error handling in division', () => {
    function divide(a, b) {
        if (b === 0) {
            throw new Error('Division by zero');
        }
        return a / b;
    }
    
    assertEqual(divide(10, 2), 5);
    assertThrows(() => divide(10, 0));
});

12.2 性能测试

js
// 性能测试工具
class PerformanceTester {
    static async measureTime(fn, iterations = 1000) {
        const start = performance.now();
        
        for (let i = 0; i < iterations; i++) {
            await fn();
        }
        
        const end = performance.now();
        return (end - start) / iterations;
    }
    
    static compare(fns, iterations = 1000) {
        return Promise.all(
            fns.map(async (fn, index) => ({
                name: fn.name || `Function ${index}`,
                time: await this.measureTime(fn, iterations)
            }))
        ).then(results => 
            results.sort((a, b) => a.time - b.time)
        );
    }
}

// 内存使用测试
function measureMemoryUsage(fn) {
    if (typeof performance !== 'undefined' && performance.memory) {
        const before = performance.memory.usedJSHeapSize;
        fn();
        const after = performance.memory.usedJSHeapSize;
        return after - before;
    }
    return null;
}

总结

本文档全面覆盖了现代JavaScript开发的核心概念和最佳实践,从基础语法到高级特性,从同步编程到异步处理,从性能优化到测试方法。通过系统学习这些内容,你将能够:

  1. 掌握现代JavaScript语法和特性
  2. 理解异步编程和事件循环机制
  3. 运用函数式编程思想
  4. 编写高性能、可维护的代码
  5. 进行有效的错误处理和调试
  6. 实施模块化开发架构

JavaScript生态系统在不断演进,保持学习热情和关注最新标准是关键。建议定期查看TC39提案和MDN文档,及时掌握新特性。


参考资源

官方文档

  1. MDN JavaScript指南
  2. ECMAScript规范
  3. Node.js文档

学习资源

  1. JavaScript.info
  2. You Don't Know JS
  3. 现代JavaScript教程

工具与框架

  1. TypeScript
  2. ESLint
  3. Prettier

性能优化

  1. Web.dev Performance
  2. Chrome DevTools
  3. JavaScript Performance Best Practices

最后更新:2024年版本:v2.0文档维护者:前端开发团队

继续探索,持续进步!🚀