1. let
和 const
概念
let
:用于声明 块级作用域 的变量。const
:用于声明 块级作用域 的常量,声明后不可重新赋值(但可以修改对象的属性或数组的内容)。
原理
JavaScript 在 ES5 中只有全局作用域和函数作用域,没有块级作用域。let
和 const
引入了块级作用域,使得变量的生命周期仅限于块内(例如 {}
中),避免了变量污染和提升问题。
用法
javascript
// let 示例
if (true) {
let x = 10;
console.log(x); // 10
}
// console.log(x); // 报错,x 未定义
// const 示例
const PI = 3.14;
// PI = 3.14159; // 报错,常量不可重新赋值
const obj = { name: 'John' };
obj.name = 'Alice'; // 可以修改对象的属性
与 var
的区别
var
有函数作用域和提升(hoisting),而let
和const
有块级作用域,无提升。var
允许重复声明,而let
和const
不允许。
javascript
var x = 10;
var x = 20; // 合法
let y = 10;
// let y = 20; // 报错,重复声明
2. 箭头函数
概念
箭头函数是一种简化函数定义的语法,使用 =>
。它没有自己的 this
,arguments
,super
或 new.target
,this
继承自外层作用域。
原理
普通函数的 this
是动态绑定的,取决于调用方式。而箭头函数的 this
是静态绑定的,继承自定义时的外层作用域。
用法
javascript
// 普通函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
// this 绑定示例
const person = {
name: 'John',
greet: function() {
setTimeout(() => {
console.log(`Hello, ${this.name}`); // this 继承自 greet
}, 1000);
}
};
person.greet(); // Hello, John
与普通函数的区别
- 箭头函数不能作为构造函数(不能使用
new
)。 - 箭头函数没有
arguments
对象。 - 箭头函数不能使用
yield
,不能作为生成器函数。
3. 模板字符串
概念
模板字符串使用反引号(`````),可以嵌入变量和多行文本,支持表达式。
原理
模板字符串在编译时会被解析为普通字符串,嵌入的变量和表达式会被替换为实际值。
用法
javascript
const name = 'Alice';
const age = 25;
const message = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(message); // Hello, my name is Alice and I am 25 years old.
// 多行文本
const multiLine = `
This is line 1.
This is line 2.
`;
console.log(multiLine);
与传统字符串的区别
- 传统字符串需要使用
+
拼接变量,模板字符串更简洁。 - 模板字符串支持多行文本,传统字符串需要手动换行符
\n
。
4. 解构赋值
概念
解构赋值允许从数组或对象中提取值,并赋值给变量。
原理
解构赋值是一种语法糖,底层仍然是通过逐个赋值实现。
用法
javascript
// 数组解构
const [a, b] = [1, 2];
console.log(a, b); // 1 2
// 对象解构
const { name, age } = { name: 'John', age: 25 };
console.log(name, age); // John 25
// 默认值
const [x = 10, y = 20] = [1];
console.log(x, y); // 1 20
与传统赋值的区别
- 传统赋值需要逐个提取值,解构赋值更简洁。
- 解构赋值支持默认值,避免变量为
undefined
。
5. 默认参数
概念
默认参数允许在函数定义时为参数设置默认值,如果调用时未提供该参数,则使用默认值。
原理
在函数调用时,如果参数值为 undefined
,则使用默认值。
用法
javascript
function greet(name = 'Guest') {
console.log(`Hello, ${name}`);
}
greet(); // Hello, Guest
greet('Alice'); // Hello, Alice
与传统方法的区别
- 传统方法需要在函数体内判断参数是否为
undefined
,默认参数更简洁。
6. 扩展运算符和剩余参数
概念
- 扩展运算符(
...
):用于展开数组或对象。 - 剩余参数(
...
):用于将剩余的参数收集为一个数组。
原理
扩展运算符将数组或对象的内容"展开"为独立的值。剩余参数将剩余的值"收集"为一个数组。
用法
javascript
// 扩展运算符
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4]
// 剩余参数
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3)); // 6
与 arguments
的区别
arguments
是一个类数组对象,剩余参数是一个真正的数组
7. 类和继承
概念
ES6 引入了 class
关键字,用于定义类和继承。
原理
class
是语法糖,底层仍然是基于原型链的继承。
用法
javascript
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog('Rex');
dog.speak(); // Rex barks.
与传统原型链的区别
class
语法更直观,易于理解。extends
关键字简化了继承的实现。
8. **模块化(import
和 export
)
概念
ES6 引入了模块化语法,允许将代码分割成多个文件,通过 import
和 export
进行导入和导出。
原理
模块化将代码组织为独立的单元,每个模块有自己的作用域。
用法
javascript
// 导出模块
export const name = 'John';
export function greet() {
console.log('Hello!');
}
// 导入模块
import { name, greet } from './module.js';
console.log(name); // John
greet(); // Hello!
与 CommonJS 的区别
- CommonJS 使用
require
和module.exports
,ES6 模块化语法更现代化。
- Symbol
概念
Symbol
是一种新的原始数据类型,表示 唯一 的值。通常用于创建对象的唯一属性名,避免属性名冲突。
原理
每次调用 Symbol()
都会生成一个唯一的值,即使传入相同的参数。
用法
javascript
const sym1 = Symbol('key');
const sym2 = Symbol('key');
console.log(sym1 === sym2); // false,Symbol 是唯一的
const obj = {
[sym1]: 'value' // 使用 Symbol 作为属性名
};
console.log(obj[sym1]); // value
与字符串属性的区别
- 字符串属性名可以被覆盖,而
Symbol
属性名是唯一的,不会冲突。
10. 迭代器(Iterators)和生成器(Generators)
概念
- 迭代器 :一种对象,具有
next()
方法,用于按顺序访问集合的元素。 - 生成器 :一种特殊的函数,使用
function*
声明,可以生成多个值。
原理
迭代器和生成器基于 协程 的机制,允许函数暂停和恢复执行。
用法
javascript
// 迭代器
const iterator = {
data: [1, 2, 3],
next() {
return {
value: this.data.shift() || undefined,
done: this.data.length === 0
};
}
};
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
// 生成器
function* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
const gen = generateNumbers();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
与普通函数的区别
- 普通函数只能返回一个值,而生成器可以返回多个值。
- 生成器的执行可以被暂停和恢复。
11. Promise
概念
Promise
是一种异步编程的解决方案,用于处理异步操作的结果。
原理
Promise
是一个对象,表示一个异步操作的最终状态(完成或失败)。它有三种状态:pending
、fulfilled
、rejected
。
用法
javascript
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Success!'); // 异步操作成功
}, 1000);
});
promise
.then(result => console.log(result)) // Success!
.catch(error => console.log(error)); // 处理错误
与回调函数的区别
- 回调函数容易导致"回调地狱",而
Promise
使用链式调用,代码更清晰。
12. Map 和 Set
概念
Map
:一种键值对的集合,键可以是任意类型的值。Set
:一种唯一值的集合,不允许重复值。
原理
Map
和 Set
是基于哈希表实现的,适合存储和查找大量数据。
用法
javascript
// Map
const map = new Map();
map.set('name', 'John');
map.set(1, 'One');
console.log(map.get('name')); // John
// Set
const set = new Set([1, 2, 3, 3]);
console.log(set); // Set { 1, 2, 3 }
与普通对象的区别
Map
的键可以是任意类型的值,而普通对象的键只能是字符串或Symbol
。Set
会自动去重,而普通数组不会。
13. Proxy
概念
Proxy
是一种对象,用于定义基本操作的自定义行为(如属性查找、赋值、函数调用等)。
原理
Proxy
可以"拦截"对目标对象的操作,并在操作执行前后添加自定义逻辑。
用法
javascript
const target = {
name: 'John'
};
const handler = {
get(obj, prop) {
return prop in obj ? obj[prop] : 'Unknown';
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // John
console.log(proxy.age); // Unknown
与直接访问对象的区别
- 直接访问对象无法拦截操作,而
Proxy
可以实现自定义行为。
14. Reflect
概念
Reflect
是一个内置对象,提供与 Proxy
相同的操作,用于简化代码和统一行为。
原理
Reflect
方法通常与 Proxy
配对使用,例如 Reflect.get()
、Reflect.set()
等。
用法
javascript
const obj = { name: 'John' };
console.log(Reflect.get(obj, 'name')); // John
Reflect.set(obj, 'age', 25);
console.log(obj.age); // 25
与直接调用的区别
Reflect
提供了一种更标准化的方式来操作对象。
15. 字符串新增方法
includes()
:检查字符串是否包含指定子串。startsWith()
:检查字符串是否以指定子串开头。endsWith()
:检查字符串是否以指定子串结尾。
用法
javascript
const str = 'Hello, world!';
console.log(str.includes('world')); // true
console.log(str.startsWith('Hello')); // true
console.log(str.endsWith('!')); // true
与 indexOf
的区别
includes()
、startsWith()
、endsWith()
更语义化,直接返回布尔值。
16. 数组新增方法
Array.from()
:将类数组对象或可迭代对象转换为数组。Array.of()
:创建包含任意数量元素的数组。find()
:查找符合条件的第一个元素。findIndex()
:查找符合条件的第一个元素的索引。fill()
:填充数组的指定范围。
用法
javascript
const arr = Array.from('abc');
console.log(arr); // ['a', 'b', 'c']
const numbers = Array.of(1, 2, 3);
console.log(numbers); // [1, 2, 3]
const firstEven = [1, 3, 5, 6, 7].find(num => num % 2 === 0);
console.log(firstEven); // 6
Array.prototype.fill()
是 ES6 引入的一个数组方法,用于用一个固定值填充数组的指定范围。它非常方便,特别是在需要初始化或重置数组时使用。
javascript
array.fill(value, start, end)
value
:用来填充数组的值。start
(可选):开始填充的索引(包含该索引),默认为0
。end
(可选):结束填充的索引(不包含该索引),默认为数组的长度。
使用示例
javascript
// 1. 填充整个数组
const arr = [1, 2, 3, 4, 5];
arr.fill(0); // 用 0 填充整个数组
console.log(arr); // [0, 0, 0, 0, 0]
// 2. 填充指定范围
const arr = [1, 2, 3, 4, 5];
arr.fill(0, 1, 3); // 从索引 1 到 3(不包含 3)用 0 填充
console.log(arr); // [1, 0, 0, 4, 5]
// 3. 从某个索引开始填充
const arr = [1, 2, 3, 4, 5];
arr.fill(0, 2); // 从索引 2 开始用 0 填充
console.log(arr); // [1, 2, 0, 0, 0]
// 4. 填充稀疏数组 如果数组是稀疏的(有空位),fill() 会跳过这些空位,只填充有值的位置:
const arr = [1, , , 4]; // 数组有空的索引
arr.fill(0, 1, 3); // 从索引 1 到 3 用 0 填充
console.log(arr); // [1, 0, 0, 4]
//注意事项
//修改原数组:fill() 会直接修改原数组,而不是返回一个新数组。
//负数索引:可以使用负数索引,表示从数组末尾开始计算:
<JAVASCRIPT>
const arr = [1, 2, 3, 4, 5];
arr.fill(0, -3, -1); // 从倒数第 3 个到倒数第 1 个用 0 填充
console.log(arr); // [1, 2, 0, 0, 5]
//超出范围的索引:
//如果 start 或 end 超出数组的索引范围,fill() 会自动调整到数组的边界:
const arr = [1, 2, 3, 4, 5];
arr.fill(0, 10); // start 超出范围,相当于不填充
console.log(arr); // [1, 2, 3, 4, 5]
实际应用场景
javascript
// 初始化数组:
const arr = new Array(5).fill(0); // 创建一个长度为 5 的数组,所有元素为 0
console.log(arr); // [0, 0, 0, 0, 0]
//重置数组的一部分:
const scores = [95, 80, 70, 60, 85];
scores.fill(0, 2, 4); // 将索引 2 和 3 的值重置为 0
console.log(scores); // [95, 80, 0, 0, 85]
// 生成固定值的数组:
const defaultValues = new Array(10).fill('default'); // 生成 10 个 'default'
console.log(defaultValues); // ['default', 'default', ..., 'default']
//与 map() 的区别
//fill() 是直接填充值,而 map() 是通过回调函数生成新值。
//示例:
const arr1 = new Array(5).fill(0); // [0, 0, 0, 0, 0]
const arr2 = new Array(5).fill().map((_, i) => i); // [0, 1, 2, 3, 4]