JavaScript简介
JavaScript是一种高层次的、解释型的编程语言,主要用于前端开发。它是Web开发的三大核心技术之一(另外两个是HTML和CSS)。
基本概念
- 解释型语言:JavaScript是一种解释型语言,代码可以直接在浏览器中被解释和执行,无需预先编译。
- 高层次语言:JavaScript是一种高层次语言,提供了许多抽象机制,使开发者能够专注于解决业务逻辑,而不用处理底层细节。
- 动态类型:JavaScript是动态类型语言,变量的类型在运行时确定,可以改变。
- 基于原型的面向对象:JavaScript使用原型链来实现继承,而不是基于类的继承。
历史和发展
- 诞生:JavaScript由Brendan Eich在1995年为Netscape Navigator浏览器开发,最初称为Mocha,后更名为LiveScript,最终定名为JavaScript。
- 标准化:1997年,JavaScript被提交给欧洲计算机制造商协会(ECMA),并成为ECMA-262标准,也称为ECMAScript。
- 版本演进:JavaScript的版本不断更新和演进,主要版本包括ES3、ES5、ES6(ES2015)、以及每年的更新(如ES2016、ES2017等)。
主要用途
- 前端开发:
- JavaScript广泛应用于前端开发,用于实现网页的交互和动态效果。通过操作DOM(文档对象模型),可以动态地修改HTML和CSS内容。
- 常用的前端框架和库:React、js、Angular。
- 后端开发:
- 通过js,JavaScript也可以在服务器端运行,支持构建高性能的服务器端应用。
- 常用的后端框架:js、NestJS、Koa。
- 全栈开发:
- 使用JavaScript可以实现全栈开发,即前端和后端都使用同一种语言,简化了开发流程。
- 移动开发:
- 通过React Native、Ionic等框架,JavaScript可以用于开发跨平台的移动应用。
- 桌面应用开发:
- 通过Electron可以使用JavaScript创建跨平台的桌面应用。
JavaScript基本语法
变量声明
在JavaScript中,var、let和const都用于声明变量,但它们之间存在重要的区别,这决定了它们在不同场景下的使用。
var(已废弃,但仍然可用)
var是JavaScript的老式变量声明方式,它具有以下几个特性:
- 作用域:var声明的变量作用域为函数作用域,而不是块级作用域。这意味着在同一个函数内部,所有 var 声明的变量都是共享的。
- 提升(Hoisting):var声明的变量会被提升到当前作用域的顶部,无论它们在代码中的实际位置在哪里。
- 可重复声明:同一个作用域内,可以多次声明同一个var 变量,后面的声明会覆盖前面的值。
使用场景:由于var的这些特性可能导致一些难以预料的问题,因此在ES6(ECMAScript2015)引入let和const之后,推荐使用它们代替var。
let
let是ES6引入的新变量声明方式,它解决了var的一些问题:
- 作用域:let声明的变量具有块级作用域,这意味着它们只在声明它们的代码块(例如,循环、if语句、函数等)内有效。
- 不可提升:let不会像 var 那样被提升到作用域顶部,这被称为暂时性死区(TemporalDeadZone,TDZ)。
- 不可重复声明:在同一作用域内,不能重复声明一个let 变量。
使用场景:通常用于局部变量,特别是需要块级作用域的场景,如循环、条件语句和避免变量提升导致的问题。
const
const也属于ES6引入的新的变量声明方式,用于声明常量:
- 作用域:与let 类似,const 也有块级作用域。
- 不可重新赋值:一旦声明并赋值,const变量的值就不能更改。
- 注意:尽管不能改变const 变量的值,但如果它是一个对象或数组,对象或数组的属性或元素是可以修改的。
使用场景:用于声明不会改变的常量,如数学常数、配置对象的引用(但不修改对象本身)等。
示例:
function example(){ var x=10; if(true){ var x=20;//同名变量,覆盖之前的值 console.log(x);//输出20 } console.log(x);//输出20 } example(); //let示例 function exampleLet(){ let y=10; if(true){ let y=20;//不同作用域的变量 console.log(y);//输出20 } console.log(y);//输出10 } exampleLet(); //const示例 const pi=3.14159; console.log(pi);//输出3.14159 pi=3;//报错:Assignment to constant variable.
数据类型
JavaScript中的基本数据类型(也称为原始数据类型)有七种:字符串(String)、数字(Number)、布尔值(Boolean)、空值(Null)、未定义(Undefined)、符号(Symbol)和大整数(BigInt)。这些数据类型都是不可变的,即它们的值无法被改变。每种数据类型有其独特的特点和使用场景。以下是对每种数据类型的详细介绍:
字符串(String)
字符串用于表示文本数据。可以使用单引号(‘)、双引号(“)或反引号(`)来定义字符串。
let str1='Hello,world!'; let str2="JavaScript is awesome!"; let str3=`Template literals allow embedding expressions:${str1}`;
特点:
- 不可变性:字符串一旦创建,其内容不能被改变。任何修改都会生成一个新的字符串。
- 多行文本:使用反引号(模板字符串),可以定义多行文本。
let multiLineString=`This is a multi-line string.`;
常用方法:
- length:获取字符串长度
- toUpperCase()、toLowerCase():转换大小写
- substring(start,end):获取子字符串
- indexOf(substring)、includes(substring):查找子字符串
- split(delimiter):分割字符串
let example = "JavaScript"; console.log(example.length); // 10 console.log(example.toUpperCase()); // "JAVASCRIPT" console.log(example.substring(0, 4)); // "Java" console.log(example.indexOf("Script")); // 4 console.log(example.includes("Script")); // true console.log(example.split("a")); // ["J","v","Script"]
数字(Number)
JavaScript使用Number类型表示所有的数字,包括整数和浮点数。
let intNum = 42; let floatNum = 3.14; let exponentialNum = 1.23e5; // 123000
特点:
- IEEE754标准:JavaScript的数字类型基于IEEE754标准的双精度64位浮点数。
- 特殊值:NaN(非数字)、Infinity和 -Infinity。
常用方法:
- parseInt(string)、parseFloat(string):将字符串转换为数字
- toFixed(digits):格式化数字为指定小数位数
- Math对象提供了许多数学函数,如 round()、Math.ceil()、Math.floor()、Math.random()、Math.max()、Math.min()。
let num = 123.456; console.log(parseInt("123")); // 123 console.log(parseFloat("123.45")); // 123.45 console.log(num.toFixed(2)); // "123.46" console.log(Math.round(num)); // 123 console.log(Math.ceil(num)); // 124 console.log(Math.floor(num)); // 123 console.log(Math.random()); // 0到1之间的随机数 console.log(Math.max(1, 2, 3)); // 3 console.log(Math.min(1, 2, 3)); // 1
布尔值(Boolean)
布尔值只有两个可能的值:true和false。
let isTrue = true;
let isFalse = false;
特点:
- 逻辑运算:常用于逻辑判断和控制流语句(如if、while 等)。
- 常用运算符
- 逻辑与&&
- 逻辑或||
- 逻辑非!
let a = true; let b = false; console.log(a && b); // false console.log(a || b); // true console.log(!a); // false
空值(Null)
null表示一个空值或一个无效的对象引用。
let emptyValue = null;
特点:
- 类型:null的类型是 object,这是一个被认为是设计失误的历史遗留问题。
未定义(Undefined)
undefined表示一个变量尚未被赋值。
let notAssigned; console.log(notAssigned); // undefined
特点:
- 自动初始化:声明但未赋值的变量自动初始化为undefined。
符号(Symbol)
Symbol是一种原始数据类型,用于创建唯一的标识符。
let symbol1 = Symbol(); let symbol2 = Symbol('description');
特点:
- 唯一性:每个Symbol 值都是唯一的,即使描述相同。
- 不可枚举:默认情况下,Symbol属性不会被包含在..in 迭代或 Object.keys() 中。
let sym1 = Symbol('foo'); let sym2 = Symbol('foo'); console.log(sym1 === sym2); // false
大整数(BigInt)
BigInt是一种可以表示任意精度整数的原始数据类型。
let bigIntNum = BigInt(123456789012345678901234567890); let anotherBigInt = 123456789012345678901234567890n;
特点:
- 表示大整数:用于表示超过Number 类型安全整数范围(2^53-1)的整数。
- 操作:可以进行正常的算术运算,但不能与Number 类型混合运算。
let big1 = 12345678901234567890n; let big2 = 98765432109876543210n; console.log(big1 + big2); // 111111111011111111100n console.log(big2 - big1); // 86419753208641975320n
数据类型检测
可以使用typeof操作符来检测变量的数据类型。
console.log(typeof 'Hello'); // "string" console.log(typeof 42); // "number" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof Symbol()); // "symbol" console.log(typeof BigInt(123)); // "bigint" console.log(typeof null); // "object"(这是一个被认为是设计失误的历史遗留问题) console.log(typeof {}); // "object" console.log(typeof function(){}); // "function"
操作符
JavaScript提供了多种操作符,用于执行各种运算和操作。常见的操作符包括算术操作符、赋值操作符、比较操作符、逻辑操作符和位操作符。下面是对这些操作符的详细介绍:
算术操作符(Arithmetic Operators)
算术操作符用于执行数学运算,主要包括加法、减法、乘法、除法等。
常见的算术操作符:
- +:加法
- -:减法
- *:乘法
- /:除法
- %:取模(余数)
- ++:自增
- –:自减
示例:
let a = 10; let b = 3; console.log(a + b); //13 console.log(a - b); //7 console.log(a * b); //30 console.log(a / b); //3.3333333333333335 console.log(a % b); //1 a++; //a=a+1,a变为11 b--; //b=b-1,b变为2 console.log(a); //11 console.log(b); //2
赋值操作符(Assignment Operators)
赋值操作符用于为变量分配值或进行某种运算后再赋值。
常见的赋值操作符:
- =:赋值
- +=:加法赋值
- -=:减法赋值
- *=:乘法赋值
- /=:除法赋值
- %=:取模赋值
示例:
let x = 5; x += 3; //相当于x=x+3,x变为8 x -= 2; //相当于x=x-2,x变为6 x *= 4; //相当于x=x*4,x变为24 x /= 2; //相当于x=x/2,x变为12 x %= 5; //相当于x=x%5,x变为2 console.log(x); //输出2
比较操作符(Comparison Operators)
比较操作符用于比较两个值,并返回布尔值(true或false)。
常见的比较操作符:
- ==:相等
- ===:全等(严格相等)
- !=:不相等
- !==:不全等(严格不相等)
- >:大于
- <:小于
- >=:大于等于
- <=:小于等于
示例:
let m = 10; let n = '10'; console.log(m == n); //true,因为==允许类型转换 console.log(m === n); //false,因为===不允许类型转换 console.log(m != n); //false,因为==允许类型转换 console.log(m !== n); //true,因为===不允许类型转换 console.log(m > 5); //true console.log(m < 15); //true console.log(m >= 10); //true console.log(m <= 9); //false
逻辑操作符(Logical Operators)
逻辑操作符用于布尔值的逻辑运算,主要包括与、或、非。
常见的逻辑操作符:
- &&:逻辑与(AND)
- ||:逻辑或(OR)
- !:逻辑非(NOT)
示例:
let p = true; let q = false; console.log(p && q); //false,只有两个操作数都为true时才返回true console.log(p || q); //true,只有两个操作数都为false时才返回false console.log(!p); //false,将true取反为false console.log(!q); //true,将false取反为true
位操作符(Bitwise Operators)
位操作符用于按位运算,即对二进制位进行操作。
常见的位操作符:
- &:按位与
- |:按位或
- ^:按位异或
- ~:按位非
- <<:左移
- >>:右移
- >>>:无符号右移
示例:
let u = 5; //二进制:0101 let v = 3; //二进制:0011 console.log(u & v); //1,按位与:0001 console.log(u | v); //7,按位或:0111 console.log(u ^ v); //6,按位异或:0110 console.log(~u); //-6,按位非:取反码再加1,结果是-(u+1) console.log(u << 1); //10,左移一位:1010 console.log(u >> 1); //2,右移一位:0010 console.log(u >>> 1); //2,无符号右移一位:0010
控制结构
JavaScript的流程控制语句用于控制代码的执行顺序,根据不同的条件执行不同的代码块。常见的流程控制语句包括条件语句、循环语句和跳转语句。下面是对这些语句的详细介绍:
条件语句(Conditional Statements)
条件语句用于根据不同的条件执行不同的代码块。
if 语句
if语句用于在条件为true时执行代码块。
let condition = true; if(condition){ console.log("Condition is true"); }
if…else 语句
if...else语句用于在条件为true时执行一个代码块,为false时执行另一个代码块。 let condition = false; if(condition){ console.log("Condition is true"); }else{ console.log("Condition is false"); }
if…else if…else 语句
if…else if…else语句用于测试多个条件。
let value = 10; if(value > 10){ console.log("Value is greater than 10"); }else if(value === 10){ console.log("Value is equal to 10"); }else{ console.log("Value is less than 10"); }
三元运算符(Ternary Operator)
三元运算符是一个简洁的条件语句,condition ? expr1 : expr2。
let age = 18; let canVote = age >= 18 ? "Yes" : "No"; console.log(canVote); //输出"Yes"
switch 语句
switch语句用于针对多个条件执行不同代码块。
let fruit = "apple"; switch(fruit){ case "apple": console.log("An apple a day keeps the doctor away."); break; case "banana": console.log("Bananas are high in potassium."); break; case "orange": console.log("Oranges are a great source of vitamin C."); break; default: console.log("Unknown fruit."); }
循环语句(Loop Statements)
循环语句用于重复执行代码块,直到特定条件为false。
for 循环
for循环用于执行定次数的循环。
for(let i=0; i<5; i++){ console.log(i); //输出0,1,2,3,4 }
while 循环
while循环在条件为true时重复执行代码块。
let i=0; while(i<5){ console.log(i); //输出0,1,2,3,4 i++; }
do…while 循环
do…while循环先执行代码块,然后在条件为true时继续执行。
let i=0; do{ console.log(i); //输出0,1,2,3,4 i++; }while(i<5);
for…in 循环
for…in循环用于遍历对象的可枚举属性。
let person={name:"John",age:30}; for(let key in person){ console.log(key+":"+person[key]); } //输出 //name:John //age:30
for…of 循环
for…of循环用于遍历可迭代对象(如数组、字符串、集合等)。
let array=[10,20,30]; for(let value of array){ console.log(value); } //输出 //10 //20 //30
跳转语句(Jump Statements)
跳转语句用于控制循环和条件语句的执行。
break 语句
break语句用于立即退出循环或switch语句。
for(let i=0; i<10; i++){ if(i===5){ break; //在i等于5时退出循环 } console.log(i); } //输出0,1,2,3,4
continue 语句
continue语句用于跳过当前迭代并继续下一次迭代。
for(let i=0; i<5; i++){ if(i===2){ continue; //跳过i等于2的迭代 } console.log(i); } //输出0,1,3,4
return 语句
return语句用于退出函数并返回一个值。
function sum(a,b){ return a+b; //返回a和b的和 } console.log(sum(5,3)); //输出8
异常处理(Exception Handling)
异常处理用于捕获和处理运行时错误,确保程序不会因为错误而中断。
try…catch 语句
try…catch语句用于捕获和处理异常。
try{ let result=someFunction(); //尝试执行函数 console.log(result); }catch(error){ console.error("An error occurred:",error.message); //捕获并处理错误 }
finally 语句
finally 语句用于在 try 和 catch 块之后执行代码,不论是否发生异常。
try{ let result=someFunction(); //尝试执行函数 console.log(result); }catch(error){ console.error("An error occurred:",error.message); //捕获并处理错误 }finally{ console.log("This will always be executed."); //始终执行 }
throw 语句
throw语句用于手动抛出异常。
function checkAge(age){ if(age<18){ throw new Error("Age must be 18 or older."); //手动抛出异常 } return "Access granted."; } try{ console.log(checkAge(15)); //尝试检查年龄 }catch(error){ console.error("An error occurred:",error.message); //捕获并处理错误 }
函数基础
在JavaScript中,函数是一等公民(first-class citizen),它们可以作为变量赋值、传递给其他函数、从函数中返回等。函数是逻辑单元,它们封装了一段可复用的代码。以下是对JavaScript函数的详细介绍:
函数定义
函数声明(Function Declaration)
函数声明使用function关键字定义一个函数。
function add(a,b){ return a+b; } console.log(add(2,3)); //输出5
函数表达式(Function Expression)
函数表达式将一个函数赋值给一个变量,可以是匿名函数或命名函数。
const multiply=function(a,b){ return a*b; }; console.log(multiply(2,3)); //输出6
箭头函数(Arrow Function)
箭头函数是ES6引入的简洁的函数定义方式,使用箭头=>语法。
const subtract=(a,b)=>{ return a-b; }; console.log(subtract(5,2)); //输出3
如果箭头函数只有一个表达式,可以省略大括号{}和return关键字。
const divide=(a,b)=>a/b; console.log(divide(6,3)); //输出2
立即执行函数(Immediately Invoked Function Expression, IIFE)
IIFE是一种立即执行的函数表达式,常用于创建独立的作用域。
(function(){ console.log("This is an IIFE"); })(); //使用箭头函数的IIFE (() => { console.log("This is another IIFE"); })();
函数参数
默认参数(Default Parameters)
可以为函数参数设置默认值,如果调用时未提供参数,则使用默认值。
function greet(name="Guest"){ return `Hello, ${name}!`; } console.log(greet());//输出"Hello, Guest!" console.log(greet("Alice"));//输出"Hello, Alice!"
剩余参数(Rest Parameters)
使用…语法,可以将多个参数收集为一个数组。
function sum(...numbers){ return numbers.reduce((total, num) => total + num, 0); } console.log(sum(1, 2, 3, 4));//输出10
函数返回值
函数可以返回任意类型的值,包括基本类型和对象类型。如果函数没有return语句,则默认返回undefined。
function noReturn(){} console.log(noReturn());//输出undefined function returnObject(){ return { name: "John", age: 30 }; } console.log(returnObject());//输出{name: "John", age: 30}
函数方法
JavaScript提供了一些内置的方法来操作函数。
call
call方法调用一个函数,并显式指定this值和参数。
function greet(greeting){ console.log(greeting + ", " + this.name); } const person = {name: "Alice"}; greet.call(person, "Hello");//输出"Hello, Alice"
apply
apply方法与call类似,但它接受一个参数数组。
function greet(greeting, punctuation){ console.log(greeting + ", " + this.name + punctuation); } const person = {name: "Alice"}; greet.apply(person, ["Hello", "!"]);//输出"Hello, Alice!"
bind
bind方法创建一个新的函数,并将this绑定到指定的值。
function greet(greeting){ console.log(greeting + ", " + this.name); } const person = {name: "Alice"}; const boundGreet = greet.bind(person); boundGreet("Hello");//输出"Hello, Alice"
高阶函数
高阶函数(Higher-Order Function)是指接受函数作为参数,或者返回一个函数作为结果的函数。在JavaScript中,高阶函数是函数式编程的一个重要特性,它可以使代码更加灵活和可重用。
接受函数作为参数
高阶函数可以接受一个或多个函数作为参数,这使得可以将函数作为操作对象传递给高阶函数,从而实现更灵活的行为。
示例:Array.prototype.map
map函数是一个常见的高阶函数,它接受一个回调函数作为参数,并对数组中的每个元素应用该回调函数,返回一个新的数组。
const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(function(number){ return number * 2; }); console.log(doubled);//输出:[2, 4, 6, 8, 10]
在这个例子中,map函数接受一个回调函数,并对数组numbers中的每个元素应用该回调函数,生成一个新的数组doubled。
示例:Array.prototype.filter
filter函数也是一个高阶函数,它接受一个回调函数作为参数,并返回一个新的数组,包含所有通过回调函数测试的元素。
const numbers = [1, 2, 3, 4, 5]; const evenNumbers = numbers.filter(function(number){ return number % 2 === 0; }); console.log(evenNumbers);//输出:[2, 4]
在这个例子中,filter函数接受一个回调函数,并返回一个新的数组evenNumbers,包含所有满足条件的偶数。
返回一个函数作为结果
高阶函数也可以返回一个新的函数,从而实现函数的动态生成和组合。
示例:创建一个倍数函数
function createMultiplier(multiplier){ return function(number){ return number * multiplier; }; } const double = createMultiplier(2); const triple = createMultiplier(3); console.log(double(5));//输出:10 console.log(triple(5));//输出:15
在这个例子中,createMultiplier是一个高阶函数,它返回一个新的函数,该函数将输入的数字乘以指定的倍数。
示例:函数组合
函数组合是函数式编程中的一个重要概念,可以通过高阶函数实现。
function compose(f, g){ return function(x){ return f(g(x)); }; } const add1 = x => x + 1; const multiply2 = x => x * 2; const add1ThenMultiply2 = compose(multiply2, add1); console.log(add1ThenMultiply2(5));//输出:12
在这个例子中,compose函数接受两个函数f和g,返回一个新的函数,该函数首先应用g,然后应用f。
实用高阶函数示例
示例:柯里化(Currying)柯里化是将接受多个参数的函数转换为一系列接受单个参数的函数的技术。
function curry(f) { return function(a) { return function(b) { return f(a, b); }; }; } function add(a, b) { return a + b; } const curriedAdd = curry(add); console.log(curriedAdd(1)(2)); // 输出: 3
在这个例子中,curry函数将add函数柯里化,生成一个接受单个参数的函数链。
示例:偏应用(Partial Application)
偏应用是将函数的一些参数预先应用,生成一个新的函数。
function partial(f, ...fixedArgs) { return function(...remainingArgs) { return f(...fixedArgs, ...remainingArgs); }; } function add(a, b) { return a + b; } const add5 = partial(add, 5); console.log(add5(3)); // 输出: 8
在这个例子中,partial函数预先应用了add函数的第一个参数,生成一个新的函数add5,它只需要一个参数。
高阶函数在回调中的使用
高阶函数在异步编程和事件处理中也非常常见。例如,在处理AJAX请求时,可以将回调函数传递给异步函数。
function fetchData(url, callback) { fetch(url) .then(response => response.json()) .then(data => callback(null, data)) .catch(error => callback(error, null)); } fetchData('https://api.example.com/data', function(error, data) { if (error) { console.error('Error fetching data:', error); } else { console.log('Data fetched:', data); } });
在这个例子中,fetchData是一个高阶函数,它接受一个回调函数callback,在数据获取完成后调用该回调函数。
匿名函数
匿名函数(Anonymous Functions)是没有名字的函数。在JavaScript中,匿名函数可以用于多种场景,如回调、立即执行函数以及高阶函数等。匿名函数通常用于那些不需要在多个地方重复使用的函数。
创建匿名函数
作为函数表达式
匿名函数可以作为函数表达式的一部分,赋值给变量或常量。
const add = function(a, b) { return a + b; }; console.log(add(2, 3)); // 输出5
立即执行函数表达式(Immediately Invoked Function Expression, IIFE) IIFE是一种立即执行的匿名函数,常用于创建独立的作用域,避免变量污染全局作用域。
(function() { console.log("This is an IIFE"); })(); // 使用箭头函数的IIFE (() => { console.log("This is another IIFE"); })();
匿名函数的使用场景
回调函数(Callback Functions)
匿名函数经常用于回调函数中,回调函数是作为参数传递给另一个函数并在特定事件或条件下调用的函数。
setTimeout(function() { console.log("This is a callback function"); }, 1000); // 使用箭头函数 setTimeout(() => { console.log("This is another callback function"); }, 1000);
高阶函数(Higher-order Functions)
高阶函数是接受一个或多个函数作为参数,或返回一个函数作为结果的函数。匿名函数经常用于这些场景。
const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(function(num) { return num * 2; }); console.log(doubled); // 输出[2, 4, 6, 8, 10] // 使用箭头函数 const squared = numbers.map(num => num * num); console.log(squared); // 输出[1, 4, 9, 16, 25]
事件处理器(Event Handlers)
在浏览器环境中,匿名函数经常用于事件处理器。
document.getElementById("myButton").addEventListener("click", function() { console.log("Button clicked"); }); // 使用箭头函数 document.getElementById("myButton").addEventListener("click", () => { console.log("Button clicked"); });
匿名函数的优缺点
优点
- 简洁: 匿名函数可以使代码更简洁,尤其是在函数只在一个地方使用时。
- 避免命名冲突: 由于匿名函数没有名字,不会污染全局命名空间,减少命名冲突的风险。
- 创建闭包: 匿名函数常用于创建闭包,可以保存函数作用域外的变量状态。
缺点
- 调试困难: 由于匿名函数没有名字,在堆栈跟踪和调试时可能不容易识别。
- 可读性差: 如果过度使用匿名函数,尤其是嵌套的匿名函数,会降低代码的可读性。
命名函数表达式(Named Function Expressions)
虽然匿名函数没有名字,但你也可以在函数表达式中为它们命名,这样在调试时更容易识别。
const factorial = function fact(n) { if (n <= 1) return 1; return n * fact(n - 1); }; console.log(factorial(5)); // 输出120
箭头函数
箭头函数(Arrow Functions)是ES6(ECMAScript 2015)引入的一种新的函数定义方式,它们提供了一种更加简洁的语法,并且在处理this绑定方面有一些独特的特性。箭头函数在很多场景中都非常有用,尤其是在函数作为参数传递或者简化回调函数中。
基本语法
箭头函数使用箭头 => 语法定义,基本形式如下:
const functionName = (parameters) => { // 函数体 };
const add = (a, b) => { return a + b; }; console.log(add(2, 3)); // 输出 5
简化语法
单个参数
如果箭头函数只有一个参数,可以省略圆括号()。
const square = x => { return x * x; }; console.log(square(4)); // 输出 16
无参数
如果箭头函数没有参数,需要使用空括号()。
const sayHello = () => { console.log("Hello"); }; sayHello(); // 输出 "Helconst multiply = (a, b) => a * b; console.log(multiply(2, 3)); // 输出 6
this 绑定
箭头函数与普通函数最显著的区别之一是它们如何处理this关键字。箭头函数不会创建自己的this,它会捕获其所在上下文的this值,作为自己的this值。这在处理回调函数时特别有用。
在普通函数中
function Person() { this.age = 0; setInterval(function growUp() { this.age++; console.log(this.age); // `this`指向全局对象(或`undefined`在严格模式下) }, 1000); } const p = new Person();
在箭头函数中
function Person() { this.age = 0; setInterval(() => { this.age++; console.log(this.age); // `this`指向`Person`实例 }, 1000); } const p = new Person();
使用场景
高阶函数和回调
const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(num => num * 2); console.log(doubled); // 输出 [2, 4, 6, 8, 10] setTimeout(() => { console.log("This is a callback function"); }, 1000);
事件处理器
document.getElementById("myButton").addEventListener("click", () => { console.log("Button clicked"); });
注意事项 没有 arguments 对象 箭头函数没有 arguments 对象。如果需要访问 arguments,可以使用剩余参数(rest parameters)语法。
const sum = (...args) => { return args.reduce((total, num) => total + num, 0); }; console.log(sum(1, 2, 3)); // 输出 6
不能用作构造函数
箭头函数不能使用 new 操作符来实例化对象,因为它们没有[[Construct]]方法。
const Person = (name) => { this.name = name; }; // const john = new Person("John"); // 会抛出错误
没有 prototype 属性
由于箭头函数不能作为构造函数,它们也没有 prototype 属性。
const foo = () => {}; console.log(foo.prototype); // 输出 undefined
无法使用 yield 关键字
箭头函数不能用作生成器函数,因为它们没有自己的执行上下文。
const generator = () => { // yield 1; // 会抛出错误 };
回调函数
回调函数(Callback Functions)是 JavaScript 中的一种重要编程模式,它们使得异步编程变得更加灵活和强大。回调函数就是作为参数传递给另一个函数并在某个时刻被调用的函数。通过回调函数,您可以在一个函数完成某项任务后执行另一个函数,而不需要阻塞代码执行。
基本概念
回调函数就是将一个函数作为参数传递给另一个函数,并在特定条件或事件发生时调用这个传递的函数。
示例:
function greet(name, callback) { console.log("Hello, " + name); callback(); } function sayGoodbye() { console.log("Goodbye!"); } greet("Alice", sayGoodbye); // 输出: // Hello, Alice // Goodbye!
同步回调和异步回调
同步回调
同步回调是在主函数完成之前立即执行的回调。
function processArray(arr, callback) { for (let i = 0; i< arr.length; i++) { callback(arr[i]); } } processArray([1, 2, 3, 4], function(element) { console.log(element * 2); }); // 输出: // 2 // 4 // 6 // 8
异步回调
异步回调是在主函数完成之后执行的回调,常用于处理异步操作,如网络请求、文件读取等。
console.log("Start"); setTimeout(function() { console.log("This is executed after 2 seconds"); }, 2000); console.log("End"); // 输出: // Start // End // This is executed after 2 seconds
回调地狱(Callback Hell)
当回调函数存在嵌套调用时,代码会变得难以阅读和维护,这种现象被称为回调地狱(Callback Hell)。
setTimeout(function(){ console.log("Step 1"); setTimeout(function(){ console.log("Step 2"); setTimeout(function(){ console.log("Step 3"); //继续嵌套回调... }, 1000); }, 1000); }, 1000); //输出: //Step 1 (after 1 second) //Step 2 (after another 1 second) //Step 3 (after another 1 second)
解决回调地狱的方法
使用命名函数
将嵌套的匿名回调函数替换为命名函数,可以提高代码的可读性。
function step1(){ console.log("Step 1"); setTimeout(step2, 1000); } function step2(){ console.log("Step 2"); setTimeout(step3, 1000); } function step3(){ console.log("Step 3"); } setTimeout(step1, 1000);
使用 Promise
Promise 是一种用于处理异步操作的对象,它可以避免回调地狱。
function wait(ms){ return new Promise(resolve => { setTimeout(resolve, ms); }); } wait(1000) .then(() => { console.log("Step 1"); return wait(1000); }) .then(() => { console.log("Step 2"); return wait(1000); }) .then(() => { console.log("Step 3"); }); //输出: //Step 1 (after 1 second) //Step 2 (after another 1 second) //Step 3 (after another 1 second)
使用 async/await
async/await 是基于 Promise 的语法糖,使异步代码看起来像同步代码。
function wait(ms){ return new Promise(resolve => { setTimeout(resolve, ms); }); } async function executeSteps(){ await wait(1000); console.log("Step 1"); await wait(1000); console.log("Step 2"); await wait(1000); console.log("Step 3"); } executeSteps(); //输出: //Step 1 (after 1 second) //Step 2 (after another 1 second) //Step 3 (after another 1 second)
回调函数的常见使用场景
事件处理
回调函数在事件处理机制中非常常见。
document.getElementById("myButton").addEventListener("click", function(){ console.log("Button clicked"); });
定时器
setTimeout 和 setInterval 等函数也需要回调函数。 setTimeout(function(){ console.log("Executed after 1 second"); }, 1000); setInterval(function(){ console.log("Executed every 2 seconds"); }, 2000);
异步操作
回调函数在处理异步操作时非常有用,如网络请求、文件读取等。
fetch("https://api.example.com/data") .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.error("Error:", error); });
闭包
闭包(Closure)是 JavaScript 中的一个重要概念,它是指那些能够访问自由变量的函数。自由变量是指在函数中使用的,但既不是该函数参数也不是该函数的局部变量的变量。闭包可以记住并访问它的词法作用域,即使当函数在其词法作用域之外执行时。
闭包的定义和基本示例
定义
闭包是指一个函数以及其词法环境(Lexical Environment)的组合。这个环境包含了这个函数能够访问的所有局部变量。
基本示例
function outerFunction(){ let outerVariable = 'I am outside!'; function innerFunction(){ console.log(outerVariable); //闭包:访问外部函数的变量 } return innerFunction; } const myInnerFunction = outerFunction(); myInnerFunction(); //输出 "I am outside!"
在这个例子中,innerFunction 是一个闭包,因为它可以访问 outerFunction 的局部变量 outerVariable,即使在 outerFunction 已经执行完毕之后。
闭包的应用场景
数据封装和私有变量
闭包可以用于创建私有变量,从而实现数据封装。
function createCounter(){ let count = 0; return { increment: function(){ count++; return count; }, decrement: function(){ count--; return count; }, getCount: function(){ return count; } }; } const counter = createCounter(); console.log(counter.increment()); //输出 1 console.log(counter.increment()); //输出 2 console.log(counter.decrement()); //输出 1 console.log(counter.getCount()); //输出 1
在这个例子中,count 变量在 createCounter 函数外部是不可访问的,但可以通过返回的对象中的方法访问,这些方法都形成了闭包。
延迟执行
闭包可以保存当前的变量状态,在未来某个时刻执行。
function delayLog(message, delay) { setTimeout(function() { console.log(message); // 每个回调都是一个闭包,记住了它创建时的`message`变量 }, delay); } delayLog('Hello after 1 second', 1000); delayLog('Hello after 2 seconds', 2000);
模拟块级作用域
在ES6之前,JavaScript没有块级作用域,闭包可以用来模拟块级作用域。
for(var i=0; i<3; i++){ (function(j){ setTimeout(function(){ console.log(j); // 输出0,1,2 }, 1000); })(i); }
在这个例子中,每次循环都会创建一个新的闭包,使得setTimeout回调函数可以记住当前的i值。
闭包的内存管理
由于闭包会持有对其词法环境的引用,这可能会导致一些内存问题,如内存泄漏。为了避免这种情况,可以显式地将不再需要的引用设为null。
function outerFunction(){ let bigData = new Array(1000); // 大数组 function innerFunction(){ console.log(bigData.length); } bigData = null; // 手动清理大数组 return innerFunction; } const myInnerFunction = outerFunction(); myInnerFunction(); // 输出1000
闭包的优缺点
优点
- 数据封装: 闭包可以创建私有变量,保护数据的隐私性。
- 函数式编程: 闭包是实现高阶函数、柯里化等函数式编程范式的重要工具。
- 延迟执行: 闭包可以保存当前状态并在未来某个时刻执行,非常适合异步编程。
缺点
- 内存消耗: 闭包持有对其词法环境的引用,可能会导致内存泄漏,尤其是在处理大数据时。
- 调试困难: 闭包的调试可能比较困难,因为它们的执行上下文可能不容易追踪。
对象(Object)
定义和创建对象
对象是JavaScript中的一种用于存储键值对(key-value pairs)的数据结构。它们可以通过对象字面量、构造函数或Object.create()方法来创建。
对象字面量
const person = { name: "Alice", age: 25, greet: function(){ console.log("Hello, my name is " + this.name); } }; console.log(person.name); // 输出"Alice" person.greet(); // 输出"Hello, my name is Alice"
构造函数
function Person(name, age){ this.name = name; this.age = age; this.greet = function(){ console.log("Hello, my name is " + this.name); }; } const person = new Person("Alice", 25); console.log(person.name); // 输出"Alice" person.greet(); // 输出"Hello, my name is Alice"
Object.create()
const personPrototype = { greet: function(){ console.log("Hello, my name is " + this.name); } }; const person = Object.create(personPrototype); person.name = "Alice"; person.age = 25; console.log(person.name); // 输出"Alice" person.greet(); // 输出"Hello, my name is Alice"
对象的属性和方法
访问和修改属性
const person = { name: "Alice", age: 25 }; console.log(person.name); // 输出"Alice" person.age = 30; console.log(person.age); // 输出"30" // 使用方括号语法 console.log(person["name"]); // 输出"Alice" person["age"] = 35; console.log(person["age"]); // 输出"35"
删除属性
delete person.age; console.log(person.age); // 输出"undefined"
遍历对象属性
const person = { name: "Alice", age: 25 }; for(let key in person){ if(person.hasOwnProperty(key)){ console.log(key + ": " + person[key]); } } // 使用Object.keys()返回一个属性数组 Object.keys(person).forEach(key => { console.log(key + ": " + person[key]); });
数组(Array)
定义和创建数组
数组是JavaScript中用于存储有序数据集合的数据结构。数组可以通过数组字面量或Array构造函数来创建。
数组字面量
const fruits = ["apple", "banana", "cherry"]; console.log(fruits[0]); // 输出"apple"
构造函数
const fruits = new Array("apple", "banana", "cherry"); console.log(fruits[1]); // 输出"banana"
数组的方法
添加和删除元素
- push():在数组末尾添加一个或多个元素
- pop():删除数组末尾的元素
- unshift():在数组开头添加一个或多个元素
- shift():删除数组开头的元素
const fruits = ["apple", "banana"]; fruits.push("cherry"); console.log(fruits); //输出["apple", "banana", "cherry"] fruits.pop(); console.log(fruits); //输出["apple", "banana"] fruits.unshift("grape"); console.log(fruits); //输出["grape", "apple", "banana"] fruits.shift(); console.log(fruits); //输出["apple", "banana"]
迭代数组
- forEach()
- map()
- filter()
- reduce()
const fruits = ["apple", "banana", "cherry"]; fruits.forEach(fruit => { console.log(fruit); }); //输出: //apple //banana //cherry const upperFruits = fruits.map(fruit => fruit.toUpperCase()); console.log(upperFruits); //输出["APPLE", "BANANA", "CHERRY"] const filteredFruits = fruits.filter(fruit => fruit.startsWith("b")); console.log(filteredFruits); //输出["banana"] const totalLength = fruits.reduce((total, fruit) => total + fruit.length, 0); console.log(totalLength); //输出17
查找和检查元素
- indexOf()和 lastIndexOf()
- includes()
- find()和 findIndex()
const fruits = ["apple", "banana", "cherry", "banana"]; console.log(fruits.indexOf("banana")); //输出1 console.log(fruits.lastIndexOf("banana")); //输出3 console.log(fruits.includes("cherry")); //输出true const foundFruit = fruits.find(fruit => fruit.startsWith("b")); console.log(foundFruit); //输出"banana" const foundIndex = fruits.findIndex(fruit => fruit.startsWith("b")); console.log(foundIndex); //输出1
数组的解构赋值
解构赋值是从数组或对象中提取值并将其赋值给变量的语法。
const fruits = ["apple", "banana", "cherry"]; const [first, second, third] = fruits; console.log(first); //输出"apple" console.log(second); //输出"banana" console.log(third); //输出"cherry"
多维数组
数组可以包含数组,这样的数组称为多维数组。
const matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; console.log(matrix[0][1]); //输出2 console.log(matrix[2][0]); //输出7
JavaScript在浏览器中的运行环境
JavaScript在浏览器中的运行环境是非常重要的概念,因为大多数开发者初次接触JavaScript时,都是在浏览器环境下进行开发的。以下是对JavaScript在浏览器中运行环境的详细介绍:
浏览器内核
浏览器内核主要包括两部分:渲染引擎和JavaScript引擎。
- 渲染引擎:负责解析HTML和CSS,并将其渲染成用户可以看到的页面。常见的渲染引擎包括Blink(Chrome和Edge)、WebKit(Safari)、Gecko(Firefox)。
- JavaScript引擎:负责解析和执行JavaScript代码。常见的JavaScript引擎包括V8(Chrome和js)、SpiderMonkey(Firefox)、JavaScriptCore(Safari)。
DOM(文档对象模型)
DOM是一种将HTML和XML文档表示为树结构的接口,每个节点代表文档的一部分。JavaScript可以通过DOM操作页面内容。
//获取页面中的元素 const element = document.getElementById('myElement'); //修改元素的内容 element.innerHTML = 'Hello, World!';
BOM(浏览器对象模型)
BOM允许JavaScript与浏览器进行交互,包含了一组用于操作浏览器窗口和实现浏览器功能的对象。
- window:代表浏览器窗口,是全局对象。
- navigator:提供了关于浏览器的信息,如用户代理等。
- location:提供了当前URL的信息,并允许重定向页面。
- history:提供了浏览器的历史记录,可以前进和后退。
- screen:提供了关于用户屏幕的信息。
//获取浏览器的用户代理 console.log(navigator.userAgent); //重定向到另一个页面 location.href = 'https://www.example.com';
BOM操作
BOM(BrowserObjectModel,浏览器对象模型)是JavaScript用来与浏览器进行交互的一组API。BOM提供了操作浏览器窗口和框架、处理浏览器历史记录、管理cookies和其他浏览器相关任务的方法。以下是对BOM主要组件及其操作的详细介绍:
window 对象
window对象是BOM的核心,它表示浏览器窗口。window对象是所有其他对象的全局对象。
示例:
console.log(window.innerWidth); //获取窗口的宽度 console.log(window.innerHeight); //获取窗口的高度
常用方法和属性
尺寸和位置:
window.innerWidth; //获取窗口的内部宽度(不包括工具栏和滚动条) window.innerHeight; //获取窗口的内部高度 window.outerWidth; //获取窗口的外部宽度(包括工具栏和滚动条) window.outerHeight; //获取窗口的外部高度 window.moveTo(x, y); //将窗口移动到(x,y)坐标 window.resizeTo(width, height); //调整窗口大小
弹出窗口:
window.open(url, name, specs, replace);//打开新窗口 window.close();//关闭当前窗口
定时器:
let timerId = window.setTimeout(function, delay);//设置定时器(一次性) window.clearTimeout(timerId);//清除定时器 let intervalId = window.setInterval(function, interval);//设置间隔定时器(循环) window.clearInterval(intervalId);//清除间隔定时器
对话框:
window.alert(message);//显示警告框 window.confirm(message);//显示确认框,返回true或false window.prompt(message, defaultValue);//显示输入框,返回用户输入的值
document 对象
document对象表示整个页面,是DOM(Document Object Model,文档对象模型)的入口。通过document对象可以访问和操作HTML内容。
示例:
document.title = "New Title";//修改文档标题 console.log(document.getElementById("myElement"));//获取元素
常用方法和属性
查找元素:
document.getElementById(id);//通过ID查找元素 document.getElementsByClassName(className);//通过类名查找元素 document.getElementsByTagName(tagName);//通过标签名查找元素 document.querySelector(selector);//通过CSS选择器查找单个元素 document.querySelectorAll(selector);//通过CSS选择器查找所有元素
创建和操作元素:
let newElement = document.createElement(tagName);//创建新元素 document.body.appendChild(newElement);//将新元素添加到文档中 let textNode = document.createTextNode(text);//创建文本节点 newElement.appendChild(textNode);//将文本节点添加到元素中 newElement.setAttribute(name, value);//设置元素属性 newElement.removeAttribute(name);//移除元素属性
页面内容:
document.body;//获取文档的body元素 document.title;//获取或设置文档标题 document.cookie;//获取或设置文档的cookies
navigator 对象
navigator对象包含关于浏览器的信息,例如用户代理、平台和在线状态等。
示例:
console.log(navigator.userAgent);//浏览器的用户代理字符串 console.log(navigator.platform);//浏览器运行的平台 console.log(navigator.onLine);//浏览器的在线状态
location 对象
location对象表示当前文档的URL,并提供了操作URL和导航的方法。
示例:
console.log(location.href);//获取当前URL location.href = "https://www.example.com";//导航到新URL location.reload();//重新加载当前页面
常用方法和属性
URL组成部分:
location.protocol;//获取URL的协议部分(如"https:") location.hostname;//获取URL的主机名(如"www.example.com") location.port;//获取URL的端口号(如"80"或"443") location.pathname;//获取URL的路径部分(如"/path/to/page") location.search;//获取URL的查询字符串(如"?id=123") location.hash;//获取URL的哈希部分(如"#section1")
导航:
location.assign(url);//导航到新URL location.replace(url);//替换当前页面,不保留历史记录 location.reload(forceReload);//重新加载当前页面,可以选择是否强制从服务器加载
history 对象
history对象用于操作浏览器的会话历史记录,例如在历史记录中前进或后退。
示例:
history.back();//后退一步 history.forward();//前进一步 history.go(-2);//后退两步,负数表示后退,正数表示前进
常用方法
导航历史:
history.back();//后退 history.forward();//前进 history.go(delta);//根据delta值导航
历史记录长度:
console.log(history.length);//获取历史记录的长度
HTML5新增方法:
history.pushState(state, title[, url]);//添加一个新的历史记录条目 history.replaceState(state, title[, url]);//修改当前的历史记录条目
screen 对象
screen对象包含关于用户屏幕的信息,例如屏幕分辨率、颜色深度等。虽然这个对象的使用相对较少,但在某些情况下仍然有用,例如调整布局以适应不同的屏幕大小。
示例:
console.log(screen.width);//获取屏幕的宽度 console.log(screen.height);//获取屏幕的高度 console.log(screen.colorDepth);//获取屏幕的颜色深度
DOM操作
DOM(Document Object Model)树是网页文档的结构化表示,类似于家谱树,但它表示的是网页中的 HTML 或 XML 文档结构。DOM 树将文档解析为节点的层次结构,每个节点表示文档的一部分,比如标签、属性、文本等。这种结构使得 JavaScript 可以通过编程方式访问和操作文档内容和结构。
DOM 树的基本结构
一个典型的 HTML 文档会被解析成如下的 DOM 树结构:
<!DOCTYPE html> <html> <head> <title>DOM Tree Example</title> </head> <body> <h1>Hello, World!</h1> <p>This is a simple DOM tree example.</p> </body> </html>
解析为 DOM 树后的结构如下:
#document | |-- html | |-- head | | | |-- title | | | |-- "DOM Tree Example" (text node) | |-- body | |-- h1 | | | |-- "Hello, World!" (text node) | |-- p | |-- "This is a simple DOM tree example." (text node)
DOM 节点类型
DOM 树由不同类型的节点组成,每种节点都有特定的属性和方法。主要节点类型包括:
- 文档节点(Document Node)
- 整个文档的根节点。
- 通过 document 对象访问。
- 元素节点(Element Node)
- HTML 标签,比如 <html>, <body>, <div> 等。
- 可以通过 .createElement() 方法创建。
- 属性节点(Attribute Node)
- 元素的属性,比如 id, class, href 等。
- 可以通过 .setAttribute() 和 .getAttribute() 方法操作。
- 文本节点(Text Node)
- 元素或属性中的文本内容。
- 可以通过 .createTextNode() 方法创建。
- 注释节点(Comment Node)
- 文档中的注释。
- 可以通过 .createComment() 方法创建。
遍历 DOM 树
通过 DOM 提供的属性和方法,可以遍历和操作 DOM 树。
访问子节点
- parentNode: 获取父节点
- childNodes: 获取子节点集合
- firstChild: 获取第一个子节点
- lastChild: 获取最后一个子节点
示例:
const body = document.body; console.log(body.parentNode); // 输出 <html> 节点 console.log(body.childNodes); // 输出 body 的子节点集合 console.log(body.firstChild); // 输出 <h1> 节点 console.log(body.lastChild); // 输出 <p> 节点
访问兄弟节点
- nextSibling: 获取下一个兄弟节点
- previousSibling: 获取上一个兄弟节点
示例:
const h1 = document.querySelector('h1'); console.log(h1.nextSibling); // 输出 <p> 节点 console.log(h1.previousSibling); // 可能是文本节点或空白节点
操作 DOM 树
DOM(Document Object Model)是 HTML 和 XML 文档的标准表示,它将文档解析为树形结构,使得可以使用 JavaScript 来操作文档的各个部分。以下是一些常见的 DOM 操作:
获取元素
getElementById() 通过元素 ID 获取元素。
const element = document.getElementById('myElement'); console.log(element);
getElementsByClassName() 通过类名获取元素集合。
const elements = document.getElementsByClassName('myClass'); console.log(elements);
getElementsByTagName() 通过标签名获取元素集合。
const elements = document.getElementsByTagName('p'); console.log(elements);
querySelector() 选择匹配 CSS 选择器的第一个元素。
const element = document.querySelector('#myElement'); console.log(element);
querySelectorAll() 选择匹配 CSS 选择器的所有元素,返回一个 NodeList。
const elements = document.querySelectorAll('.myClass, p'); console.log(elements);
元素操作
innerHTML 设置或获取元素的 HTML 内容。
const element = document.getElementById('myElement'); element.innerHTML = '<h1>Hello, World!</h1>'; console.log(element.innerHTML);
innerText 和 textContent 设置或获取元素的文本内容。
const element = document.getElementById('myElement'); element.innerText = 'Hello, World!'; console.log(element.innerText);
appendChild() 向元素的子节点末尾添加新元素。
const parent = document.getElementById('parent'); const child = document.createElement('div'); child.textContent = 'Child Element'; parent.appendChild(child);
insertBefore()在指定元素之前插入新元素。
const parent = document.getElementById('parent'); const existingChild = document.getElementById('existingChild'); const newChild = document.createElement('div'); newChild.textContent = 'New Child'; parent.insertBefore(newChild, existingChild);
removeChild()移除元素。
const parent = document.getElementById('parent'); const childToRemove = document.getElementById('childToRemove'); parent.removeChild(childToRemove);
属性操作
getAttribute()获取元素的属性值。
const element = document.getElementById('myElement'); const attributeValue = element.getAttribute('data-id'); console.log(attributeValue);
setAttribute()设置元素的属性值。
const element = document.getElementById('myElement'); element.setAttribute('data-id', '123');
removeAttribute()移除元素的属性。
const element = document.getElementById('myElement'); element.removeAttribute('data-id');
事件处理
addEventListener()为元素添加事件监听器。
const button = document.getElementById('myButton'); button.addEventListener('click', () => { console.log('Button clicked'); });
removeEventListener()移除事件监听器。
const button = document.getElementById('myButton'); button.removeEventListener('click', clickHandler); function clickHandler(){ console.log('Button clicked'); }
样式操作
style直接操作元素的内联样式。
const element = document.getElementById('myElement'); element.style.color = 'red'; element.style.fontSize = '20px';
getComputedStyle()获取元素的计算样式(包括浏览器应用的默认样式和继承样式)。
const element = document.getElementById('myElement'); const styles = window.getComputedStyle(element); console.log(styles.getPropertyValue('color'));
事件处理
在JavaScript中,事件处理是非常重要的概念,主要用于响应用户的交互操作如点击、输入、悬停等。事件处理的关键组成部分包括事件监听器(或事件处理程序)、事件对象和事件传播机制。
事件驱动编程
JavaScript是一种事件驱动语言,这意味着程序的行为可以由各种事件触发,如用户的点击、键盘输入、鼠标移动、文件加载完成等。事件驱动编程是JavaScript在Web开发中非常重要的特性,它允许开发者编写响应用户交互的代码。以下是对JavaScript事件驱动核心特性的详细介绍。
事件驱动编程是一种编程范式,其中程序的执行是由事件触发的。事件可以是用户的动作(如点击按钮、滚动页面)或系统的变化(如AJAX请求完成、定时器结束)。在事件驱动编程中,程序等待事件的发生,当事件发生时,执行相应的事件处理程序。
事件监听与事件处理程序
事件监听器(EventListener)或事件处理程序(EventHandler)是绑定到特定元素上的函数,当特定事件发生时,这些函数会被调用。
添加事件监听器
可以使用addEventListener方法来添加事件监听器,这是推荐的方式,因为它允许在同一个元素上添加多个事件处理程序。
const button = document.getElementById('myButton'); button.addEventListener('click', function(event){ console.log('Button clicked!'); });
移除事件监听器
可以使用removeEventListener方法来移除事件监听器。
function handleClick(event){ console.log('Button clicked!'); } button.addEventListener('click', handleClick); //需要传入相同的函数引用才能成功移除 button.removeEventListener('click', handleClick);
常见事件类型
JavaScript支持许多类型的事件,以下是一些常见的事件类型:
- 鼠标事件:click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave
- 键盘事件:keydown, keypress, keyup
- 表单事件:submit, change, input, focus, blur
- 窗口事件:load, resize, scroll, unload
事件对象
当事件被触发时,会产生一个事件对象(Event Object),该对象包含了事件的相关信息,并作为参数传递给事件处理程序。
常见的事件对象属性
- type:事件的类型(如"click", "keydown")
- target:触发事件的元素
- currentTarget:绑定事件处理程序的元素
- preventDefault():阻止事件的默认行为
- stopPropagation():停止事件传播
- clientX 和 clientY:鼠标点击时在窗口中的坐标
- key:键盘事件中按下的键的值
示例:
button.addEventListener('click', function(event) { console.log('Event type:', event.type); console.log('Event target:', event.target); console.log('Mouse coordinates:', event.clientX, event.clientY); });
事件传播(Event Propagation)
事件传播机制决定了事件在 DOM 树中的传播方式。事件传播分为三个阶段:
- 捕获阶段(Capturing Phase):事件从根元素向目标元素传播
- 目标阶段(Target Phase):事件到达目标元素
- 冒泡阶段(Bubbling Phase):事件从目标元素向根元素传播
捕获和冒泡
默认情况下,事件处理程序在冒泡阶段触发。可以通过传递第三个参数 true 给 addEventListener 方法来使事件处理程序在捕获阶段触发。
const parent = document.getElementById('parent'); const child = document.getElementById('child'); // 捕获阶段 parent.addEventListener('click', function(event) { console.log('Parent capturing'); }, true); // 冒泡阶段 parent.addEventListener('click', function(event) { console.log('Parent bubbling'); }); child.addEventListener('click', function(event) { console.log('Child clicked'); });
阻止事件传播
可以使用 stopPropagation() 方法来阻止事件在 DOM 树中的传播。
parent.addEventListener('click', function(event) { console.log('Parent clicked'); }); child.addEventListener('click', function(event) { event.stopPropagation(); console.log('Child clicked'); });
阻止默认行为
有时候可能需要阻止事件的默认行为,可以使用 preventDefault() 方法。
const link = document.getElementById('myLink'); link.addEventListener('click', function(event) { event.preventDefault(); console.log('Default action prevented'); });
事件代理(Event Delegation)
事件代理是一种将事件处理程序添加到父元素而不是多个子元素的方法。通过利用事件冒泡机制,可以使父元素处理来自子元素的事件,从而提高性能和简化代码。
假设有一个动态生成的列表项,你希望在点击任意列表项时执行某些操作。添加事件处理程序到每个列表项会非常低效,可以使用事件代理:
<ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul>
const list = document.getElementById('myList'); list.addEventListener('click', function(event) { if(event.target && event.target.nodeName === 'LI') { console.log('List item clicked:', event.target.textContent); } });
异步事件
JavaScript 的事件驱动模型非常适合处理异步操作,如 AJAX 请求、定时器等。异步操作可以通过事件触发回调函数来处理。
使用定时器的异步事件:
setTimeout(function() { console.log('This message is displayed after 2 seconds'); }, 2000); // 异步的 AJAX 请求: fetch('https://api.example.com/data') .then(response => response.json()) .then(data => { console.log('Data fetched:', data); }) .catch(error => { console.error('Error fetching data:', error); });
面向对象编程
面向对象编程(OOP)是一种编程范式,通过将数据和操作数据的函数封装在对象中来实现。JavaScript 是一种基于原型的面向对象编程语言,但 ES6 引入了类(class)语法,使得面向对象编程变得更加直观和易于理解。
类与实例
类(Class)
类是创建对象的蓝图或模板。ES6 引入了 class 关键字来定义类。
class Person { // 构造函数 constructor(name, age) { this.name = name; this.age = age; } // 方法 greet() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } }
实例(Instance)
实例是从类创建的对象。使用 new 关键字可以创建类的实例。
const john = new Person('John', 30); john.greet(); // 输出: Hello, my name is John and I am 30 years old. const jane = new Person('Jane', 25); jane.greet(); // 输出: Hello, my name is Jane and I am 25 years old.
构造函数模式
在 ES6 之前,JavaScript 没有类(class)的概念,主要通过构造函数和原型来模拟类和继承。
构造函数
构造函数是一种特殊的函数,用于创建和初始化对象实例。它们通常与 new 操作符一起使用。
function Person(name, age) { this.name = name; this.age = age; } const alice = new Person("Alice", 25); console.log(alice.name); // 输出 "Alice" console.log(alice.age); // 输出 25
原型继承
每个JavaScript函数都有一个 prototype 属性,指向一个对象。这个对象是通过构造函数创建的实例的原型。
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.greet = function() { console.log("Hello, my name is " + this.name); }; const alice = new Person("Alice", 25); alice.greet(); // 输出 "Hello, my name is Alice"
ES6 class 语法
ES6引入了 class 语法,使面向对象编程更加直观和简洁。
定义类
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log("Hello, my name is " + this.name); } } const alice = new Person("Alice", 25); alice.greet(); // 输出 "Hello, my name is Alice"
继承
使用 extends 关键字实现类的继承,子类可以继承父类的属性和方法。
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log("Hello, my name is " + this.name); } } class Student extends Person { constructor(name, age, major) { super(name, age); // 调用父类的构造函数 this.major = major; } study() { console.log(this.name + " is studying " + this.major); } } const alice = new Student("Alice", 25, "Computer Science"); alice.greet(); // 输出 "Hello, my name is Alice" alice.study(); // 输出 "Alice is studying Computer Science"
继承(extends)
继承是面向对象编程的一个重要特性,允许一个类继承另一个类的属性和方法。使用 extends 关键字可以实现继承。
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a noise.`); } } class Dog extends Animal { constructor(name, breed) { super(name); // 调用父类的构造函数 this.breed = breed; } speak() { console.log(`${this.name} barks.`); } } const myDog = new Dog('Rex', 'German Shepherd'); myDog.speak(); // 输出: Rex barks.
在上面的例子中,Dog 类继承了 Animal 类,并且重写了 speak 方法。super 关键字用于调用父类的构造函数和方法。
原型链(Prototype Chain)
JavaScript中的每个对象都有一个原型对象(prototype),对象可以通过原型从其他对象继承属性和方法。这种继承机制称为原型链。
原型对象与 __proto__
每个函数都有一个 prototype 属性,指向其原型对象。对象通过 __proto__ 属性指向其构造函数的原型对象。
function Animal(name) { this.name = name; } Animal.prototype.speak = function() { console.log(`${this.name} makes a noise.`); }; const cat = new Animal('Whiskers'); cat.speak(); // 输出: Whiskers makes a noise. console.log(cat.__proto__ === Animal.prototype); // 输出: true
原型链示例
当访问一个对象的属性或方法时,JavaScript引擎首先在对象自身查找,如果找不到,会沿着原型链继续查找。
function Animal(name) { this.name = name; } Animal.prototype.speak = function() { console.log(`${this.name} makes a noise.`); }; function Dog(name, breed) { Animal.call(this, name); this.breed = breed; } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.speak = function() { console.log(`${this.name} barks.`); }; const myDog = new Dog('Rex', 'German Shepherd'); myDog.speak(); // 输出: Rex barks. console.log(myDog.__proto__ === Dog.prototype); // 输出: true console.log(Dog.prototype.__proto__ === Animal.prototype); // 输出: true
在这个例子中,Dog 继承了 Animal,并且通过 Object.create(Animal.prototype) 设置了原型链。访问 myDog.speak 时,JavaScript会首先在 myDog 对象查找,如果找不到,会沿着原型链在 Dog.prototype 和 Animal.prototype 中查找。
类的静态方法和静态属性
静态方法和静态属性是直接定义在类本身上的,而不是类的实例上的。
静态方法
class MathHelper { static square(x) { return x * x; } } console.log(MathHelper.square(5)); // 输出 25
静态属性
class Configuration { static DEFAULT_TIMEOUT = 5000; } console.log(Configuration.DEFAULT_TIMEOUT); // 输出 5000
私有属性和方法
当前,JavaScript提供了一种方式来定义类的私有属性和方法,通过在属性或方法前面加上 # 字符。
私有属性
class Person { #name; #age; constructor(name, age) { this.#name = name; this.#age = age; } greet() { console.log("Hello, my name is " + this.#name); } } const alice = new Person("Alice", 25); alice.greet(); // 输出 "Hello, my name is Alice" console.log(alice.#name); // 抛出错误:SyntaxError: Private field '#name' must be declared in an enclosing class
Getters 和 Setters
Getters 和 Setters 提供了一种更灵活的方式来访问和设置对象的属性。
class Person { constructor(name, age) { this._name = name; // 注意属性名的命名约定 this._age = age; } get name() { return this._name; } set name(newName) { this._name = newName; } get age() { return this._age; } set age(newAge) { if (newAge > 0) { this._age = newAge; } else { console.log("Age must be positive"); } } } const alice = new Person("Alice", 25); console.log(alice.name); // 输出 "Alice" alice.name = "Bob"; console.log(alice.name); // 输出 "Bob" alice.age = -5; // 输出 "Age must be positive" console.log(alice.age); // 输出 25
多态性
多态性允许一个对象在不同的上下文中表现出不同的行为。通过方法的重载和覆盖来实现多态性。
方法重载(JavaScript 不直接支持,但可以通过参数处理)
class MathHelper { static add(a, b) { if (typeof a === 'string' || typeof b === 'string') { return String(a) + String(b); } return a + b; } } console.log(MathHelper.add(1, 2)); // 输出 3 console.log(MathHelper.add("Hello", "World")); // 输出 "HelloWorld"
方法覆盖
class Animal { makeSound() { console.log("Some generic sound"); } } class Dog extends Animal { makeSound() { console.log("Bark"); } } const myDog = new Dog(); myDog.makeSound(); // 输出 "Bark"
异步编程
JavaScript 是一种单线程语言,这意味着它一次只能执行一段代码。然而,现代 Web 应用程序需要处理许多任务,比如网络请求、文件读取和计时等,这些任务可能会耗费大量时间。如果这些任务是同步执行的,那么会阻塞主线程,导致用户界面无响应。为了避免这种情况,JavaScript 提供了异步编程的机制。JavaScript 中有多种实现异步编程的方式,包括回调函数、Promise、async/await 等。
回调函数
回调函数(Callback Function)是最基本的异步编程方式。一个函数作为参数传递给另一个函数,并在任务完成时调用该回调函数。
示例:
function fetchData(callback) { setTimeout(() => { const data = { name: 'Alice', age: 25 }; callback(data); }, 2000); } fetchData((data) => { console.log('Data received:', data); }); // 输出(2 秒后): // Data received: { name: 'Alice', age: 25 }
Promise
Promise 是 ES6 引入的一种异步编程方式,提供了一种更加优雅和链式调用的方式来处理异步操作。Promise 提供了一种更优雅的方式来处理异步操作,避免了回调地狱。Promise 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
创建和使用 Promise
const fetchData = () => { return new Promise((resolve, reject) => { setTimeout(() => { const data = { name: 'Alice', age: 25 }; resolve(data); }, 2000); }); }; fetchData() .then((data) => { console.log('Data received:', data); }) .catch((error) => { console.error('Error:', error); }); // 输出(2 秒后): // Data received: { name: 'Alice', age: 25 }
Promise 链式调用
多个异步操作可以通过链式调用来处理,避免嵌套回调(回调地狱)。
const fetchData = () => { return new Promise((resolve, reject) => { setTimeout(() => { const data = { name: 'Alice', age: 25 }; resolve(data); }, 2000); }); }; fetchData() .then((data) => { console.log('Data received:', data); return new Promise((resolve, reject) => { setTimeout(() => { data.age += 1; resolve(data); }, 2000); }); }) .then((updatedData) => { console.log('Updated data:', updatedData); }) .catch((error) => { console.error('Error:', error); }); // 输出(2 秒后): // Data received: { name: 'Alice', age: 25 } // 输出(再 2 秒后): // Updated data: { name: 'Alice', age: 26 }
async/await
async 和 await 是 ES8 引入的语法糖,用于简化基于 Promise 的异步编程,使代码看起来更像同步代码。
定义 async 函数
使用 async 关键字定义的函数返回一个 Promise。await 关键字只能在 async 函数内部使用,用于等待一个 Promise 的结果。
示例:
const fetchData = () => { return new Promise((resolve, reject) => { setTimeout(() => { const data = {name: 'Alice', age: 25}; resolve(data); }, 2000); }); }; const processData = async () => { try { const data = await fetchData(); console.log('Data received:', data); const updatedData = await new Promise((resolve, reject) => { setTimeout(() => { data.age += 1; resolve(data); }, 2000); }); console.log('Updated data:', updatedData); } catch (error) { console.error('Error:', error); } }; processData(); //输出(2秒后): //Data received: {name: 'Alice', age: 25} //输出(再2秒后): //Updated data: {name: 'Alice', age: 26}
异步编程的常见场景
事件处理
事件处理是异步的,当事件发生时,回调函数会被调用。
document.getElementById('myButton').addEventListener('click', () => { console.log('Button clicked'); });
定时器
setTimeout和setInterval是异步的。 setTimeout(() => { console.log('Executed after 2 seconds'); }, 2000); let count = 0; const intervalId = setInterval(() => { count++; console.log('Interval executed', count); if (count === 5) { clearInterval(intervalId); } }, 1000);
网络请求
使用fetch API发送网络请求时,返回一个Promise。
const fetchData = async () => { try { const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); if (!response.ok) { throw new Error('Network response was not ok'); } const data = await response.json(); console.log('Data:', data); } catch (error) { console.error('Error:', error); } }; fetchData(); //输出: //Data: {userId: 1, id: 1, title: "delectus aut autem", completed: false}
并行执行异步任务
有时候需要并行执行多个异步操作,可以使用Promise.all和Promise.race来处理这种情况。
Promise.all
Promise.all接受一个包含多个Promise的数组,当所有Promise都完成时,返回一个新的Promise,该Promise的值是一个数组,包含每个Promise的结果。
const promise1 = doSomethingAsync(); const promise2 = doSomethingAsync(); const promise3 = doSomethingAsync(); Promise.all([promise1, promise2, promise3]) .then(results => { console.log(results); //输出: ['Async operation completed', 'Async operation completed', 'Async operation completed'] }) .catch(error => { console.error(error); });
Promise.race
Promise.race接受一个包含多个Promise的数组,当任意一个Promise完成时,返回一个新的Promise,该Promise的值是第一个完成的Promise的结果。
const promise1 = new Promise((resolve) => setTimeout(resolve, 100, 'First')); const promise2 = new Promise((resolve) => setTimeout(resolve, 200, 'Second')); Promise.race([promise1, promise2]) .then(result => { console.log(result); //输出: First }) .catch(error => { console.error(error); });
错误处理
JavaScript的错误处理机制帮助开发者捕获和处理运行时错误,确保代码更加健壮和可靠。主要的错误处理机制包括try...catch语句、throw语句、自定义错误类型以及现代的Promise和async/await异步错误处理。
try...catch 语句
try...catch语句用于捕获和处理代码块中的异常。
语法:
try { //尝试执行的代码 } catch(error) { //捕获错误并处理 } finally { //可选的,始终会执行的代码块 }
示例:
try { let result = someUndefinedFunction(); console.log(result); } catch(error) { console.error('Error caught:', error.message); } finally { console.log('This will always run'); } //输出: //Error caught: someUndefinedFunction is not defined //This will always run
finally 代码块
无论是否发生错误,finally代码块中的代码都会执行,通常用于释放资源或做清理工作。
try { let result = someFunction(); } catch(error) { console.error('Error caught:', error.message); } finally { console.log('Cleaning up...'); }
throw 语句
throw语句用于抛出一个自定义错误,可以是字符串、数字、布尔值或对象。
示例:
function divide(a, b) { if (b === 0) { throw new Error('Division by zero is not allowed.'); } return a / b; } try { console.log(divide(10, 0)); } catch (error) { console.error('Caught an error:', error.message); } //输出: //Caught an error: Division by zero is not allowed.
错误类型
JavaScript中的错误类型是为了帮助开发者识别和处理程序中的各种异常情况。理解这些错误类型有助于更好地进行调试和错误处理。以下是JavaScript中常见的错误类型及其详细解释:
Error
Error是所有错误类型的基类,所有其他错误类型都继承自Error。可以直接使用Error构造函数创建一个通用的错误对象。
示例:
throw new Error("Something went wrong");
SyntaxError
SyntaxError表示代码的语法错误,例如缺少括号、分号等。通常在代码解析阶段(编译时)就会被发现。
示例:
try { eval("var a = ;"); //语法错误 } catch(e) { console.log(e instanceof SyntaxError); //true }
TypeError
TypeError表示变量或参数不是预期类型。例如,试图调用非函数类型的变量,或者访问未定义的属性。
示例:
try { const obj = null; obj.prop = 123; //TypeError: Cannot set property 'prop' of null } catch(e) { console.log(e instanceof TypeError); //true }
ReferenceError
ReferenceError表示对未定义的变量进行引用。例如,访问一个不存在的变量。
示例:
try { console.log(nonExistentVariable); //ReferenceError: nonExistentVariable is not defined } catch(e) { console.log(e instanceof ReferenceError); //true }
RangeError
RangeError表示数值超出允许的范围。例如,数组长度为负,或者传递给函数的参数超出范围。
示例:
try { const arr = new Array(-1); //RangeError: Invalid array length } catch(e) { console.log(e instanceof RangeError); //true }
URIError
URIError表示全局URI处理函数(如decodeURI、decodeURIComponent、encodeURI、encodeURIComponent)的参数无效。
示例:
try { decodeURIComponent("%"); //URIError: URI malformed } catch(e) { console.log(e instanceof URIError); //true }
EvalError
EvalError表示对eval函数的错误使用。在现代JavaScript中,这种错误类型几乎不再使用,但为了向后兼容仍然存在。
示例:
try { throw new EvalError("EvalError example"); } catch(e) { console.log(e instanceof EvalError); //true }
AggregateError
AggregateError是ES2021引入的一种新的错误类型,用于表示多个错误的集合,通常与Promise.any方法一起使用。
示例:
try { throw new AggregateError([ new Error("Error 1"), new Error("Error 2") ], "Multiple errors occurred"); } catch(e) { console.log(e instanceof AggregateError); //true console.log(e.errors); //[Error: Error 1, Error: Error 2] }
自定义错误类型
通过继承Error类,可以创建自定义错误类型,添加更多的上下文信息。
示例:
class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; } } function validateUser(user) { if (!user.name) { throw new ValidationError('Name is required'); } if (!user.age) { throw new ValidationError('Age is required'); } //其他验证逻辑 } try { validateUser({name: 'Alice'}); } catch(error) { if (error instanceof ValidationError) { console.error('ValidationError:', error.message); } else { console.error('Unexpected Error:', error.message); } } //输出: //ValidationError: Age is required
Promise错误处理
在异步编程中,Promise提供了一种处理错误的机制。可以使用.catch()方法捕获Promise链中的错误。
示例:
const fetchData = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Failed to fetch data')); }, 2000); }); }; fetchData() .then(data => { console.log('Data:', data); }) .catch(error => { console.error('Caught an error:', error.message); }); //输出: //Caught an error: Failed to fetch data
async/await错误处理
async和await提供了一种更加直观的方式来处理异步代码,可以结合try...catch来捕获错误。
示例:
const fetchData = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Failed to fetch data')); }, 2000); }); }; const processData = async () => { try { const data = await fetchData(); console.log('Data:', data); } catch (error) { console.error('Caught an error:', error.message); } }; processData(); //输出: //Caught an error: Failed to fetch data
全局错误处理
有时需要处理未捕获的全局错误,可以使用 window.onerror 或 unhandledrejection 事件来捕获全局错误和未处理的 Promise 拒绝。
全局错误捕获
window.onerror = (message, source, lineno, colno, error) => { console.error('Global error caught:', message); //可以选择返回 true 来防止默认的浏览器错误提示 return true; }; //触发一个未捕获的错误 someUndefinedFunction();
未处理的 Promise 拒绝
window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled promise rejection:', event.reason); }); //触发一个未处理的 Promise 拒绝 Promise.reject(new Error('Promise rejected'));
JavaScript 框架
热门的 JavaScript 框架和库遍布前端和后端开发领域,它们帮助开发者更加高效地构建和维护应用程序。以下是一些当前最流行且广泛使用的 JavaScript 框架和库:
前端框架和库
- React
- 概述: 由 Facebook 开发和维护,是一个用于构建用户界面的库,特别适用于单页应用(SPA)。
- 特点: 组件化开发、虚拟 DOM、单向数据流。
- 官网: React
- js
- 概述: 由尤雨溪(Evan You)开发的渐进式 JavaScript 框架,适用于构建用户界面。
- 特点: 易于学习和使用、双向数据绑定、渐进式框架。
- 官网: js
- Angular
- 概述: 由 Google 开发和维护的前端框架,适用于构建复杂和大型的单页应用。
- 特点: 完整的框架、强类型支持(TypeScript)、依赖注入、内置路由和表单处理。
- 官网: Angular
- Svelte
- 概述: 一个新兴的前端框架,通过编译时将组件转换为高效的命令式代码,减少了运行时开销。
- 特点: 编译时框架、更少的开销和更高的性能、简洁的语法。
- 官网: Svelte
- js
- 概述: 一个对开发者友好的框架,用于构建具有高度交互性的前端应用。
- 特点: 强大的路由系统、内建最佳实践、双向数据绑定。
- 官网: js
后端框架
- js
- 概述: 虽然不是一个框架,但 js 是一个重要的 JavaScript 运行时,允许在服务器端运行 JavaScript,支持事件驱动和非阻塞 I/O。
- 特点: 高并发性能、丰富的包生态系统(通过 npm)。
- 官网: js
- js
- 概述: 一个轻量级的 js Web 应用框架,简洁且灵活。
- 特点: 简洁的 API、强大的路由机制、中间件支持。
- 官网: Express
- NestJS
- 概述: 一个用于构建高效、可扩展的 js 服务器端应用的框架,构建在 TypeScript 之上。
- 特点: 模块化架构、依赖注入、内置支持微服务。
- 官网: NestJS
- Koa
- 概述: 由 Express 的原班人马创建的一个新一代 js 框架,致力于更小、更具表现力和更强大的基础。
- 特点: 更简洁的 API、良好的错误处理机制、现代化设计。
- 官网: Koa
- js
- 概述: 一个用于构建强大且可扩展的应用程序的 js 框架。
- 特点: 丰富的插件系统、内置验证和授权、配置驱动的声明式编码。
- 官网: js
流行趋势和选择建议
选择哪个框架或库取决于你的项目需求和个人偏好:
- React 和 js 是前端开发中的主流选择,适合构建现代、响应式的用户界面。
- Angular 适合大型企业级应用开发,由于其完整的解决方案和强类型支持。
- Svelte 是一个新兴的选择,性能优秀且代码简洁,非常适合追求高效和现代开发的项目。
- js 和 NestJS 是后端开发的主流选择,前者轻量且灵活,后者则是一个更加全面和结构化的框架。
- js 是全栈开发中的重要技术,通过其丰富的包生态系统,可以快速构建和部署应用。
无论选择哪个框架或库,掌握其核心概念和生态系统中的工具都是非常重要的。希望这些信息能帮助你更好地选择适合自己的框架进行学习和实践。如果有任何问题,请随时向我提问!
参考链接: