var 在函数或全局作用域中声明变量,无论在块级作用域内外都可以访问。
var 即使在块级作用域外部访问,仍然能获取到结果。
let 在块级作用域内声明变量,仅在其所在的作用域中有效。
let 如果在作用域外访问,会抛出 ReferenceError
// 示例
{
var a = 10;
let b = 20;
}
console.log(a); // 10 (var 在全局或函数作用域内有效)
console.log(b); // ReferenceError: b is not defined (let 仅在块级作用域内有效)
1.2重复定义
复制代码
var: 可以在同一个作用域内多次声明同名变量,而不会抛出错误,后面声明的变量会覆盖前面的值。
let: 不允许在同一作用域内重复声明同名变量,重复声明会抛出 SyntaxError。
// 示例
{
var a = 1;
var a = 2; // 无报错,a 赋值为 2
console.log(a); // 2
let b = 1;
//let b = 2; // 报错Uncaught SyntaxError: Identifier 'b' has already been declared
console.log(b); // 1
}
1.3变量提升
复制代码
var: 变量声明会被提升到作用域的顶部,但赋值不会被提升,因此会在使用变量前显示为 undefined。
let: 变量声明不会被提升,尝试在声明前访问会抛出 ReferenceError,因为变量在声明之前处于"暂时性死区(TDZ)。
// 示例
{
console.log(a); // undefined (var 变量会提升,初始化为 undefined)
var a = 10;
console.log(b); // Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 10; // let 变量不会提升,访问前会抛出错误
}
1.4块级作用域与闭包
复制代码
var在块级作用域内声明的变量会被提升到函数或全局作用域,因此可能会影响闭包的行为。
let创建的是块级作用域变量,避免了 var 在闭包中的不期望行为。
// 示例
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i); // 输出 3, 由于 var 的提升,i 的值在 for 循环外部是 3
}, 100);
}
for (let j = 0; j < 3; j++) {
setTimeout(function () {
console.log(j); // 输出 0, 1, 2, 因为 let 会在每次迭代中创建新的作用域
}, 100);
}
1.5总结
复制代码
var 和 let 的主要区别在于作用域、变量提升、以及是否支持重复定义。
var 是函数作用域或全局作用域,且会提升到顶部;let 是块级作用域,不会提升。
let 在闭包和异步代码中表现得更稳定,避免了 var 可能带来的问题。
// 示例
{
var a = 10;
let b = 20;
}
console.log(a); // 10 (var 在全局或函数作用域内有效)
console.log(b); // ReferenceError: b is not defined (let 仅在块级作用域内有效)
2.const 与 let 的差异
复制代码
const: 声明的是常量,必须在声明时初始化,且值不能修改。
但 const 声明的是引用类型的对象时,对象的内容是可变的,只是不能重新赋值给其他对象。
const a = 10;
a = 20; // Uncaught TypeError: Assignment to constant variable.
console.log(a)
const obj = {name: 'tom',age: 18};
obj.age = 22;
console.log(obj) // {name: 'tom', age: 22}
obj = {}; // Uncaught TypeError: Assignment to constant variable.
总结:
var 和 let 的主要区别在于作用域、变量提升、以及是否支持重复定义。
var 是函数作用域或全局作用域,且会提升到顶部;let 是块级作用域,不会提升。
let 在闭包和异步代码中表现得更稳定,避免了 var 可能带来的问题。
3.解构
3.1数组解构
复制代码
解构赋值允许从数组中提取值并赋给变量。通过解构语法,我们可以更简洁地获取数组元素。
let arr = [1, 2, 3]
let [a, b, c] = arr
// 等价于
// let a = arr[0]
// let b = arr[1]
// let c = arr[2]
console.log(a)
console.log(b)
console.log(c)
3.2对象解构
复制代码
对象解构允许从对象中提取值并赋给变量,变量名必须与对象的属性名相同。
let obj = { name: 'tom', age: 18 };
let { name, age } = obj
// 等价于
// let name = obj.name
// let age = obj.age
console.log(name)
console.log(age)
可以导出多个变量或函数。导出的名称需要与导入时的名称一致。
//写法一
//导出变量
export const PI = 3.14;
//导出函数
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
//导出类
export class Person {
constructor(name, age) {
this.name = name
this.age = age
}
sayHello() {
console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
}
}
//写法二
//导出变量
const PI = 3.14;
export {PI}
//导出函数
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
export{add,subtract}
10.2默认导出(Default Exports)
复制代码
默认导出可以是一个函数、对象、类等。每个模块只能有一个默认导出。
//greet.js
//默认导出变量
export default 10
const person = {
name: 'Amy',
age: 20,
}
export default person
//默认导出函数
export default function greet(name) {
return `Hello, ${name}!`;
}
//默认导出类
export default class Person {
constructor(name, age) {
this.name = name
this.age = age
}
sayHello() {
console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
}
}
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14;
export * from './math.js';
// app.js
import * as math from './index.js';
console.log(math.add(1, 2)); // 3
console.log(math.subtract(5, 3)); // 2
console.log(math.PI); // 3.14
// app.js
import { add as addition, subtract as subtraction, PI as pi } from './math.js';
console.log(addition(2, 3)); // 5
console.log(subtraction(5, 3)); // 2
console.log(pi); // 3.14
10.8export 与 import 的复合写法
复制代码
如果在一个模块之中,先输入后输出同一个模块,import 语句可以与 export 语句写在一起。
export { foo, bar } from 'my_module'
// 可以简单理解为
import { foo, bar } from 'my_module'
export { foo, bar }