疯狂HTML+CSS+JS 中JS总结


本总结基于《疯狂HTML+CSS+JS》一书,结合现代JavaScript特性,对JavaScript核心知识点进行全面梳理。

1. JavaScript语法

1.1 执行js代码

javascript:alert('执行js');一般放在超链接中,用户点击即执行。

<script>
    alert("执行js");
</script>

1.2 变量赋值

var a = 1; // 显式声明
let b = 2; // ES6 块级作用域变量
const c = 3; // ES6 常量

1.3 全局变量与局部变量

var scope = "全局变量";
function test() {
    alert(scope); // undefined (变量提升)
    var scope = "局部变量";
    alert(scope); // 局部变量
}

// ES6 块级作用域
function testES6() {
    let scope = "局部变量";
    if (true) {
        let scope = "块级变量";
        console.log(scope); // 块级变量
    }
    console.log(scope); // 局部变量
}

1.4 浮点数

var a = 0.333;
var b = a * 5;
alert(b); // 1.665

在js中判断浮点数是否相等,建议判断两者的差值是否小于一个足够小的数(例如0.0000000000001)。

1.5 字符串

js中没有字符类型变量,”” 与 ‘’ 一致。

var s = "abcdefg";
b = s.slice(3, -1); // "def"

// ES6 模板字符串
let name = "John";
let greeting = `Hello, ${name}!`;

1.6 字符串的正则表达式方法

  1. match()返回匹配的字符串(数组或null),可加/g进行全局匹配
  2. search()返回匹配的索引值(单个)
  3. replace()替换匹配的字符串,默认只替换第一个匹配项;使用正则表达式/g标志可替换所有匹配项
  4. split()根据正则表达式分割字符串

1.7 undefined和null

null == undefined; // true
null === undefined; // false

undefined 表示变量已声明但未初始化,null表示变量被显式设置为空值。

1.8 运算符

// 逗号运算符 依次计算每个表达式,并返回最后一个表达式的值
a = ((b = 5), (c = 7), (d = 56)); // a = 56
a = void ((b = 5), (c = 7), (d = 56)); // a = undefined

// ES6 扩展运算符
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

// 解构赋值
let { x, y } = { x: 1, y: 2 };

1.9 typeof和instanceof

typeof "123"; // "string"
typeof 123; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (历史遗留问题)
typeof {}; // "object"
typeof []; // "object"
typeof function () {}; // "function"

// instanceof 判断对象是否为某构造函数的实例
var a = [4, 5];
alert(a instanceof Array); // true

1.10 语句

抛出异常

throw new Error("用户自定义异常");

try {
    // 可能出错的代码
} catch (e) {
    alert(e.message); // "用户自定义异常"
} finally {
    // 无论是否出错都会执行的代码
}

Error对象

JavaScript内置Error对象,包含message和name属性。

try {
    throw new Error("出错了", "错误原因");
} catch (e) {
    console.log(e.message); // "出错了"
    console.log(e.name); // "Error"
    console.log(e.stack); // 错误堆栈信息
}

自定义错误类型

class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = "ValidationError";
    }
}

try {
    throw new ValidationError("输入不合法");
} catch (e) {
    if (e instanceof ValidationError) {
        console.log("验证错误:", e.message);
    } else {
        throw e;
    }
}

for in 循环

// 遍历对象属性
for (let prop_name in navigator) {
    console.log(prop_name + ": " + navigator[prop_name]);
}

// ES6 for of 循环
for (let value of [1, 2, 3]) {
    console.log(value);
}

1.11 函数

js 允许先调用函数再定义函数(函数提升)。

定义函数

var show_name = function (name) {
    alert(name);
};

show_name("K"); // K

// ES6 箭头函数
const showName = name => {
    alert(name);
};

函数即对象

var hello = function () {
    /* ... */
};

console.log(hello instanceof Function); // true
console.log(hello instanceof Object); // true
console.log(hello.toString()); // 输出函数源代码

调用函数方式的不同

  1. 直接调用函数:返回return的值或undefined
  2. new 函数名():得到的是对象实例

this关键字

  1. 在函数中使用this变量,该变量指向函数的调用者
  2. 函数可作为对象的方法调用,如没有指定调用者,则在非严格模式下 this 默认指向 window对象(浏览器环境),严格模式下指向 undefined
var hello = function () {
    /* ... */
};
window.hello();

var p = {
    work: function () {
        /* ... */
    },
};
p.work();

// ES6 箭头函数中的this
const obj = {
    name: "John",
    sayHello: function () {
        setTimeout(() => {
            console.log(this.name); // 箭头函数不绑定自己的this,继承外部上下文的this
        }, 1000);
    },
};
obj.sayHello(); // 1秒后输出: John

函数中的变量

function Person() {
    // 局部变量 只能在函数里访问
    var id;

    // 实例属性 通过对象.访问
    this.age;

    // 类属性 通过Person.name访问
    Person.name;
}

动态添加属性和方法

JavaScript是一种动态语言,能随时给对象增加属性和方法。

function Student(name) {
    this.name = name; // 实例属性
}

var student = new Student("K");

// 动态增加实例属性
student.age = 22;
alert(student.age); // 22

// 动态增加实例方法
student.sayHello = function () {
    console.log(`Hello, I am ${this.name}`);
};
student.sayHello(); // Hello, I am K

// 动态增加静态属性(通过构造函数)
Student.MAX_AGE = 100;
alert(Student.MAX_AGE); // 100

调用函数的三种方式

  1. 直接调用
window.alert();
// 或
alert();
  1. call()调用
var each = function (array, fn) {
    for (var index in array) {
        // null表示以window为调用者
        fn.call(null, index, array[index]);
    }
};

each([4, 20, 3], function (index, ele) {
    alert("第 " + index + " 个元素是 : " + ele);
});
  1. apply()调用
var myfun = function (a, b) {
    alert(a + "  " + b);
};

myfun.call(window, 12, 23); // 12 23
myfun.apply(window, [20, 39]); // 20 39

// 使用 ES6 spread operator 直接传参
myfun(...[10, 20]); // 10 20

// 使用 apply 配合 arguments 对象
var example = function (num1, num2) {
    myfun.apply(this, [num1, num2]); // 传入实际参数
};
example(20, 40); // 20 40

函数的独立性

在函数A中可以定义函数B,但是函数B还是独立于函数A。

参数传递方式

JavaScript中所有参数都是按值传递的。但需要注意:

  • 基本类型:传递的是值的副本,修改不会影响原变量
  • 引用类型:传递的是对象引用的副本,所以修改对象的属性会影响原对象

基本类型:

function change(arg) {
    arg = 10;
    alert(arg); // 10
}
var x = 5;
alert(x); // 5
change(x);
alert(x); // 5

复合类型:

function change(person) {
    person.age = 10;
    alert(person.age); // 10
    person = null;
}

var person = { age: 5 };
alert(person.age); // 5
change(person);
alert(person.age); // 10
console.log(person); // {age: 10}

可选参数与默认参数

JavaScript函数不检查参数数量,少传的参数为undefined。

function text(person) {
    alert(typeof person); // undefined
}

text(); // undefined

// ES6 默认参数
function greet(name = "Guest") {
    console.log(`Hello, ${name}!`);
}
greet(); // Hello, Guest!
greet("John"); // Hello, John!

对象属性遍历

JavaScript对象可以像关联数组一样访问属性。

function Person(name) {
    this.name = name;
    this.info = function () {
        alert(this.name);
    };
}

var person = new Person("K");
// 遍历person属性
for (let propName in person) {
    console.log(person[propName]);
}

继承和prototype

使用prototype实现继承:

function Person() {}

var person = new Person();

// 添加原型方法
Person.prototype.work = function () {
    console.log("Working...");
};

person.work(); // OK

// ES6 Class
class PersonES6 {
    constructor(name) {
        this.name = name;
    }

    info() {
        console.log(this.name);
    }
}

class Student extends PersonES6 {
    constructor(name, grade) {
        super(name);
        this.grade = grade;
    }
}

1.12 创建对象三种方式

new关键字调用构造器创建对象

function Person(name) {
    this.name = name;
}
var person_1 = new Person();
var person_2 = new Person("K");

使用Object直接创建对象

var my_obj = new Object();
my_obj.name = "K";
my_obj.handsome = function () {
    /* ... */
};

对象字面量(JSON)创建对象

var person = {
    name: "K",
    school: ["ChangAn", "TianJin"],
    girl_friends: [
        {
            name: "Haski",
            age: 11,
        },
        {
            name: "Samoyed",
            age: 8,
        },
    ],
};

alert(person.girl_friends[0].name); // Haski

// ES6 简洁对象字面量
const name = "John";
const age = 30;
const personES6 = {
    name,
    age,
    greet() {
        console.log(`Hello, ${this.name}!`);
    },
};

2. DOM编程

DOM操作其实JQuery已经做得很好了,这里简单补充一下原生JS的知识。

2.1 访问HTML元素

  1. 通过id或name:getElementById('id')getElementsByName('name')
  2. 根据节点关系:
node.parentNode; // 返回父节点
node.previousSibling; // 返回前一个兄弟节点
node.nextSibling; // 返回后一个兄弟节点
node.childNodes; // 返回当前节点的所有子节点
node.getElementsByTagName("标签名称"); // 返回当前节点具有指定标签的子节点

// 现代DOM API
node.querySelector(selector); // 返回第一个匹配的元素
node.querySelectorAll(selector); // 返回所有匹配的元素

2.2 创建HTML元素

const element = document.createElement("div");
element.textContent = "Hello";

// 复制节点
const clone = element.cloneNode(true); // true表示深拷贝

2.3 添加节点

parent.appendChild(node); // 添加为当前节点的最后一个子节点
parent.insertBefore(newNode, refNode); // 在refNode前添加newNode
parent.replaceChild(newChild, oldChild); // 替换节点

2.4 删除节点

parent.removeChild(oldNode);

// 现代API
node.remove();

2.5 window对象

// 导航
window.location.href = "https://www.example.com";
window.history.back();

// 屏幕信息
console.log(window.screen.width);
console.log(window.screen.height);

// 对话框
window.alert("Message");
const result = window.confirm("Are you sure?");
const input = window.prompt("Enter your name:");

// 定时器
const timer = setInterval(() => {
    console.log("Hello");
}, 1000);
clearInterval(timer);

2.6 navigator和地理位置

// 浏览器信息
console.log(navigator.userAgent);

// 地理位置
if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
        position => {
            console.log(position.coords.latitude, position.coords.longitude);
        },
        error => {
            console.error(error);
        }
    );
}

3. 事件处理机制

3.1 常见事件

  • click:点击
  • mouseover/mouseout:鼠标指针进入/离开元素(会冒泡)
  • mouseenter/mouseleave:鼠标指针进入/离开元素(不冒泡)
  • keydown/keyup:键盘按下/松开
  • submit:表单提交
  • load:页面加载完成
  • resize:窗口大小改变

3.2 事件处理和this

const p = {
    name: "John",
    info: function () {
        alert(this.name);
    },
};

document.getElementById("bt").onclick = p.info; // this指向'bt'控件
document.getElementById("bt").onclick = () => p.info(); // this指向p

3.3 DOM事件监听

// 添加事件监听器
element.addEventListener("click", function (event) {
    console.log("Clicked!");
});

// 阻止事件传播
element.addEventListener("click", function (event) {
    event.stopPropagation();
});

// 取消默认行为
element.addEventListener("click", function (event) {
    event.preventDefault();
});

// 事件委托
parentElement.addEventListener("click", function (event) {
    if (event.target.matches(".child")) {
        console.log("Child clicked!");
    }
});

4. 本地存储与离线应用

4.1 Web Storage

使用理由:Cookie的局限性

  1. Cookie大小被限制为4KB
  2. Cookie会包含在每次HTTP请求中
  3. Cookie如未设置Secure标志,在HTTP传输中为明文

Web Storage有两种:

  • Session Storage:生命周期与用户Session一致
  • Local Storage:保存在用户的磁盘中,需要手动删除
// 存储数据
localStorage.setItem("name", "John");
sessionStorage.setItem("token", "abc123");

// 读取数据
const name = localStorage.getItem("name");

// 删除数据
localStorage.removeItem("name");

// 清除所有数据
localStorage.clear();

// 存储对象
const user = { name: "John", age: 30 };
localStorage.setItem("user", JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem("user"));

4.2 构建离线应用

使用Service Worker实现离线缓存:

// 注册Service Worker
if ("serviceWorker" in navigator) {
    navigator.serviceWorker
        .register("/sw.js")
        .then(registration => {
            console.log("Service Worker registered:", registration);
        })
        .catch(error => {
            console.error("Service Worker registration failed:", error);
        });
}

5. 使用worker创建多线程

5.1 Worker简介

Worker中无法使用DOM、alert等与界面有关的操作,也无法访问window对象的属性,但可以使用console.log、setTimeout等API,以及发送消息与主线程通信。

5.2 主线程与Worker通信

// 主线程
const worker = new Worker("worker.js");

// 发送消息给Worker
worker.postMessage({ start: 1, end: 100 });

// 接收Worker的消息
worker.onmessage = function (event) {
    console.log("Received from worker:", event.data);
};

// 处理Worker的错误
worker.onerror = function (error) {
    console.error("Worker error:", error.message);
};

// 关闭Worker
worker.terminate();

// worker.js 内容示例
/*
self.onmessage = function (event) {
    const { start, end } = event.data;
    const primes = findPrimes(start, end);
    self.postMessage(primes);
};

function findPrimes(start, end) {
    const primes = [];
    for (let i = start; i <= end; i++) {
        if (isPrime(i)) primes.push(i);
    }
    return primes;
}

function isPrime(num) {
    if (num < 2) return false;
    for (let i = 2; i <= Math.sqrt(num); i++) {
        if (num % i === 0) return false;
    }
    return true;
}
*/

5.3 Worker应用场景

Worker适用于计算密集型任务,如图像处理、数据加密、大数据排序等,可以避免阻塞主线程。

6. 客户端通信

6.1 跨文档通信

// 发送消息
const targetWindow = window.open("https://example.com");
targetWindow.postMessage("Hello", "https://example.com");

// 接收消息
window.addEventListener("message", function (event) {
    if (event.origin !== "https://example.com") return;
    console.log("Received message:", event.data);
    event.source.postMessage("Reply", event.origin);
});

6.2 WebSocket与服务器通信

const socket = new WebSocket("ws://localhost:3000");

socket.onopen = function () {
    console.log("Connected to server");
    socket.send("Hello Server");
};

socket.onmessage = function (event) {
    console.log("Received:", event.data);
};

socket.onclose = function () {
    console.log("Disconnected from server");
};

socket.onerror = function (error) {
    console.error("WebSocket error:", error);
};

7. 现代JavaScript特性

7.1 ES6+ 新特性

  • let/const:块级作用域变量和常量
  • 箭头函数:更简洁的函数语法
  • 模板字符串:支持多行字符串和变量插值
  • 解构赋值:从对象或数组中提取值
  • 默认参数:函数参数默认值
  • 剩余参数:处理不定数量的参数
  • 扩展运算符:展开数组或对象
  • Promise:处理异步操作
  • async/await:更简洁的异步代码
  • Class:面向对象编程
  • Module:模块化

7.2 异步编程

// Promise
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data received");
        }, 1000);
    });
}

fetchData()
    .then(data => console.log(data))
    .catch(error => console.error(error));

// async/await
async function getData() {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}

getData();

7.3 模块化

// 导出
// module.js
export const name = "John";
export function greet() {
    console.log("Hello!");
}

const user = {
    age: 30,
    city: "New York",
};
export default user;

// 导入
// main.js
import { name, greet } from "./module.js";
import user from "./module.js";

console.log(name); // John
greet(); // Hello!
console.log(user.age); // 30

7.4 重要数据结构

Symbol

Symbol是ES6新增的原始数据类型,表示唯一的标识符,常用于对象属性的键。

const sym = Symbol("description");
const obj = {
    [sym]: "value",
};
console.log(obj[sym]); // value

// Symbol的唯一性
const s1 = Symbol("id");
const s2 = Symbol("id");
console.log(s1 === s2); // false

// 常用的内置Symbol
const iterator = Symbol.iterator;
console.log(typeof iterator); // symbol

Set

Set是ES6新增的数据结构,类似于数组,但所有元素都是唯一的。

const set = new Set([1, 2, 3, 2, 1]);

console.log(set.size); // 3
console.log([...set]); // [1, 2, 3]

// 添加和删除元素
set.add(4);
set.delete(2);
console.log(set.has(2)); // false

// 遍历
for (const item of set) {
    console.log(item);
}

Map

Map是ES6新增的数据结构,类似于对象,但键可以是任意类型。

const map = new Map();

map.set("name", "John");
map.set(123, "number key");
map.set(true, "boolean key");

console.log(map.get("name")); // John
console.log(map.size); // 3

// 使用对象作为键
const objKey = { id: 1 };
map.set(objKey, "object value");
console.log(map.get(objKey)); // object value

// 遍历
for (const [key, value] of map) {
    console.log(`${key}: ${value}`);
}

WeakSet和WeakMap

WeakSet和WeakMap是弱引用版本,适用于临时存储对象引用,不阻止垃圾回收。

// WeakSet - 只能存放对象,且都是弱引用
const weakSet = new WeakSet();
let obj = { name: "John" };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
obj = null; // 对象可以被垃圾回收

// WeakMap - 键必须是对象,且都是弱引用
const weakMap = new WeakMap();
let key = { id: 1 };
weakMap.set(key, "value");
console.log(weakMap.get(key)); // value
key = null; // 对象可以被垃圾回收

7.5 代理与反射

Proxy

Proxy用于创建一个对象的代理,从而可以拦截和修改对象的基本操作。

const target = {
    name: "John",
    age: 30,
};

const handler = {
    get(target, prop, receiver) {
        console.log(`Getting ${prop}`);
        return Reflect.get(target, prop, receiver);
    },
    set(target, prop, value, receiver) {
        console.log(`Setting ${prop} to ${value}`);
        return Reflect.set(target, prop, value, receiver);
    },
};

const proxy = new Proxy(target, handler);
console.log(proxy.name); // Getting name / John
proxy.age = 31; // Setting age to 31

Reflect

Reflect是ES6新增的内置对象,提供了一些与Proxy拦截器方法对应的默认实现。

const obj = { name: "John" };

// 与Object方法对应,但更简洁
console.log(Reflect.has(obj, "name")); // true
console.log(Reflect.get(obj, "name")); // John
Reflect.set(obj, "age", 30);
console.log(Reflect.ownKeys(obj)); // ["name", "age"]

// Reflect.apply: 调用函数并绑定this和参数
function greet(greeting, punctuation) {
    return greeting + ", " + this.name + punctuation;
}
const person = { name: "Alice" };
console.log(Reflect.apply(greet, person, ["Hello", "!"])); // Hello, Alice!

// Reflect.construct: 相当于new操作符
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}
const instance = Reflect.construct(Person, ["Bob", 25]);
console.log(instance.name); // Bob
console.log(instance.age); // 25

// Reflect.getPrototypeOf: 获取原型对象
console.log(Reflect.getPrototypeOf(instance)); // Person.prototype

// Reflect.setPrototypeOf/setPrototypeOf: 设置原型对象
const proto = {
    greet() {
        return "Hi";
    },
};
Reflect.setPrototypeOf(instance, proto);
console.log(Reflect.getPrototypeOf(instance) === proto); // true

// Reflect.defineProperty: 定义属性
Reflect.defineProperty(obj, "city", { value: "Beijing", writable: true });
console.log(obj.city); // Beijing

// Reflect.deleteProperty: 删除属性
Reflect.deleteProperty(obj, "age");
console.log(obj.age); // undefined

// Reflect.isExtensible: 检查对象是否可扩展
console.log(Reflect.isExtensible(obj)); // true

// Reflect.preventExtensions: 阻止对象扩展
Reflect.preventExtensions(obj);
console.log(Reflect.isExtensible(obj)); // false

7.6 迭代器和生成器

迭代器

迭代器是一个对象,具有next方法,返回{value, done}形式的结果。

function createIterator(array) {
    let index = 0;
    return {
        next() {
            if (index < array.length) {
                return { value: array[index++], done: false };
            }
            return { value: undefined, done: true };
        },
    };
}

const iterator = createIterator([1, 2, 3]);
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().done); // false
console.log(iterator.next().done); // true

生成器

生成器是一种特殊的迭代器,使用 function 语法。

function* generator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = generator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
console.log(gen.next().done); // true

// 使用for...of遍历
for (const value of generator()) {
    console.log(value);
}

8. 性能优化

8.1 代码优化

  • 使用let/const代替var
  • 避免全局变量
  • 使用事件委托
  • 减少DOM操作
  • 使用requestAnimationFrame进行动画
  • 避免频繁重排和重绘

8.2 网络优化

  • 减少HTTP请求
  • 使用CDN
  • 启用Gzip压缩
  • 使用缓存
  • 延迟加载非关键资源

8.3 工具链

  • 使用Babel转译ES6+
  • 使用Webpack等构建工具
  • 使用ESLint进行代码检查
  • 使用Prettier进行代码格式化

9. 最佳实践

9.1 代码质量

  • 代码可读性:使用清晰的命名和注释
  • 错误处理:使用try/catch和Promise.catch
  • 安全性:防止XSS和CSRF攻击
  • 可维护性:遵循编码规范和设计模式
  • 性能:优化代码和网络请求

9.2 学习建议

通过以上知识点的学习和实践,你将能够掌握JavaScript的核心概念和现代特性,编写出高质量的JavaScript代码。

10. 数组高级方法

10.1 数组迭代方法

const numbers = [1, 2, 3, 4, 5];

// map: 转换数组元素
const doubled = numbers.map(num => num * 2); // [2, 4, 6, 8, 10]

// filter: 过滤数组元素
const evens = numbers.filter(num => num % 2 === 0); // [2, 4]

// reduce: 累加数组元素
const sum = numbers.reduce((acc, num) => acc + num, 0); // 15

// find: 查找第一个符合条件的元素
const found = numbers.find(num => num > 3); // 4

// some: 检查是否有元素满足条件
const hasEven = numbers.some(num => num % 2 === 0); // true

// every: 检查是否所有元素都满足条件
const allPositive = numbers.every(num => num > 0); // true

// forEach: 遍历数组
numbers.forEach(num => console.log(num));

10.2 数组操作方法

const arr = [1, 2, 3, 4, 5];

// flat: 扁平化嵌套数组
const nested = [1, [2, [3, [4, 5]]]];
const flattened = nested.flat(Infinity); // [1, 2, 3, 4, 5]

// flatMap: 映射并扁平化
const doubledFlat = [1, 2, 3].flatMap(num => [num, num * 2]); // [1, 2, 2, 4, 3, 6]

// includes: 检查是否包含元素
const hasValue = arr.includes(3); // true

// sort: 排序
const sorted = arr.sort((a, b) => a - b); // 升序
const sortedDesc = arr.sort((a, b) => b - a); // 降序

// slice: 提取子数组
const sliced = arr.slice(1, 3); // [2, 3]

// splice: 删除或插入元素
// splice(开始位置, 删除数量, 插入元素...)
const arr2 = [1, 2, 3, 4, 5];
arr2.splice(2, 1, 99); // 从索引2开始删除1个元素,插入99
console.log(arr2); // [1, 2, 99, 4, 5]

// concat: 连接数组
const concatenated = arr.concat([6, 7]); // [1, 2, 3, 4, 5, 6, 7]

// reverse: 反转数组(会修改原数组)
const original = [1, 2, 3, 4, 5];
const reversed = [...original].reverse(); // 使用展开运算符创建副本避免修改原数组
console.log(original); // [1, 2, 3, 4, 5] 原数组不变
console.log(reversed); // [5, 4, 3, 2, 1]

10.3 数组解构和展开

// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]

// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 数组拷贝
const copy = [...arr1]; // 浅拷贝

11. 正则表达式详细内容

11.1 正则表达式语法

// 基本语法
const pattern = /abc/; // 匹配 "abc"
const patternWithFlags = /abc/gi; // 全局匹配,不区分大小写

// 字符类
const digitPattern = /\d/; // 匹配数字
const letterPattern = /[a-zA-Z]/; // 匹配字母
const notDigitPattern = /\D/; // 匹配非数字
const whitespacePattern = /\s/; // 匹配空白字符

// 量词
const oneOrMore = /a+/; // 一个或多个 a
const zeroOrMore = /a*/; // 零个或多个 a
const zeroOrOne = /a?/; // 零个或一个 a
const exactlyN = /a{3}/; // 恰好3个 a
const atLeastN = /a{3,}/; // 至少3个 a
const rangeN = /a{3,5}/; // 3到5个 a

11.2 正则表达式标志

// g: 全局匹配
const globalPattern = /a/g;
"aaabbb".replace(globalPattern, "x"); // "xxxbbb"

// i: 不区分大小写
const caseInsensitive = /hello/i;
caseInsensitive.test("HELLO"); // true

// m: 多行模式
const multiline = /^hello/m;
multiline.test("first line\nhello"); // true

// u: Unicode模式,用于处理Unicode字符
const unicode = /\u{1F600}/u; // 匹配Unicode字符 😊

// y: 粘滞模式,从lastIndex位置开始匹配
const sticky = /hello/y;
sticky.lastIndex = 3;
console.log(sticky.test("hello hello")); // true

11.3 正则表达式应用

// 验证邮箱
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
emailPattern.test("test@example.com"); // true

// 验证手机号
const phonePattern = /^1[3-9]\d{9}$/;
phonePattern.test("13800138000"); // true

// 提取URL中的域名
const urlPattern = /https?:\/\/([^\/]+)/;
const url = "https://www.example.com/path";
const domain = url.match(urlPattern)[1]; // "www.example.com"

// 替换HTML标签
const html = "<div>Hello</div>";
const text = html.replace(/<[^>]*>/g, ""); // "Hello"

// 分割字符串
const csv = "apple,banana,cherry";
const fruits = csv.split(/,/); // ["apple", "banana", "cherry"]

12. 日期和时间处理

12.1 Date对象基础

// 创建日期对象
const now = new Date(); // 当前时间
const specificDate = new Date(2024, 0, 1); // 2024年1月1日
const fromString = new Date("2024-01-01"); // 从字符串创建
const fromTimestamp = new Date(1704067200000); // 从时间戳创建

// 获取日期部分
const year = now.getFullYear(); // 年份
const month = now.getMonth(); // 月份 (0-11)
const date = now.getDate(); // 日期
const day = now.getDay(); // 星期 (0-6)
const hours = now.getHours(); // 小时
const minutes = now.getMinutes(); // 分钟
const seconds = now.getSeconds(); // 秒
const milliseconds = now.getMilliseconds(); // 毫秒

// 设置日期部分
now.setFullYear(2025);
now.setMonth(0);
now.setDate(1);

12.2 日期格式化和解析

// 格式化日期
const date = new Date();
const formatted = date.toLocaleDateString(); // 本地日期格式
const timeFormatted = date.toLocaleTimeString(); // 本地时间格式
const dateTimeFormatted = date.toLocaleString(); // 本地日期时间格式

// 自定义格式化
function formatDate(date) {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}

// 解析日期字符串
const parsedDate = new Date("2024-01-01T00:00:00");

// 时间戳操作
const timestamp = Date.now(); // 当前时间戳
const dateFromTimestamp = new Date(timestamp);

12.3 日期计算和比较

// 日期计算
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);

const nextWeek = new Date(today);
nextWeek.setDate(today.getDate() + 7);

// 日期比较
const date1 = new Date("2024-01-01");
const date2 = new Date("2024-01-02");
const diff = date2 - date1; // 毫秒差
const daysDiff = diff / (1000 * 60 * 60 * 24); // 天数差

// 判断日期先后
if (date1 < date2) {
    console.log("date1 在 date2 之前");
}

13. Math对象和数学运算

13.1 Math对象常用方法

// 基本数学运算
Math.abs(-5); // 5: 绝对值
Math.round(4.7); // 5: 四舍五入
Math.floor(4.9); // 4: 向下取整
Math.ceil(4.1); // 5: 向上取整
Math.trunc(4.9); // 4: 去除小数部分

// 幂运算和对数
Math.pow(2, 3); // 8: 2的3次方
Math.sqrt(16); // 4: 平方根
Math.cbrt(27); // 3: 立方根
Math.log(10); // 自然对数
Math.log10(100); // 以10为底的对数

// 三角函数
Math.sin(Math.PI / 2); // 1
Math.cos(0); // 1
Math.tan(Math.PI / 4); // 1

// 常量
Math.PI; // 3.141592653589793
Math.E; // 2.718281828459045
Math.SQRT2; // 1.4142135623730951

13.2 随机数生成

// 生成0-1之间的随机数
const random = Math.random();

// 生成指定范围的随机整数
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

getRandomInt(1, 10); // 1-10之间的随机整数

// 生成随机字符串
function generateRandomString(length) {
    const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    let result = "";
    for (let i = 0; i < length; i++) {
        result += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return result;
}

generateRandomString(10); // 10位随机字符串

13.3 实际应用场景

// 计算两点之间的距离
function getDistance(x1, y1, x2, y2) {
    return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}

// 限制数值范围
function clamp(value, min, max) {
    return Math.min(Math.max(value, min), max);
}

clamp(15, 0, 10); // 10
clamp(5, 0, 10); // 5
clamp(-5, 0, 10); // 0

// 线性插值
function lerp(start, end, t) {
    return start + (end - start) * t;
}

lerp(0, 10, 0.5); // 5

14. 类型转换和类型检查

14.1 显式类型转换

// 转换为字符串
String(123); // "123"
String(true); // "true"
String(null); // "null"
(123).toString(); // "123"
true.toString(); // "true"

// 转换为数字
Number("123"); // 123
Number("123abc"); // NaN
Number(true); // 1
Number(false); // 0
Number(null); // 0
Number(undefined); // NaN

parseInt("123"); // 123
parseInt("123abc"); // 123
parseFloat("123.45"); // 123.45

// 转换为布尔值
Boolean(1); // true
Boolean(0); // false
Boolean("hello"); // true
Boolean(""); // false
Boolean(null); // false
Boolean(undefined); // false

14.2 隐式类型转换

// 字符串拼接
"5" + 5; // "55"
"5" + true; // "5true"

// 算术运算
"5" - 3; // 2
"5" * 2; // 10
"5" / 2; // 2.5

// 比较运算
5 == "5"; // true (类型转换)
5 === "5"; // false (严格相等)

// 逻辑运算
!0; // true
!!0; // false
!"hello"; // false

14.3 精确类型检查

// 基本类型检查
typeof "hello"; // "string"
typeof 123; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (历史遗留问题)
typeof {}; // "object"
typeof []; // "object"
typeof function () {}; // "function"

// 数组检查
Array.isArray([1, 2, 3]); // true
Array.isArray({}); // false

// NaN检查
Number.isNaN(NaN); // true
Number.isNaN(123); // false
isNaN(NaN); // true
isNaN("hello"); // true (会先转换为数字)

// 有限数检查
Number.isFinite(123); // true
Number.isFinite(Infinity); // false
Number.isFinite(NaN); // false

// 整数检查
Number.isInteger(123); // true
Number.isInteger(123.45); // false
Number.isInteger("123"); // false

// 安全整数检查
Number.isSafeInteger(9007199254740991); // true
Number.isSafeInteger(9007199254740992); // false

15. 闭包和作用域链

15.1 作用域和作用域链

// 全局作用域
var globalVar = "全局变量";

function outerFunction() {
    // 外部函数作用域
    var outerVar = "外部变量";

    function innerFunction() {
        // 内部函数作用域
        var innerVar = "内部变量";

        console.log(innerVar); // 可以访问
        console.log(outerVar); // 可以访问
        console.log(globalVar); // 可以访问
    }

    innerFunction();
    // console.log(innerVar); // 错误:无法访问
}

outerFunction();
// console.log(outerVar); // 错误:无法访问

15.2 闭包的概念

// 闭包:函数能够记住并访问其词法作用域
function createCounter() {
    let count = 0;

    return function () {
        count++;
        return count;
    };
}

const counter1 = createCounter();
const counter2 = createCounter();

console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1 (独立的闭包)
console.log(counter1()); // 3

15.3 闭包的实际应用

// 数据私有化
function createPerson(name) {
    let _name = name;

    return {
        getName: function () {
            return _name;
        },
        setName: function (newName) {
            _name = newName;
        },
    };
}

const person = createPerson("John");
console.log(person.getName()); // "John"
person.setName("Jane");
console.log(person.getName()); // "Jane"

// 函数工厂
function createMultiplier(multiplier) {
    return function (x) {
        return x * multiplier;
    };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

// 事件处理
function setupButtons() {
    for (let i = 0; i < 3; i++) {
        document.getElementById(`btn${i}`).onclick = function () {
            console.log(`Button ${i} clicked`);
        };
    }
}

15.4 内存泄漏和性能

// 避免内存泄漏
function setupEventHandlers() {
    const elements = document.querySelectorAll(".item");

    elements.forEach(element => {
        element.addEventListener("click", function handler() {
            console.log("Clicked");
            // 使用完后移除事件监听器
            element.removeEventListener("click", handler);
        });
    });
}

// 及时释放闭包引用
function createClosure() {
    const largeData = new Array(1000000).fill("data");

    return function () {
        // 使用largeData
        console.log(largeData.length);
    };
}

const closure = createClosure();
closure();
// 使用完后将引用设为null
closure = null;

16. 设计模式在JavaScript中的应用

16.1 单例模式

// 单例模式:确保一个类只有一个实例
const Singleton = (function () {
    let instance;

    function createInstance() {
        return {
            data: [],
            add: function (item) {
                this.data.push(item);
            },
            get: function () {
                return this.data;
            },
        };
    }

    return {
        getInstance: function () {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        },
    };
})();

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

16.2 工厂模式

// 工厂模式:创建对象的接口
class Car {
    constructor(make, model) {
        this.make = make;
        this.model = model;
    }

    drive() {
        console.log(`Driving ${this.make} ${this.model}`);
    }
}

class CarFactory {
    static createCar(type) {
        switch (type) {
            case "sedan":
                return new Car("Toyota", "Camry");
            case "suv":
                return new Car("Honda", "CR-V");
            case "sports":
                return new Car("Porsche", "911");
            default:
                throw new Error("Unknown car type");
        }
    }
}

const sedan = CarFactory.createCar("sedan");
const suv = CarFactory.createCar("suv");

16.3 观察者模式

// 观察者模式:对象间的一对多依赖关系
class Subject {
    constructor() {
        this.observers = [];
    }

    subscribe(observer) {
        this.observers.push(observer);
    }

    unsubscribe(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    notify(data) {
        this.observers.forEach(observer => observer.update(data));
    }
}

class Observer {
    constructor(name) {
        this.name = name;
    }

    update(data) {
        console.log(`${this.name} received: ${data}`);
    }
}

const subject = new Subject();
const observer1 = new Observer("Observer 1");
const observer2 = new Observer("Observer 2");

subject.subscribe(observer1);
subject.subscribe(observer2);

subject.notify("Hello Observers!");

16.4 模块模式

// 模块模式:封装私有变量和方法
const Module = (function () {
    let privateVar = 0;

    function privateFunction() {
        console.log("Private function");
    }

    return {
        publicMethod: function () {
            privateVar++;
            privateFunction();
            return privateVar;
        },
        publicVar: 42,
    };
})();

console.log(Module.publicVar); // 42
console.log(Module.publicMethod()); // 1
// Module.privateVar; // undefined
// Module.privateFunction(); // undefined

16.5 策略模式

// 策略模式:定义算法族,分别封装
const strategies = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => a / b,
};

function calculate(strategy, a, b) {
    if (!strategies[strategy]) {
        throw new Error("Unknown strategy");
    }
    return strategies[strategy](a, b);
}

console.log(calculate("add", 5, 3)); // 8
console.log(calculate("multiply", 5, 3)); // 15

17. 函数式编程

17.1 高阶函数

// 高阶函数:接收函数作为参数或返回函数
function withLogging(fn) {
    return function (...args) {
        console.log(`Calling ${fn.name} with`, args);
        const result = fn(...args);
        console.log(`Result:`, result);
        return result;
    };
}

const add = (a, b) => a + b;
const loggedAdd = withLogging(add);

loggedAdd(5, 3); // 输出日志并返回8

17.2 纯函数和副作用

// 纯函数:相同输入总是产生相同输出,无副作用
const pureAdd = (a, b) => a + b;

console.log(pureAdd(2, 3)); // 5
console.log(pureAdd(2, 3)); // 5

// 非纯函数:有副作用
let counter = 0;
const impureAdd = (a, b) => {
    counter++;
    return a + b;
};

// 避免副作用
const pureAddWithCounter = (a, b, count) => ({
    result: a + b,
    newCount: count + 1,
});

17.3 函数组合

// 函数组合:将多个函数组合成一个
const compose =
    (...fns) =>
    x =>
        fns.reduceRight((v, f) => f(v), x);

const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;

const composed = compose(square, double, addOne);
console.log(composed(3)); // square(double(3 + 1)) = square(8) = 64

// 管道:从左到右组合
const pipe =
    (...fns) =>
    x =>
        fns.reduce((v, f) => f(v), x);

const piped = pipe(addOne, double, square);
console.log(piped(3)); // square(double(3 + 1)) = 64

17.4 柯里化和偏函数

// 柯里化:将多参数函数转换为单参数函数链
const curry = fn => {
    const curried = (...args) => {
        if (args.length >= fn.length) {
            return fn(...args);
        }
        return (...more) => curried(...args, ...more);
    };
    return curried;
};

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

// 偏函数:预设部分参数
const partial =
    (fn, ...presetArgs) =>
    (...laterArgs) =>
        fn(...presetArgs, ...laterArgs);

const multiply = (a, b, c) => a * b * c;
const doubleAndTriple = partial(multiply, 2, 3);

console.log(doubleAndTriple(4)); // 24

18. TypeScript基础

18.1 类型系统

// 基本类型
let name: string = "John";
let age: number = 30;
let isStudent: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;

// 数组类型
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];

// 对象类型
interface Person {
    name: string;
    age: number;
}

let person: Person = {
    name: "John",
    age: 30,
};

// 联合类型
let value: string | number = "hello";
value = 42;

// 字面量类型
let direction: "up" | "down" | "left" | "right" = "up";

18.2 接口和类型别名

// 接口
interface User {
    id: number;
    name: string;
    email?: string; // 可选属性
    readonly createdAt: Date; // 只读属性
}

const user: User = {
    id: 1,
    name: "John",
    createdAt: new Date(),
};

// 类型别名
type ID = number | string;
type Coordinates = {
    x: number;
    y: number;
};

let userId: ID = 123;
let position: Coordinates = { x: 10, y: 20 };

18.3 泛型

// 泛型函数
function identity<T>(arg: T): T {
    return arg;
}

identity<string>("hello");
identity<number>(42);

// 泛型接口
interface Box<T> {
    value: T;
}

const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 42 };

// 泛型约束
interface Lengthwise {
    length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

logLength("hello"); // 5
logLength([1, 2, 3]); // 3

18.4 类和装饰器

// 类
class Animal {
    protected name: string;

    constructor(name: string) {
        this.name = name;
    }

    speak(): void {
        console.log(`${this.name} makes a sound`);
    }
}

class Dog extends Animal {
    breed: string;

    constructor(name: string, breed: string) {
        super(name);
        this.breed = breed;
    }

    speak(): void {
        console.log(`${this.name} barks`);
    }
}

const dog = new Dog("Buddy", "Golden Retriever");
dog.speak();

// 装饰器
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
class Greeter {
    greeting: string;

    constructor(message: string) {
        this.greeting = message;
    }

    greet() {
        return `Hello, ${this.greeting}`;
    }
}

19. 前端测试

19.1 单元测试基础

// Jest测试示例
describe("Math operations", () => {
    test("add two numbers", () => {
        expect(add(2, 3)).toBe(5);
    });

    test("subtract two numbers", () => {
        expect(subtract(5, 3)).toBe(2);
    });

    test("async operation", async () => {
        const result = await fetchData();
        expect(result).toBe("data");
    });
});

// 测试函数
function add(a, b) {
    return a + b;
}

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

async function fetchData() {
    return new Promise(resolve => {
        setTimeout(() => resolve("data"), 100);
    });
}

19.2 测试覆盖率

// 配置Jest覆盖率
module.exports = {
    collectCoverage: true,
    coverageDirectory: "coverage",
    collectCoverageFrom: ["src/**/*.{js,jsx}", "!src/**/*.test.{js,jsx}", "!src/**/*.d.ts"],
    coverageThreshold: {
        global: {
            branches: 80,
            functions: 80,
            lines: 80,
            statements: 80,
        },
    },
};

19.3 测试最佳实践

// AAA模式:Arrange, Act, Assert
describe("User service", () => {
    test("create user successfully", () => {
        // Arrange: 准备测试数据
        const userData = {
            name: "John",
            email: "john@example.com",
        };

        // Act: 执行被测试的代码
        const user = userService.create(userData);

        // Assert: 验证结果
        expect(user.name).toBe("John");
        expect(user.email).toBe("john@example.com");
        expect(user.id).toBeDefined();
    });
});

// Mock外部依赖
jest.mock("./api");
import { fetchUser } from "./api";

test("fetch user data", async () => {
    fetchUser.mockResolvedValue({ name: "John" });

    const data = await getUserData(1);

    expect(data.name).toBe("John");
    expect(fetchUser).toHaveBeenCalledWith(1);
});

20. Web API扩展

20.1 Canvas API

// 基本绘图
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");

// 绘制矩形
ctx.fillStyle = "red";
ctx.fillRect(10, 10, 100, 100);

// 绘制圆形
ctx.beginPath();
ctx.arc(150, 150, 50, 0, Math.PI * 2);
ctx.fillStyle = "blue";
ctx.fill();

// 绘制线条
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(200, 200);
ctx.strokeStyle = "green";
ctx.stroke();

// 绘制文本
ctx.font = "20px Arial";
ctx.fillStyle = "black";
ctx.fillText("Hello Canvas", 10, 50);

20.2 WebGL基础

// 初始化WebGL
const canvas = document.getElementById("webglCanvas");
const gl = canvas.getContext("webgl");

// 顶点着色器
const vsSource = `
  attribute vec4 aVertexPosition;
  void main() {
    gl_Position = aVertexPosition;
  }
`;

// 片段着色器
const fsSource = `
  void main() {
    gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
  }
`;

// 编译着色器
function loadShader(gl, type, source) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    return shader;
}

const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

// 创建程序
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

20.3 WebRTC

// 获取媒体流
async function startVideo() {
    try {
        const stream = await navigator.mediaDevices.getUserMedia({
            video: true,
            audio: true,
        });

        const videoElement = document.getElementById("localVideo");
        videoElement.srcObject = stream;
        videoElement.play();
    } catch (error) {
        console.error("Error accessing media devices:", error);
    }
}

// WebRTC连接
const peerConnection = new RTCPeerConnection({
    iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
});

peerConnection.onicecandidate = event => {
    if (event.candidate) {
        // 发送ICE候选到对方
        sendCandidate(event.candidate);
    }
};

peerConnection.ontrack = event => {
    const remoteVideo = document.getElementById("remoteVideo");
    remoteVideo.srcObject = event.streams[0];
};

20.4 Notification API

// 请求通知权限
function requestNotificationPermission() {
    Notification.requestPermission().then(permission => {
        if (permission === "granted") {
            showNotification();
        }
    });
}

// 显示通知
function showNotification() {
    const notification = new Notification("Hello!", {
        body: "This is a notification",
        icon: "/icon.png",
        tag: "unique-tag",
    });

    notification.onclick = () => {
        window.focus();
        notification.close();
    };
}

// 检查权限
if (Notification.permission === "default") {
    requestNotificationPermission();
}

21. 安全性深入

21.1 XSS攻击防护

// 输入转义
function escapeHtml(unsafe) {
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}

// 使用textContent而不是innerHTML
const userContent = escapeHtml(userInput);
element.textContent = userContent;

// CSP (Content Security Policy)
// 在HTTP头中设置
// Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com

21.2 CSRF攻击防护

// CSRF Token
function getCsrfToken() {
    return document.querySelector('meta[name="csrf-token"]').getAttribute("content");
}

// 在请求中包含CSRF Token
fetch("/api/data", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-CSRF-Token": getCsrfToken(),
    },
    body: JSON.stringify({ data: "value" }),
});

// SameSite Cookie属性
// Set-Cookie: sessionId=abc123; SameSite=Strict; Secure

21.3 数据加密和哈希

// 使用Web Crypto API进行哈希
async function hashPassword(password) {
    const encoder = new TextEncoder();
    const data = encoder.encode(password);

    const hashBuffer = await crypto.subtle.digest("SHA-256", data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");

    return hashHex;
}

// 生成随机值
function generateRandomBytes(length) {
    const array = new Uint8Array(length);
    crypto.getRandomValues(array);
    return Array.from(array)
        .map(b => b.toString(16).padStart(2, "0"))
        .join("");
}

// 使用示例
hashPassword("mypassword").then(hash => {
    console.log("Password hash:", hash);
});

22. 性能监控和分析

22.1 Performance API

// 测量页面加载时间
window.addEventListener("load", () => {
    const perfData = performance.getEntriesByType("navigation")[0];

    console.log("页面加载时间:", perfData.loadEventEnd - perfData.fetchStart);
    console.log("DNS查询时间:", perfData.domainLookupEnd - perfData.domainLookupStart);
    console.log("TCP连接时间:", perfData.connectEnd - perfData.connectStart);
    console.log("请求响应时间:", perfData.responseEnd - perfData.requestStart);
});

// 测量特定操作
const startMark = "operation-start";
const endMark = "operation-end";

performance.mark(startMark);

// 执行需要测量的操作
performExpensiveOperation();

performance.mark(endMark);
performance.measure("operation", startMark, endMark);

const measure = performance.getEntriesByName("operation")[0];
console.log("操作耗时:", measure.duration, "ms");

22.2 内存分析

// 获取内存使用情况
function getMemoryUsage() {
    if (performance.memory) {
        return {
            usedJSHeapSize: performance.memory.usedJSHeapSize,
            totalJSHeapSize: performance.memory.totalJSHeapSize,
            jsHeapSizeLimit: performance.memory.jsHeapSizeLimit,
        };
    }
    return null;
}

// 监控内存泄漏
let memorySnapshots = [];

function takeMemorySnapshot() {
    const memory = getMemoryUsage();
    if (memory) {
        memorySnapshots.push({
            timestamp: Date.now(),
            ...memory,
        });

        if (memorySnapshots.length > 10) {
            memorySnapshots.shift();
        }

        checkMemoryLeak();
    }
}

function checkMemoryLeak() {
    if (memorySnapshots.length < 2) return;

    const first = memorySnapshots[0];
    const last = memorySnapshots[memorySnapshots.length - 1];
    const growth = last.usedJSHeapSize - first.usedJSHeapSize;

    if (growth > 1000000) {
        // 超过1MB增长
        console.warn("可能的内存泄漏:", growth, "bytes");
    }
}

22.3 网络性能监控

// 监控资源加载
const resourceObserver = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
        console.log("资源加载:", entry.name);
        console.log("加载时间:", entry.duration, "ms");
        console.log("大小:", entry.transferSize, "bytes");
    }
});

resourceObserver.observe({ entryTypes: ["resource"] });

// 监控长任务
const longTaskObserver = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
        console.warn("长任务检测到:", entry.duration, "ms");
        console.log("开始时间:", entry.startTime);
    }
});

longTaskObserver.observe({ entryTypes: ["longtask"] });

23. 代码组织和架构

23.1 MVC模式

// Model: 数据模型
class UserModel {
    constructor(data) {
        this.data = data;
    }

    get name() {
        return this.data.name;
    }

    set name(value) {
        this.data.name = value;
    }
}

// View: 视图
class UserView {
    constructor(element) {
        this.element = element;
    }

    render(user) {
        this.element.innerHTML = `
      <h1>${user.name}</h1>
      <p>Email: ${user.email}</p>
    `;
    }
}

// Controller: 控制器
class UserController {
    constructor(model, view) {
        this.model = model;
        this.view = view;
    }

    handleNameChange(newName) {
        this.model.name = newName;
        this.view.render(this.model);
    }
}

// 使用
const model = new UserModel({ name: "John", email: "john@example.com" });
const view = new UserView(document.getElementById("app"));
const controller = new UserController(model, view);

view.render(model);

23.2 状态管理

// 简单的状态管理器
class Store {
    constructor(initialState) {
        this.state = initialState;
        this.listeners = [];
    }

    getState() {
        return this.state;
    }

    setState(newState) {
        this.state = { ...this.state, ...newState };
        this.notify();
    }

    subscribe(listener) {
        this.listeners.push(listener);
        return () => {
            this.listeners = this.listeners.filter(l => l !== listener);
        };
    }

    notify() {
        this.listeners.forEach(listener => listener(this.state));
    }
}

// 使用
const store = new Store({ count: 0, name: "John" });

store.subscribe(state => {
    console.log("State changed:", state);
});

store.setState({ count: 1 });
store.setState({ name: "Jane" });

23.3 依赖注入

// 服务类
class Logger {
    log(message) {
        console.log(`[LOG] ${message}`);
    }
}

class Database {
    constructor(logger) {
        this.logger = logger;
    }

    query(sql) {
        this.logger.log(`Executing query: ${sql}`);
        // 执行数据库查询
    }
}

class UserService {
    constructor(database, logger) {
        this.database = database;
        this.logger = logger;
    }

    getUser(id) {
        this.logger.log(`Getting user ${id}`);
        return this.database.query(`SELECT * FROM users WHERE id = ${id}`);
    }
}

// 依赖注入容器
class Container {
    constructor() {
        this.services = new Map();
    }

    register(name, factory) {
        this.services.set(name, factory);
    }

    get(name) {
        const factory = this.services.get(name);
        if (!factory) {
            throw new Error(`Service ${name} not found`);
        }
        return factory(this);
    }
}

// 使用
const container = new Container();
container.register("logger", () => new Logger());
container.register("database", c => new Database(c.get("logger")));
container.register("userService", c => new UserService(c.get("database"), c.get("logger")));

const userService = container.get("userService");
userService.getUser(1);

24. 浏览器兼容性处理

24.1 Polyfill使用

// 检测特性支持
function supportsFeature(feature) {
    return feature in window || feature in document;
}

// Array.prototype.includes polyfill
if (!Array.prototype.includes) {
    Array.prototype.includes = function (searchElement, fromIndex) {
        if (this == null) {
            throw new TypeError('"this" is null or not defined');
        }

        const O = Object(this);
        const len = O.length >>> 0;
        let k = fromIndex || 0;

        if (k < 0) {
            k = Math.max(0, len + k);
        }

        for (; k < len; k++) {
            if (O[k] === searchElement) {
                return true;
            }
        }
        return false;
    };
}

// Fetch API polyfill
if (!window.fetch) {
    window.fetch = function (url, options) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open(options?.method || "GET", url);
            xhr.onload = () =>
                resolve({
                    ok: xhr.status >= 200 && xhr.status < 300,
                    status: xhr.status,
                    text: () => xhr.responseText,
                });
            xhr.onerror = () => reject(new Error("Network error"));
            xhr.send(options?.body);
        });
    };
}

24.2 Babel配置

// babel.config.js
module.exports = {
    presets: [
        [
            "@babel/preset-env",
            {
                targets: {
                    browsers: ["> 1%", "last 2 versions", "not ie <= 11"],
                },
                useBuiltIns: "usage",
                corejs: 3,
            },
        ],
    ],
    plugins: [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-object-rest-spread",
    ],
};

24.3 特性检测和优雅降级

// 特性检测
const features = {
    flexbox: CSS.supports("display", "flex"),
    grid: CSS.supports("display", "grid"),
    webp: document.createElement("canvas").toDataURL("image/webp").indexOf("data:image/webp") === 0,
    touch: "ontouchstart" in window,
};

// 优雅降级
function applyLayout() {
    if (features.grid) {
        container.style.display = "grid";
    } else if (features.flexbox) {
        container.style.display = "flex";
    } else {
        container.style.display = "block";
    }
}

// 渐进增强
function enhanceWithWebP() {
    if (features.webp) {
        const images = document.querySelectorAll("img[data-webp]");
        images.forEach(img => {
            img.src = img.dataset.webp;
        });
    }
}

25. 响应式设计和移动端

25.1 媒体查询

// 检测视口大小
function getViewportSize() {
    return {
        width: window.innerWidth,
        height: window.innerHeight,
    };
}

// 监听视口变化
function setupResponsiveHandlers() {
    const handlers = {
        mobile: () => console.log("Mobile view"),
        tablet: () => console.log("Tablet view"),
        desktop: () => console.log("Desktop view"),
    };

    function checkViewport() {
        const width = window.innerWidth;

        if (width < 768) {
            handlers.mobile();
        } else if (width < 1024) {
            handlers.tablet();
        } else {
            handlers.desktop();
        }
    }

    window.addEventListener("resize", checkViewport);
    checkViewport();
}

25.2 触摸事件

// 触摸事件处理
function setupTouchHandlers() {
    const element = document.getElementById("touchElement");

    element.addEventListener("touchstart", e => {
        console.log("Touch start:", e.touches[0].clientX, e.touches[0].clientY);
    });

    element.addEventListener("touchmove", e => {
        e.preventDefault(); // 防止滚动
        console.log("Touch move:", e.touches[0].clientX, e.touches[0].clientY);
    });

    element.addEventListener("touchend", e => {
        console.log("Touch end");
    });

    // 手势识别
    let startX, startY;

    element.addEventListener("touchstart", e => {
        startX = e.touches[0].clientX;
        startY = e.touches[0].clientY;
    });

    element.addEventListener("touchend", e => {
        const endX = e.changedTouches[0].clientX;
        const endY = e.changedTouches[0].clientY;

        const diffX = endX - startX;
        const diffY = endY - startY;

        if (Math.abs(diffX) > Math.abs(diffY)) {
            if (diffX > 50) {
                console.log("Swipe right");
            } else if (diffX < -50) {
                console.log("Swipe left");
            }
        } else {
            if (diffY > 50) {
                console.log("Swipe down");
            } else if (diffY < -50) {
                console.log("Swipe up");
            }
        }
    });
}

25.3 设备API

// 电池状态API
if ("getBattery" in navigator) {
    navigator.getBattery().then(battery => {
        function updateBatteryStatus() {
            console.log("Battery level:", battery.level * 100, "%");
            console.log("Charging:", battery.charging);

            if (battery.charging) {
                console.log("Time to full:", battery.chargingTime, "seconds");
            } else {
                console.log("Time to empty:", battery.dischargingTime, "seconds");
            }
        }

        updateBatteryStatus();
        battery.addEventListener("levelchange", updateBatteryStatus);
        battery.addEventListener("chargingchange", updateBatteryStatus);
    });
}

// 震动API
function vibrate(pattern) {
    if ("vibrate" in navigator) {
        navigator.vibrate(pattern);
    }
}

vibrate([200, 100, 200]); // 震动200ms,暂停100ms,再震动200ms

// 屏幕方向API
window.addEventListener("orientationchange", () => {
    console.log("Orientation changed:", screen.orientation.angle);
});

// PWA基础
if ("serviceWorker" in navigator) {
    navigator.serviceWorker.register("/sw.js").then(registration => {
        console.log("Service Worker registered");
    });
}

26. 构建工具深入

26.1 Webpack高级配置

// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    entry: {
        main: "./src/index.js",
        vendor: "./src/vendor.js",
    },

    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "[name].[contenthash].js",
        chunkFilename: "[name].[contenthash].js",
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: ["@babel/preset-env"],
                    },
                },
            },
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                use: [
                    {
                        loader: "file-loader",
                        options: {
                            name: "[name].[hash].[ext]",
                            outputPath: "images/",
                        },
                    },
                ],
            },
        ],
    },

    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html",
            filename: "index.html",
        }),
        new MiniCssExtractPlugin({
            filename: "[name].[contenthash].css",
        }),
    ],

    optimization: {
        splitChunks: {
            chunks: "all",
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "vendor",
                    chunks: "all",
                },
            },
        },
    },
};

26.2 Vite使用

// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
    plugins: [react()],

    build: {
        outDir: "dist",
        assetsDir: "assets",
        sourcemap: true,
        rollupOptions: {
            output: {
                manualChunks: {
                    vendor: ["react", "react-dom"],
                    utils: ["lodash", "axios"],
                },
            },
        },
    },

    server: {
        port: 3000,
        proxy: {
            "/api": {
                target: "http://localhost:8080",
                changeOrigin: true,
                rewrite: path => path.replace(/^\/api/, ""),
            },
        },
    },
});

26.3 代码优化和压缩

// Terser配置(代码压缩)
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                terserOptions: {
                    compress: {
                        drop_console: true,
                        pure_funcs: ["console.log"],
                    },
                    mangle: {
                        safari10: true,
                    },
                },
                extractComments: false,
            }),
        ],
    },
};

// 图片优化
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");

module.exports = {
    module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif|svg)$/i,
                type: "asset",
                use: [
                    {
                        loader: ImageMinimizerPlugin.loader,
                        options: {
                            minimizer: {
                                implementation: ImageMinimizerPlugin.imageminGenerate,
                                options: {
                                    plugins: [
                                        ["imagemin-mozjpeg", { quality: 80 }],
                                        ["imagemin-pngquant", { quality: [0.65, 0.9] }],
                                    ],
                                },
                            },
                        },
                    },
                ],
            },
        ],
    },
};

27. 调试技巧

27.1 Chrome DevTools高级用法

// Console高级用法
console.log("Basic log");
console.info("Info message");
console.warn("Warning message");
console.error("Error message");

console.table([
    { name: "John", age: 30 },
    { name: "Jane", age: 25 },
]);

console.group("User Data");
console.log("Name: John");
console.log("Age: 30");
console.groupEnd();

console.time("Operation");
performOperation();
console.timeEnd("Operation");

console.assert(1 === 2, "This will fail");

// 条件断点
// 在代码中添加 debugger; 语句
function debugFunction() {
    const x = 5;
    debugger; // 程序会在这里暂停
    const y = x + 10;
    return y;
}

// 性能分析
console.profile("MyProfile");
// 执行需要分析的代码
console.profileEnd("MyProfile");

// 内存快照
console.memory; // 查看内存使用情况

27.2 断点调试

// 条件断点
function processArray(arr) {
    for (let i = 0; i < arr.length; i++) {
        // 当 i === 5 时触发断点
        if (i === 5) {
            debugger;
        }
        console.log(arr[i]);
    }
}

// 日志断点
function complexCalculation(a, b) {
    const result = a * b + Math.sqrt(a);
    // 在控制台输出调试信息
    console.log(`Calculation: ${a} * ${b} + sqrt(${a}) = ${result}`);
    return result;
}

// 监控变量
function monitorVariable(obj, prop) {
    let value = obj[prop];
    Object.defineProperty(obj, prop, {
        get() {
            console.log(`Getting ${prop}:`, value);
            return value;
        },
        set(newValue) {
            console.log(`Setting ${prop}:`, value, "->", newValue);
            value = newValue;
        },
    });
}

const user = { name: "John", age: 30 };
monitorVariable(user, "name");
user.name = "Jane"; // 控制台会输出设置信息
console.log(user.name); // 控制台会输出获取信息

27.3 网络调试

// 监控网络请求
const originalFetch = window.fetch;
window.fetch = function (...args) {
    console.log("Fetch request:", args[0]);
    return originalFetch.apply(this, args).then(response => {
        console.log("Fetch response:", response.status, response.url);
        return response;
    });
};

// 监控XMLHttpRequest
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.open = function (method, url) {
    this._method = method;
    this._url = url;
    return originalOpen.apply(this, arguments);
};

XMLHttpRequest.prototype.send = function () {
    console.log("XHR request:", this._method, this._url);
    this.addEventListener("load", () => {
        console.log("XHR response:", this.status, this._url);
    });
    return originalSend.apply(this, arguments);
};

通过以上27个章节的全面学习,你将能够掌握JavaScript从基础到高级的所有核心概念和现代特性,编写出高质量、高性能、安全的JavaScript代码。


文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录