JavaScript 学习笔记
1. JavaScript 基础概念
1.1 什么是 JavaScript?
定义 :一种轻量级、解释型的编程语言,是 Web 开发的三大核心技术之一
运行环境 :浏览器(前端)、Node.js(后端/工具链)
特点 :
动态类型:变量类型在运行时确定
单线程:基于事件循环处理异步任务
原型继承:基于原型链实现面向对象
函数是一等公民:函数可以作为参数、返回值、赋值给变量
1.2 JavaScript 在网页中的引入方式
<script > console .log ('Hello World' ); </script > <script src ="main.js" > </script > <script src ="main.js" defer > </script > <script src ="main.js" async > </script >
推荐将 script 标签放在 body 底部,或使用 defer 属性,避免阻塞 HTML 解析。
1.3 输出与调试
console .log ('普通信息' );console .warn ('警告信息' );console .error ('错误信息' );console .table ([{ name : '张三' , age : 18 }]); console .time ('计时' );console .timeEnd ('计时' ); alert ('提示' );confirm ('确认?' ); prompt ('请输入' );
2. 变量与数据类型
2.1 变量声明
var name = '张三' ;let age = 18 ;age = 19 ; const PI = 3.14159 ;
三者对比
var
let
const
作用域
函数级
块级
块级
变量提升
有
无
无
重复声明
允许
不允许
不允许
重新赋值
允许
允许
不允许
推荐程度
不推荐
推荐
优先推荐
实际开发原则:默认使用 const,需要重新赋值时改用 let,避免使用 var。
2.2 数据类型
JavaScript 共有 8 种数据类型:
基本类型(7种)
let num = 42 ;let float = 3.14 ;let inf = Infinity ;let nan = NaN ; let str = '单引号' ;let str2 = "双引号" ;let str3 = `模板字符串` ;let bool = true ;let bool2 = false ;let undef;console .log (undef); let empty = null ;let sym = Symbol ('描述' );let sym2 = Symbol ('描述' );console .log (sym === sym2); let big = 9007199254740991n ;
引用类型(1种)
let obj = { name : '张三' };let arr = [1 , 2 , 3 ];let fn = function ( ) {};
2.3 类型检测
typeof 42 typeof 'hello' typeof true typeof undefined typeof null typeof {} typeof [] typeof function ( ){} [] instanceof Array {} instanceof Object Array .isArray ([]) Array .isArray ({}) Object .prototype .toString .call (42 ) Object .prototype .toString .call ([]) Object .prototype .toString .call (null )
2.4 类型转换
显式转换
Number ('42' ) Number ('abc' ) Number (true ) Number (false ) Number (null ) Number (undefined ) parseInt ('42px' ) parseFloat ('3.14rem' ) String (42 ) String (true ) String (null ) (42 ).toString () (255 ).toString (16 ) Boolean (0 ) Boolean ('' ) Boolean (null ) Boolean (undefined ) Boolean (NaN ) Boolean (false )
隐式转换(需要注意的坑)
'5' + 3 '5' - 3 '5' * '3' 0 == false '' == false null == undefined null == 0 0 === false 1 === '1'
3. 运算符
3.1 算术运算符
let a = 10 , b = 3 ;a + b a - b a * b a / b a % b a ** b let x = 5 ;x++ ++x x-- --x
3.2 比较运算符
5 > 3 5 < 3 5 >= 5 5 <= 4 5 == '5' 5 === '5' 5 != '5' 5 !== '5'
3.3 逻辑运算符
true && true true && false 'a' && 'b' '' && 'b' true || false false || false '' || 'default' !true !0 null ?? 'default' undefined ?? 'default' 0 ?? 'default' '' ?? 'default'
3.4 赋值运算符
let x = 10 ;x += 5 ; x -= 3 ; x *= 2 ; x /= 4 ; x %= 4 ; x **= 3 ; x ??= 'default' ; x ||= 'default' ; x &&= 100 ;
3.5 其他运算符
let result = age >= 18 ? '成年' : '未成年' ;let user = null ;user?.name user?.address ?.city user?.getName ?.() let arr = [1 , 2 , 3 ];let arr2 = [...arr, 4 , 5 ]; let obj = { a : 1 };let obj2 = { ...obj, b : 2 };
4. 流程控制
4.1 条件语句
let score = 85 ;if (score >= 90 ) { console .log ('优秀' ); } else if (score >= 70 ) { console .log ('良好' ); } else if (score >= 60 ) { console .log ('及格' ); } else { console .log ('不及格' ); } let day = 3 ;switch (day) { case 1 : console .log ('周一' ); break ; case 2 : console .log ('周二' ); break ; case 3 : case 4 : console .log ('周三或周四' ); break ; default : console .log ('其他' ); }
4.2 循环语句
for (let i = 0 ; i < 5 ; i++) { console .log (i); } let i = 0 ;while (i < 5 ) { console .log (i); i++; } let j = 0 ;do { console .log (j); j++; } while (j < 5 ); let arr = [10 , 20 , 30 ];for (let value of arr) { console .log (value); } let obj = { a : 1 , b : 2 , c : 3 };for (let key in obj) { console .log (key, obj[key]); }
4.3 循环控制
for (let i = 0 ; i < 10 ; i++) { if (i === 5 ) break ; console .log (i); } for (let i = 0 ; i < 5 ; i++) { if (i === 2 ) continue ; console .log (i); } outer : for (let i = 0 ; i < 3 ; i++) { for (let j = 0 ; j < 3 ; j++) { if (j === 1 ) break outer; console .log (i, j); } }
4.4 异常处理
try { let result = JSON .parse ('invalid json' ); } catch (error) { console .error (error.name ); console .error (error.message ); } finally { console .log ('无论是否报错都会执行' ); } function divide (a, b ) { if (b === 0 ) { throw new Error ('除数不能为0' ); } return a / b; } class ValidationError extends Error { constructor (message ) { super (message); this .name = 'ValidationError' ; } } try { throw new ValidationError ('输入不合法' ); } catch (error) { if (error instanceof ValidationError ) { console .log ('验证错误:' , error.message ); } }
5. 函数
5.1 函数声明方式
function greet (name ) { return 'Hello, ' + name; } const greet = function (name ) { return 'Hello, ' + name; }; const greet = (name ) => { return 'Hello, ' + name; }; const greet = name => 'Hello, ' + name;(function ( ) { console .log ('立即执行' ); })();
5.2 函数参数
function greet (name = '陌生人' ) { return 'Hello, ' + name; } greet () greet ('张三' ) function sum (first, ...rest ) { return rest.reduce ((acc, val ) => acc + val, first); } sum (1 , 2 , 3 , 4 ) function fn ( ) { console .log (arguments ); console .log ([...arguments ]); } function createUser ({ name, age = 18 , role = 'user' } ) { return { name, age, role }; } createUser ({ name : '张三' , age : 25 });
5.3 作用域与闭包
let globalVar = '全局变量' ;function outer ( ) { let outerVar = '外部变量' ; function inner ( ) { let innerVar = '内部变量' ; console .log (globalVar); console .log (outerVar); } inner (); } function makeCounter ( ) { let count = 0 ; return { increment ( ) { count++; }, decrement ( ) { count--; }, getCount ( ) { return count; } }; } const counter = makeCounter ();counter.increment (); counter.increment (); counter.getCount ();
5.4 this 关键字
const obj = { name : '张三' , greet : function ( ) { console .log (this .name ); } }; const obj2 = { name : '李四' , greet : () => { console .log (this .name ); }, greetLater : function ( ) { setTimeout (() => { console .log (this .name ); }, 1000 ); } }; function sayName ( ) { console .log (this .name ); } const user = { name : '王五' };sayName.call (user); sayName.apply (user, []); const bound = sayName.bind (user);bound ();
5.5 高阶函数
function execute (fn, value ) { return fn (value); } execute (x => x * 2 , 5 ); function multiplier (factor ) { return num => num * factor; } const double = multiplier (2 );const triple = multiplier (3 );double (5 ); triple (5 ); function curry (fn ) { return function curried (...args ) { if (args.length >= fn.length ) { return fn (...args); } return (...more ) => curried (...args, ...more); }; } const add = (a, b, c ) => a + b + c;const curriedAdd = curry (add);curriedAdd (1 )(2 )(3 ); curriedAdd (1 , 2 )(3 );
6. 数组
6.1 数组创建
const arr = [1 , 2 , 3 , 4 , 5 ];const arr2 = new Array (3 ); const arr3 = new Array (1 , 2 , 3 ); Array .from ('hello' ); Array .from ({ length : 3 }, (_, i ) => i + 1 ); Array .of (1 , 2 , 3 );
6.2 数组基本操作
const arr = [1 , 2 , 3 , 4 , 5 ];arr[0 ] arr.at (-1 ) arr[0 ] = 10 ; arr.length Array .isArray (arr)
6.3 增删操作
const arr = [1 , 2 , 3 ];arr.push (4 , 5 ); arr.pop (); arr.unshift (0 ); arr.shift (); const arr2 = [1 , 2 , 3 , 4 , 5 ];arr2.splice (1 , 2 ); arr2.splice (1 , 0 , 10 , 20 ); arr2.splice (1 , 1 , 99 );
6.4 查找操作
const arr = [1 , 2 , 3 , 2 , 5 ];arr.indexOf (2 ) arr.lastIndexOf (2 ) arr.includes (3 ) const users = [ { id : 1 , name : '张三' }, { id : 2 , name : '李四' }, ]; users.find (u => u.id === 2 ) users.findIndex (u => u.id === 2 ) arr.findLast (n => n < 4 ) arr.findLastIndex (n => n < 4 )
6.5 遍历操作
const arr = [1 , 2 , 3 , 4 , 5 ];arr.forEach ((item, index ) => { console .log (index, item); }); const doubled = arr.map (n => n * 2 ); const evens = arr.filter (n => n % 2 === 0 ); const sum = arr.reduce ((acc, n ) => acc + n, 0 ); const str = ['a' , 'b' , 'c' ].reduceRight ((acc, s ) => acc + s, '' ); arr.every (n => n > 0 ) arr.some (n => n > 4 ) [1 , [2 , [3 , [4 ]]]].flat () [1 , [2 , [3 , [4 ]]]].flat (Infinity ) [[1 , 2 ], [3 , 4 ]].flatMap (x => x)
6.6 排序与变换
const arr = [3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ];arr.sort ((a, b ) => a - b); arr.sort ((a, b ) => b - a); [1 , 2 , 3 ].reverse (); const arr2 = [1 , 2 , 3 , 4 , 5 ];arr2.slice (1 , 3 ) arr2.slice (-2 ) arr2.slice () [1 , 2 ].concat ([3 , 4 ], [5 , 6 ]) [1 , 2 , 3 ].join ('-' ) [1 , 2 , 3 ].join ('' ) new Array (5 ).fill (0 ) [1 , 2 , 3 , 4 , 5 ].fill (0 , 2 , 4 )
6.7 数组解构
const [a, b, c] = [1 , 2 , 3 ];console .log (a, b, c); const [first, , third] = [1 , 2 , 3 ];const [x = 10 , y = 20 ] = [5 ];console .log (x, y); const [head, ...tail] = [1 , 2 , 3 , 4 ];console .log (head, tail); let m = 1 , n = 2 ;[m, n] = [n, m]; console .log (m, n);
7. 对象
7.1 对象创建
const user = { name : '张三' , age : 18 , greet ( ) { return 'Hello, ' + this .name ; } }; function User (name, age ) { this .name = name; this .age = age; } const user2 = new User ('李四' , 20 );const proto = { greet ( ) { return 'Hello' ; } };const obj = Object .create (proto);
7.2 属性操作
const obj = { name : '张三' , age : 18 };obj.name obj['name' ] const key = 'age' ;obj[key] obj.email = 'test@example.com' ; obj['phone' ] = '123456' ; delete obj.phone ;'name' in obj obj.hasOwnProperty ('name' ) Object .hasOwn (obj, 'name' ) const name = '张三' ;const age = 18 ;const user = { name, age }; const prefix = 'user' ;const config = { [prefix + 'Name' ]: '张三' , [`${prefix} Age` ]: 18 };
7.3 对象遍历
const obj = { a : 1 , b : 2 , c : 3 };for (let key in obj) { if (obj.hasOwnProperty (key)) { console .log (key, obj[key]); } } Object .keys (obj) Object .values (obj) Object .entries (obj) Object .entries (obj).forEach (([key, value] ) => { console .log (key, value); });
7.4 对象操作
const target = { a : 1 };const source = { b : 2 , c : 3 };Object .assign (target, source); const merged = { ...target, ...source, d : 4 };const copy = { ...obj };const copy2 = Object .assign ({}, obj);const deep = JSON .parse (JSON .stringify (obj)); const deep2 = structuredClone (obj); const frozen = Object .freeze ({ x : 1 });frozen.x = 2 ; frozen.x Object .fromEntries ([['a' , 1 ], ['b' , 2 ]]); Object .fromEntries (new Map ([['a' , 1 ]]));
7.5 对象解构
const user = { name : '张三' , age : 18 , city : '北京' };const { name, age } = user;const { name : userName, age : userAge } = user;const { name, role = 'user' } = user;const { address : { city, street } } = { address : { city : '北京' , street : '长安街' } }; const { name : n, ...rest } = user;console .log (rest); function display ({ name, age = 18 } ) { console .log (name, age); } display ({ name : '张三' });
8. 字符串
8.1 字符串基本操作
const str = 'Hello World' ;str.length str[0 ] str.at (-1 ) str.toUpperCase () str.toLowerCase () ' hello ' .trim () ' hello ' .trimStart () ' hello ' .trimEnd () '5' .padStart (3 , '0' ) '5' .padEnd (3 , '0' ) 'ab' .repeat (3 )
8.2 字符串查找
const str = 'Hello World Hello' ;str.indexOf ('Hello' ) str.lastIndexOf ('Hello' ) str.includes ('World' ) str.startsWith ('Hello' ) str.startsWith ('World' , 6 ) str.endsWith ('Hello' ) str.endsWith ('World' , 11 )
8.3 字符串提取与替换
const str = 'Hello World' ;str.slice (0 , 5 ) str.slice (-5 ) str.substring (6 , 11 ) 'a,b,c' .split (',' ) 'abc' .split ('' ) 'abc' .split ('' , 2 ) str.replace ('World' , 'JS' ) str.replaceAll ('Hello' , 'Hi' ) str.replace (/Hello/g , 'Hi' )
8.4 模板字符串
const name = '张三' ;const age = 18 ;const msg = `我叫${name} ,今年${age} 岁` ;const result = `${age >= 18 ? '成年' : '未成年' } ` ;const html = ` <div> <p>${name} </p> </div> ` ;function highlight (strings, ...values ) { return strings.reduce ((result, str, i ) => { return result + str + (values[i] ? `<mark>${values[i]} </mark>` : '' ); }, '' ); } const output = highlight`姓名:${name} ,年龄:${age} ` ;
8.5 正则表达式基础
const reg1 = /hello/i ; const reg2 = new RegExp ('hello' , 'i' ); const str = 'Hello World 123' ;/\d+/ .test (str) str.match (/\d+/ ) str.match (/[a-z]+/gi ) str.replace (/\d+/ , '***' )
9. Map 与 Set
9.1 Map
Map 是键值对集合,与普通对象的区别是:键可以是任意类型,且保持插入顺序。
const map = new Map ();const map2 = new Map ([['a' , 1 ], ['b' , 2 ]]); map.set ('name' , '张三' ); map.set (1 , 'number key' ); map.set (true , 'bool key' ); map.set ({}, 'object key' ); map.get ('name' ) map.has ('name' ) map.delete ('name' ) map.size map.clear () const m = new Map ([['a' , 1 ], ['b' , 2 ], ['c' , 3 ]]);m.forEach ((value, key ) => console .log (key, value)); for (let [key, value] of m) { console .log (key, value); }[...m.keys ()] [...m.values ()] [...m.entries ()] const obj = Object .fromEntries (m); const map3 = new Map (Object .entries (obj));
9.2 Set
Set 是值的集合,所有值都是唯一的,不允许重复。
const set = new Set ();const set2 = new Set ([1 , 2 , 3 , 2 , 1 ]); set.add (1 ); set.add (2 ); set.add (1 ); set.has (1 ) set.delete (1 ) set.size set.clear () const s = new Set ([1 , 2 , 3 ]);s.forEach (value => console .log (value)); for (let value of s) { console .log (value); }[...s] const arr = [1 , 2 , 3 , 2 , 1 , 3 ];const unique = [...new Set (arr)]; const a = new Set ([1 , 2 , 3 , 4 ]);const b = new Set ([3 , 4 , 5 , 6 ]);const union = new Set ([...a, ...b]); const intersection = new Set ([...a].filter (x => b.has (x))); const difference = new Set ([...a].filter (x => !b.has (x)));
9.3 WeakMap 与 WeakSet
const wm = new WeakMap ();let obj = {};wm.set (obj, '一些数据' ); obj = null ; const privateData = new WeakMap ();class User { constructor (name ) { privateData.set (this , { name }); } getName ( ) { return privateData.get (this ).name ; } } const ws = new WeakSet ();let element = document .querySelector ('div' );ws.add (element); ws.has (element);
10. 面向对象与原型
10.1 原型链
const arr = [1 , 2 , 3 ];arr.__proto__ === Array .prototype Array .prototype .__proto__ === Object .prototype Object .prototype .__proto__ === null arr.push arr.toString
10.2 构造函数
function Person (name, age ) { this .name = name; this .age = age; } Person .prototype .greet = function ( ) { return `我叫${this .name} ,今年${this .age} 岁` ; }; Person .create = function (name, age ) { return new Person (name, age); }; const p1 = new Person ('张三' , 18 );const p2 = new Person ('李四' , 20 );p1.greet () p1 instanceof Person p1.constructor === Person
10.3 Class 语法(ES6)
class Person { #id; static count = 0 ; constructor (name, age ) { this .#id = Math .random (); this .name = name; this .age = age; Person .count ++; } greet ( ) { return `我叫${this .name} ,今年${this .age} 岁` ; } static create (name, age ) { return new Person (name, age); } get info () { return `${this .name} (${this .age} )` ; } set info (val ) { [this .name , this .age ] = val.split (',' ); } } const p = new Person ('张三' , 18 );p.greet () p.info Person .count
10.4 继承
class Animal { constructor (name ) { this .name = name; } speak ( ) { return `${this .name} 发出了声音` ; } toString ( ) { return `Animal(${this .name} )` ; } } class Dog extends Animal { constructor (name, breed ) { super (name); this .breed = breed; } speak ( ) { return `${this .name} 汪汪叫` ; } describe ( ) { return `${super .speak()} ,品种是${this .breed} ` ; } } const dog = new Dog ('小黑' , '拉布拉多' );dog.speak () dog.describe () dog instanceof Dog dog instanceof Animal
10.5 Mixin 模式
const Serializable = (superclass ) => class extends superclass { serialize ( ) { return JSON .stringify (this ); } static deserialize (json ) { return Object .assign (new this (), JSON .parse (json)); } }; const Validatable = (superclass ) => class extends superclass { validate ( ) { return Object .keys (this ).every (key => this [key] !== null ); } }; class Base { constructor (data ) { Object .assign (this , data); } } class User extends Serializable (Validatable (Base )) { constructor (data ) { super (data); } } const user = new User ({ name : '张三' , age : 18 });user.validate () user.serialize ()
11. 异步编程
11.1 同步与异步
console .log ('第一行' );console .log ('第二行' );console .log ('第三行' );console .log ('开始' );setTimeout (() => { console .log ('异步任务' ); }, 1000 ); console .log ('结束' );
11.2 回调函数
function fetchData (callback ) { setTimeout (() => { const data = { name : '张三' }; callback (null , data); }, 1000 ); } fetchData ((error, data ) => { if (error) { console .error (error); return ; } console .log (data); }); fetchUser (id, (err, user ) => { fetchOrders (user.id , (err, orders ) => { fetchOrderDetail (orders[0 ].id , (err, detail ) => { }); }); });
11.3 Promise
Promise 是对异步操作的封装,有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
const promise = new Promise ((resolve, reject ) => { const success = true ; if (success) { resolve ('成功的数据' ); } else { reject (new Error ('失败原因' )); } }); promise .then (data => { console .log (data); return '下一步的数据' ; }) .then (data => { console .log (data); }) .catch (error => { console .error (error); }) .finally (() => { console .log ('无论成败都执行' ); }); function delay (ms ) { return new Promise (resolve => setTimeout (resolve, ms)); } function fetchUser (id ) { return new Promise ((resolve, reject ) => { setTimeout (() => { if (id > 0 ) { resolve ({ id, name : '张三' }); } else { reject (new Error ('无效的ID' )); } }, 1000 ); }); }
Promise 静态方法
Promise .all ([ fetchUser (1 ), fetchUser (2 ), fetchUser (3 ) ]).then (([user1, user2, user3] ) => { console .log (user1, user2, user3); }).catch (error => { console .error ('有一个请求失败' , error); }); Promise .allSettled ([ fetchUser (1 ), fetchUser (-1 ), ]).then (results => { results.forEach (result => { if (result.status === 'fulfilled' ) { console .log ('成功' , result.value ); } else { console .log ('失败' , result.reason ); } }); }); Promise .race ([ fetchUser (1 ), delay (500 ).then (() => { throw new Error ('超时' ); }) ]).then (user => { console .log (user); }).catch (error => { console .error (error); }); Promise .any ([ Promise .reject (new Error ('失败1' )), fetchUser (1 ), fetchUser (2 ) ]).then (user => { console .log (user); });
11.4 async / await
async/await 是基于 Promise 的语法糖,让异步代码看起来像同步代码。
async function fetchUserData (id ) { try { const user = await fetchUser (id); const orders = await fetchOrders (user.id ); return { user, orders }; } catch (error) { console .error ('请求失败' , error); throw error; } } fetchUserData (1 ).then (data => console .log (data));async function fetchAll ( ) { const user1 = await fetchUser (1 ); const user2 = await fetchUser (2 ); const [user1, user2] = await Promise .all ([ fetchUser (1 ), fetchUser (2 ) ]); } const data = await fetchUser (1 );
11.5 事件循环
console .log ('1' );setTimeout (() => console .log ('2' ), 0 ); Promise .resolve () .then (() => console .log ('3' )) .then (() => console .log ('4' )); console .log ('5' );
12. DOM 操作
12.1 获取元素
document .querySelector ('.box' ) document .querySelectorAll ('.item' ) document .getElementById ('app' )document .getElementsByClassName ('box' ) document .getElementsByTagName ('div' )document .documentElement document .head document .body
12.2 元素内容与属性
const el = document .querySelector ('.box' );el.textContent el.innerHTML el.innerText el.getAttribute ('href' ) el.setAttribute ('href' , '#' ) el.removeAttribute ('disabled' ) el.hasAttribute ('disabled' ) el.dataset .userId el.dataset .userId = '456' el.classList .add ('active' ) el.classList .remove ('active' ) el.classList .toggle ('active' ) el.classList .contains ('active' ) el.classList .replace ('old' , 'new' )
12.3 样式操作
const el = document .querySelector ('.box' );el.style .color = 'red' ; el.style .backgroundColor = '#fff' ; el.style .cssText = 'color: red; font-size: 16px;' ; const styles = getComputedStyle (el);styles.color styles.fontSize el.style .setProperty ('--color' , 'red' ); getComputedStyle (el).getPropertyValue ('--color' );
12.4 节点操作
const div = document .createElement ('div' );const text = document .createTextNode ('文本节点' );const fragment = document .createDocumentFragment (); const parent = document .querySelector ('.container' );const child = document .querySelector ('.item' );parent.appendChild (div) parent.prepend (div) parent.insertBefore (div, child) child.before (div) child.after (div) parent.replaceChild (div, child) child.replaceWith (div) parent.removeChild (child) child.remove () const clone = child.cloneNode (true ) const fragment2 = document .createDocumentFragment ();for (let i = 0 ; i < 100 ; i++) { const li = document .createElement ('li' ); li.textContent = `第${i} 项` ; fragment2.appendChild (li); } document .querySelector ('ul' ).appendChild (fragment2);
12.5 节点关系
const el = document .querySelector ('.box' );el.parentNode el.parentElement el.childNodes el.children el.firstChild el.firstElementChild el.lastChild el.lastElementChild el.previousSibling el.previousElementSibling el.nextSibling el.nextElementSibling
13. 事件
13.1 事件监听
const btn = document .querySelector ('button' );btn.addEventListener ('click' , function (event ) { console .log ('点击了' , event.target ); }); btn.addEventListener ('click' , (event ) => { console .log (event.target ); }); function handleClick (event ) { console .log ('点击' ); } btn.addEventListener ('click' , handleClick); btn.removeEventListener ('click' , handleClick); btn.addEventListener ('click' , handleClick, { once : true });
13.2 事件对象
document .querySelector ('a' ).addEventListener ('click' , (event ) => { event.preventDefault () event.stopPropagation () event.stopImmediatePropagation () event.target event.currentTarget event.type event.timeStamp event.clientX event.clientY event.pageX event.pageY event.button event.key event.code event.ctrlKey event.shiftKey event.altKey });
13.3 事件冒泡与捕获
document .querySelector ('.outer' ).addEventListener ('click' , () => { console .log ('outer 冒泡' ); }, false ); document .querySelector ('.inner' ).addEventListener ('click' , () => { console .log ('inner 冒泡' ); }, false ); document .querySelector ('.outer' ).addEventListener ('click' , () => { console .log ('outer 捕获' ); }, true );
13.4 事件委托
const ul = document .querySelector ('ul' );ul.addEventListener ('click' , (event ) => { const li = event.target .closest ('li' ); if (!li) return ; console .log ('点击了' , li.textContent ); const action = event.target .dataset .action ; if (action === 'delete' ) { li.remove (); } else if (action === 'edit' ) { li.contentEditable = true ; li.focus (); } });
13.5 常用事件类型
element.addEventListener ('click' , handler); element.addEventListener ('dblclick' , handler); element.addEventListener ('mouseenter' , handler); element.addEventListener ('mouseleave' , handler); element.addEventListener ('mouseover' , handler); element.addEventListener ('mouseout' , handler); element.addEventListener ('mousemove' , handler); element.addEventListener ('contextmenu' , handler); document .addEventListener ('keydown' , handler); document .addEventListener ('keyup' , handler); input.addEventListener ('input' , handler); input.addEventListener ('change' , handler); input.addEventListener ('focus' , handler); input.addEventListener ('blur' , handler); form.addEventListener ('submit' , handler); form.addEventListener ('reset' , handler); window .addEventListener ('load' , handler); window .addEventListener ('DOMContentLoaded' , handler); window .addEventListener ('resize' , handler); window .addEventListener ('scroll' , handler); const event = new CustomEvent ('myEvent' , { detail : { name : '张三' }, bubbles : true , }); element.dispatchEvent (event); element.addEventListener ('myEvent' , e => console .log (e.detail ));
14. 浏览器 API
14.1 存储
localStorage .setItem ('key' , 'value' )localStorage .getItem ('key' ) localStorage .removeItem ('key' )localStorage .clear ()localStorage .setItem ('user' , JSON .stringify ({ name : '张三' }))const user = JSON .parse (localStorage .getItem ('user' ))sessionStorage .setItem ('key' , 'value' )sessionStorage .getItem ('key' )document .cookie = 'name=张三; max-age=3600; path=/'
14.2 定时器
const timer = setTimeout (() => { console .log ('1秒后执行' ); }, 1000 ); clearTimeout (timer); const interval = setInterval (() => { console .log ('每秒执行' ); }, 1000 ); clearInterval (interval); function animate ( ) { element.style .left = (parseInt (element.style .left ) + 1 ) + 'px' ; if (parseInt (element.style .left ) < 300 ) { requestAnimationFrame (animate); } } requestAnimationFrame (animate);
14.3 网络请求
fetch ('https://api.example.com/users' ) .then (response => { if (!response.ok ) { throw new Error (`HTTP错误: ${response.status} ` ); } return response.json (); }) .then (data => console .log (data)) .catch (error => console .error (error)); async function fetchData (url ) { try { const response = await fetch (url, { method : 'POST' , headers : { 'Content-Type' : 'application/json' , 'Authorization' : 'Bearer token123' }, body : JSON .stringify ({ name : '张三' }) }); if (!response.ok ) { throw new Error (`HTTP错误: ${response.status} ` ); } return await response.json (); } catch (error) { console .error ('请求失败' , error); throw error; } } async function request (url, options = {} ) { const defaultOptions = { headers : { 'Content-Type' : 'application/json' }, }; const response = await fetch (url, { ...defaultOptions, ...options }); if (!response.ok ) { const error = new Error ('请求失败' ); error.status = response.status ; throw error; } return response.json (); } const users = await request ('/api/users' );const newUser = await request ('/api/users' , { method : 'POST' , body : JSON .stringify ({ name : '张三' }) });
14.4 URL 与路由
const url = new URL ('https://example.com/search?q=hello&page=2#result' );url.protocol url.hostname url.pathname url.search url.hash const params = new URLSearchParams ('q=hello&page=2' );params.get ('q' ) params.set ('page' , '3' ) params.append ('sort' , 'asc' ) params.delete ('q' ) params.toString () window .history .pushState ({ page : 2 }, '' , '/page/2' ) window .history .replaceState ({ page : 2 }, '' , '/page/2' ) window .history .back () window .history .forward () window .history .go (-2 ) window .addEventListener ('popstate' , (event ) => { console .log ('历史状态' , event.state ); });
15. 模块化
15.1 ES Module(ESM)
export const PI = 3.14159 ;export function add (a, b ) { return a + b; }export class User { }export default class App { }const name = '张三' ;const age = 18 ;export { name, age };export { name as userName }; import App from './App.js' ; import { add, PI } from './utils.js' ; import { add as sum } from './utils.js' ; import * as utils from './utils.js' ; import App , { add, PI } from './module.js' ; const module = await import ('./heavy-module.js' );module .default ();module .someExport ();button.addEventListener ('click' , async () => { const { Chart } = await import ('./chart.js' ); new Chart (data); });
15.2 模块设计原则
export function formatDate (date ) { return new Intl .DateTimeFormat ('zh-CN' ).format (date); } export function formatPrice (price ) { return `¥${price.toFixed(2 )} ` ; } export { formatDate, formatPrice } from './format.js' ;export { request } from './request.js' ;export { storage } from './storage.js' ;import { formatDate, request } from './utils/index.js' ;
16. 常用内置对象
16.1 Math
Math .PI Math .abs (-5 ) Math .ceil (4.1 ) Math .floor (4.9 ) Math .round (4.5 ) Math .max (1 , 2 , 3 ) Math .min (1 , 2 , 3 ) Math .pow (2 , 10 ) Math .sqrt (16 ) Math .random () Math .floor (Math .random () * (max - min + 1 )) + minMath .max (...[1 , 2 , 3 , 4 , 5 ])
16.2 Date
const now = new Date ();const date = new Date ('2024-01-01' );const date2 = new Date (2024 , 0 , 1 ); const timestamp = Date .now (); now.getFullYear () now.getMonth () now.getDate () now.getDay () now.getHours () now.getMinutes () now.getSeconds () now.getTime () const formatter = new Intl .DateTimeFormat ('zh-CN' , { year : 'numeric' , month : '2-digit' , day : '2-digit' , hour : '2-digit' , minute : '2-digit' , }); formatter.format (now) const start = new Date ('2024-01-01' );const end = new Date ('2024-12-31' );const diffMs = end - start; const diffDays = Math .floor (diffMs / 86400000 );
16.3 JSON
const obj = { name : '张三' , age : 18 , hobbies : ['读书' , '编程' ] };JSON .stringify (obj)JSON .stringify (obj, null , 2 ) JSON .stringify (obj, ['name' , 'age' ]) JSON .stringify ({ fn : function ( ){}, undef : undefined , sym : Symbol () })JSON .parse ('{"name":"张三","age":18}' )function safeParseJSON (str, fallback = null ) { try { return JSON .parse (str); } catch { return fallback; } }
17. 代码规范与实践建议
17.1 命名规范
let userName = '张三' ;function getUserInfo ( ) {}class UserService {}function Person ( ) {}const MAX_RETRY_COUNT = 3 ;const API_BASE_URL = 'https://api.example.com' ;class MyClass { #privateField = 0 ; _legacyPrivate = 0 ; } let isLoading = false ;let hasPermission = true ;let canEdit = false ;
17.2 常见最佳实践
if (value === null ) {} function process (user ) { if (!user) return null ; if (!user.name ) return null ; return user.name .toUpperCase (); } const MAX_ITEMS = 10 ; if (items.length > MAX_ITEMS ) {} const { name, age } = user;const [first, ...rest] = arr;const city = user?.address ?.city ?? '未知' ;function validateEmail (email ) { }function sendEmail (email, content ) { }function add (a, b ) { return a + b; } let total = 0 ;function addToTotal (n ) { total += n; }
17.3 性能注意事项
for (let i = 0 ; i < 1000 ; i++) { document .querySelector ('.list' ).innerHTML += `<li>${i} </li>` ; } const fragment = document .createDocumentFragment ();for (let i = 0 ; i < 1000 ; i++) { const li = document .createElement ('li' ); li.textContent = i; fragment.appendChild (li); } document .querySelector ('.list' ).appendChild (fragment);function debounce (fn, delay ) { let timer = null ; return function (...args ) { clearTimeout (timer); timer = setTimeout (() => fn.apply (this , args), delay); }; } const handleSearch = debounce ((event ) => { console .log ('搜索:' , event.target .value ); }, 500 ); input.addEventListener ('input' , handleSearch); function throttle (fn, interval ) { let lastTime = 0 ; return function (...args ) { const now = Date .now (); if (now - lastTime >= interval) { lastTime = now; fn.apply (this , args); } }; } const handleScroll = throttle (() => { console .log ('滚动位置:' , window .scrollY ); }, 100 ); window .addEventListener ('scroll' , handleScroll);
总结
知识体系速查
JavaScript 知识体系 ├── 基础概念 → 引入方式、输出调试 ├── 变量与类型 → var/let/const、8种数据类型、类型转换 ├── 运算符 → 算术/比较/逻辑/可选链/展开 ├── 流程控制 → if/switch/for/while/try-catch ├── 函数 → 声明方式、参数、作用域、闭包、this、高阶函数 ├── 数组 → 增删查改、遍历方法、解构 ├── 对象 → 创建、属性操作、遍历、解构 ├── 字符串 → 常用方法、模板字符串、正则基础 ├── Map 与 Set → 用途、API、WeakMap/WeakSet ├── 面向对象 → 原型链、构造函数、Class、继承、Mixin ├── 异步编程 → 回调、Promise、async/await、事件循环 ├── DOM 操作 → 获取元素、内容属性、节点操作、节点关系 ├── 事件 → 监听、事件对象、冒泡捕获、事件委托 ├── 浏览器 API → 存储、定时器、fetch、URL/History ├── 模块化 → ESM 导入导出、动态导入、设计原则 ├── 内置对象 → Math、Date、JSON └── 最佳实践 → 命名规范、代码风格、防抖节流
常见问题速查
问题
解决方案
变量提升导致的 bug
使用 let/const 替代 var
this 指向错误
箭头函数、bind()、或提前保存 this
深浅拷贝混淆
简单场景用 structuredClone(),复杂场景用第三方库
回调地狱
改用 Promise 或 async/await
类型判断不准确
使用 Object.prototype.toString.call()
异步并行性能差
使用 Promise.all() 并行执行
频繁 DOM 操作卡顿
使用文档片段批量操作,或虚拟 DOM 框架
事件监听内存泄漏
及时 removeEventListener,或使用 once 选项
高频事件性能问题
防抖(debounce)或节流(throttle)
访问 null/undefined 属性报错
使用可选链运算符(?.)