ES6+ 关键特性 解构赋值 解构赋值是一种从数组或对象中提取值并赋给变量的语法糖。
数组解构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const [a, b, c] = [1 , 2 , 3 ];console .log (a, b, c); const [first, , third] = [1 , 2 , 3 ];console .log (first, third); const [head, ...tail] = [1 , 2 , 3 , 4 , 5 ];console .log (head); console .log (tail); const [x = 10 , y = 20 ] = [5 ];console .log (x, y); let m = 1 , n = 2 ; [m, n] = [n, m];console .log (m, n); const nested = [1 , [2 , 3 ], 4 ];const [a, [b, c], d] = nested;console .log (a, b, c, d);
对象解构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 const { name, age } = { name : '张三' , age : 25 };console .log (name, age); const { name : userName, age : userAge } = { name : '李四' , age : 30 };console .log (userName, userAge); const { x = 100 , y = 200 } = { x : 50 };console .log (x, y); const user = { id : 1 , profile : { name : '王五' , contacts : { email : 'wang@example.com' , phone : '1234567890' } } };const { profile : { name, contacts : { email } } } = user;console .log (name, email); function greet ({ name, greeting = '你好' } ) { console .log (`${greeting} , ${name} !` ); }greet ({ name : '赵六' }); greet ({ name : '孙七' , greeting : '早上好' });
展开运算符 展开运算符 (...) 可以将数组或对象"展开"成独立的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 const arr1 = [1 , 2 , 3 ];const arr2 = [4 , 5 , 6 ];const combined = [...arr1, ...arr2];console .log (combined); const newArr = [0 , ...arr1, 4 ];console .log (newArr); const copy = [...arr1];console .log (copy); const obj1 = { a : 1 , b : 2 };const obj2 = { c : 3 , d : 4 };const merged = { ...obj1, ...obj2 };console .log (merged); const updated = { ...obj1, b : 10 };console .log (updated); function sum (x, y, z ) { return x + y + z; }const nums = [1 , 2 , 3 ];console .log (sum (...nums)); const original = { nested : { value : 1 } };const shallowCopy = { ...original }; shallowCopy.nested .value = 2 ;console .log (original.nested .value );
可选链与空值合并 这两个操作符大大简化了处理可能为 null 或 undefined 的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 const user = { name : '张三' , address : { city : '北京' } };const zip1 = user && user.address && user.address .zip ;const zip2 = user?.address ?.zip ;console .log (zip2); const arr = [1 , 2 , 3 ];console .log (arr?.[0 ]); console .log (arr?.[5 ]); const obj = { greet : () => console .log ('你好' ) }; obj.greet ?.(); obj.sayHi ?.(); const value1 = 0 ?? 10 ;console .log (value1); const value2 = '' ?? 'default' ;console .log (value2); const value3 = null ?? 'default' ;console .log (value3); const value4 = undefined ?? 'default' ;console .log (value4); const val1 = 0 || 'default' ;console .log (val1); const val2 = 0 ?? 'default' ;console .log (val2); const config = { timeout : 3000 };const timeout = config?.timeout ?? 5000 ;console .log (timeout);
Promise 与异步处理 Promise 是处理异步操作的核心机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 const promise = new Promise ((resolve, reject ) => { setTimeout (() => { const success = true ; if (success) { resolve ('操作成功' ); } else { reject ('操作失败' ); } }, 1000 ); }); promise .then (result => console .log (result)) .catch (error => console .error (error)) .finally (() => console .log ('操作完成' ));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 (); return data; } catch (error) { console .error ('获取数据失败:' , error); throw error; } }async function fetchMultipleData ( ) { try { 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 }; } catch (error) { console .error ('批量获取失败:' , error); } }async function fetchWithTimeout (url, timeout = 5000 ) { const timeoutPromise = new Promise ((_, reject ) => { setTimeout (() => reject (new Error ('请求超时' )), timeout); }); return Promise .race ([ fetch (url).then (r => r.json ()), timeoutPromise ]); }async function fetchAllIgnoreErrors ( ) { const promises = [ fetch ('/api/users' ).then (r => r.json ()), fetch ('/api/invalid' ).then (r => r.json ()), fetch ('/api/posts' ).then (r => r.json ()) ]; const results = await Promise .allSettled (promises); const successful = results .filter (r => r.status === 'fulfilled' ) .map (r => r.value ); const failed = results .filter (r => r.status === 'rejected' ) .map (r => r.reason ); return { successful, failed }; }
模块化 ES6 模块系统使得代码组织更加清晰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 export const add = (a, b ) => a + b;export const subtract = (a, b ) => a - b;export const multiply = (a, b ) => a * b;export const divide = (a, b ) => { if (b === 0 ) throw new Error ('除数不能为零' ); return a / b; };export default class Calculator { constructor ( ) { this .result = 0 ; } add (n ) { this .result += n; return this ; } getResult ( ) { return this .result ; } }export const user = { name : '张三' , age : 25 };export function greet (name ) { console .log (`你好, ${name} !` ); }import Calculator , { add, subtract } from './math.js' ;import { user, greet } from './user.js' ;import * as MathUtils from './math.js' ; const calc = new Calculator (); calc.add (5 ).add (3 );console .log (calc.getResult ()); console .log (add (10 , 5 )); greet (user.name ); console .log (MathUtils .multiply (4 , 5 )); async function loadModule ( ) { const { heavyFunction } = await import ('./heavy-module.js' ); heavyFunction (); }export { default as Calculator } from './math.js' ;export * from './user.js' ;
核心概念深入 事件循环 (Event Loop) JavaScript 是单线程语言,通过事件循环实现异步操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 console .log ('1. 同步代码开始' );setTimeout (() => { console .log ('4. 宏任务:setTimeout' ); }, 0 );Promise .resolve () .then (() => { console .log ('3. 微任务:Promise.then' ); }) .then (() => { console .log ('3.5 微任务:Promise.then 链' ); });console .log ('2. 同步代码结束' );
事件循环流程:
执行同步代码 :调用栈中的代码优先执行检查微任务队列 :执行所有微任务(Promise.then、MutationObserver、queueMicrotask)执行渲染 :如果需要更新 DOM,浏览器会进行渲染检查宏任务队列 :执行一个宏任务(setTimeout、setInterval、I/O、UI 事件)重复步骤 2-4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 setTimeout (() => { console .log ('宏任务 1' ); }, 0 );queueMicrotask (() => { console .log ('微任务 1' ); });Promise .resolve ().then (() => { console .log ('微任务 2' ); });setTimeout (() => { console .log ('宏任务 2' ); }, 0 );
实际应用:避免阻塞主线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function heavyComputation ( ) { const start = Date .now (); while (Date .now () - start < 1000 ) { } }function chunkedComputation (items, callback ) { let index = 0 ; const chunkSize = 100 ; function processChunk ( ) { const end = Math .min (index + chunkSize, items.length ); for (; index < end; index++) { callback (items[index]); } if (index < items.length ) { setTimeout (processChunk, 0 ); } } processChunk (); }
闭包 (Closure) 闭包是指函数能够记住并访问其词法作用域,即使该函数在其词法作用域之外执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 function outer ( ) { let count = 0 ; return function inner ( ) { count++; console .log (`计数: ${count} ` ); }; }const counter = outer ();counter (); counter (); counter (); function createCounter (initialValue = 0 ) { let count = initialValue; return { increment : () => ++count, decrement : () => --count, getCount : () => count, reset : () => { count = initialValue; return count; } }; }const myCounter = createCounter (10 );console .log (myCounter.increment ()); console .log (myCounter.increment ()); console .log (myCounter.decrement ()); console .log (myCounter.getCount ()); console .log (myCounter.reset ()); function multiply (x ) { return function (y ) { return x * y; }; }const double = multiply (2 );const triple = multiply (3 );console .log (double (5 )); console .log (triple (5 )); function memoize (fn ) { const cache = new Map (); return function (...args ) { const key = JSON .stringify (args); if (cache.has (key)) { console .log ('从缓存获取' ); return cache.get (key); } const result = fn (...args); cache.set (key, result); console .log ('计算并缓存' ); return result; }; }const expensiveCalculation = memoize ((a, b ) => { console .log ('执行复杂计算...' ); return a + b; });console .log (expensiveCalculation (1 , 2 )); console .log (expensiveCalculation (1 , 2 )); console .log (expensiveCalculation (2 , 3 )); function createFunctions ( ) { const functions = []; for (var i = 0 ; i < 3 ; i++) { functions.push (() => console .log (i)); } return functions; }const funcs = createFunctions (); funcs[0 ](); funcs[1 ](); funcs[2 ](); function createFunctionsCorrect ( ) { const functions = []; for (let i = 0 ; i < 3 ; i++) { functions.push (() => console .log (i)); } return functions; }const funcsCorrect = createFunctionsCorrect (); funcsCorrect[0 ](); funcsCorrect[1 ](); funcsCorrect[2 ](); function createFunctionsWithClosure ( ) { const functions = []; for (var i = 0 ; i < 3 ; i++) { functions.push ((function (j ) { return () => console .log (j); })(i)); } return functions; }
原型链 (Prototype Chain) JavaScript 使用原型链实现继承和属性共享。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 function Person (name, age ) { this .name = name; this .age = age; }Person .prototype .greet = function ( ) { console .log (`你好, 我是${this .name} , ${this .age} 岁` ); };Person .prototype .toString = function ( ) { return `${this .name} (${this .age} )` ; };const person1 = new Person ('张三' , 25 );const person2 = new Person ('李四' , 30 ); person1.greet (); person2.greet (); console .log (person1.__proto__ === Person .prototype ); console .log (Person .prototype .__proto__ === Object .prototype ); console .log (Object .prototype .__proto__ === null ); console .log (person1.hasOwnProperty ('name' )); console .log (person1.hasOwnProperty ('greet' )); console .log ('greet' in person1); class Animal { constructor (name, sound ) { this .name = name; this .sound = sound; } speak ( ) { console .log (`${this .name} 发出${this .sound} 的声音` ); } static create (name, sound ) { return new Animal (name, sound); } }class Dog extends Animal { constructor (name, breed ) { super (name, '汪汪' ); this .breed = breed; } speak ( ) { console .log (`${this .name} (${this .breed} )汪汪叫` ); } fetch ( ) { console .log (`${this .name} 去捡球` ); } }const dog = new Dog ('旺财' , '金毛' ); dog.speak (); dog.fetch (); console .log (dog instanceof Dog ); console .log (dog instanceof Animal ); console .log (dog instanceof Object ); const dog2 = Animal .create ('小白' , '喵喵' );
this 绑定规则 this 的值取决于函数的调用方式,而不是定义方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 function globalFunction ( ) { console .log (this ); }globalFunction (); const obj = { name : '对象' , greet ( ) { console .log (`你好, ${this .name} ` ); } }; obj.greet (); const greet = obj.greet ;greet (); function introduce (greeting, punctuation ) { console .log (`${greeting} , 我是${this .name} ${punctuation} ` ); }const person = { name : '张三' }; introduce.call (person, '你好' , '!' ); introduce.apply (person, ['早上好' , '。' ]); const boundIntroduce = introduce.bind (person);boundIntroduce ('晚上好' , '~' ); function Person (name ) { this .name = name; }const p = new Person ('李四' );console .log (p.name ); const obj2 = { name : '箭头函数对象' , greet : function ( ) { setTimeout (function ( ) { console .log (`普通函数: ${this .name} ` ); }, 100 ); setTimeout (() => { console .log (`箭头函数: ${this .name} ` ); }, 100 ); } }; obj2.greet ();class Button { constructor (element ) { this .element = element; this .clickCount = 0 ; this .element .addEventListener ('click' , this .handleClick .bind (this )); this .element .addEventListener ('click' , () => this .handleClick ()); this .element .addEventListener ('click' , this .handleClickArrow ); } handleClick ( ) { this .clickCount ++; console .log (`点击次数: ${this .clickCount} ` ); } handleClickArrow = () => { this .clickCount ++; console .log (`点击次数: ${this .clickCount} ` ); } }
实用技巧 防抖与节流 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function debounce (func, delay ) { let timeoutId; return function (...args ) { clearTimeout (timeoutId); timeoutId = setTimeout (() => { func.apply (this , args); }, delay); }; }const searchInput = document .getElementById ('search' ); searchInput.addEventListener ('input' , debounce ((e ) => { console .log ('搜索:' , e.target .value ); }, 500 ));function throttle (func, interval ) { let lastTime = 0 ; return function (...args ) { const now = Date .now (); if (now - lastTime >= interval) { lastTime = now; func.apply (this , args); } }; }window .addEventListener ('scroll' , throttle (() => { console .log ('滚动位置:' , window .scrollY ); }, 100 ));
深拷贝与浅拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 const shallowCopy1 = { ...original };const shallowCopy2 = Object .assign ({}, original);const shallowCopy3 = [...originalArray];function deepClone (obj, hash = new WeakMap () ) { if (obj === null || typeof obj !== 'object' ) { return obj; } if (hash.has (obj)) { return hash.get (obj); } if (obj instanceof Date ) { return new Date (obj); } if (obj instanceof RegExp ) { return new RegExp (obj.source , obj.flags ); } if (Array .isArray (obj)) { const clone = []; hash.set (obj, clone); obj.forEach ((item, index ) => { clone[index] = deepClone (item, hash); }); return clone; } const clone = {}; hash.set (obj, clone); Object .keys (obj).forEach (key => { clone[key] = deepClone (obj[key], hash); }); return clone; }const original = { name : '张三' , nested : { value : 1 }, date : new Date (), regex : /test/gi };const cloned = structuredClone (original);