ES相关2.0
ES6 prox的作用
ES6 引入了 Proxy
(代理),它是一种元编程特性,允许你拦截并自定义对象的基本操作(如属性查找、赋值、枚举等)。Proxy
的作用是为对象创建一个代理层,通过这个代理层可以控制对目标对象的访问和操作。
以下是 Proxy
的主要作用和应用场景:
- 基本用法
Proxy
的基本语法是:
const proxy = new Proxy(target, handler);
target
:目标对象,即需要被代理的对象。handler
:一个对象,定义了拦截操作的“陷阱”(trap)函数。
示例:
const target = {
name: 'Alice',
age: 25
};
const handler = {
get(target, prop) {
if (prop === 'age') {
return target[prop] + ' years old';
}
return target[prop];
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Alice
console.log(proxy.age); // 25 years old
- 常用的陷阱函数
handler
对象中可以定义多种陷阱函数来拦截不同的操作:
陷阱函数 | 拦截的操作 |
---|---|
get(target, prop) | 读取属性时调用 |
set(target, prop, value) | 设置属性时调用 |
has(target, prop) | 使用in 操作符时调用 |
deleteProperty(target, prop) | 使用delete 操作符时调用 |
apply(target, thisArg, args) | 调用函数时调用(用于代理函数) |
construct(target, args) | 使用new 操作符时调用(用于代理构造函数) |
ownKeys(target) | 获取对象自身属性键时调用 |
示例:拦截属性读取和设置
const target = {
name: 'Alice',
age: 25
};
const handler = {
get(target, prop) {
console.log(`Reading property: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Setting property: ${prop} = ${value}`);
target[prop] = value;
return true; // 表示设置成功
}
};
const proxy = new Proxy(target, handler);
proxy.name; // 输出: Reading property: name
proxy.age = 30; // 输出: Setting property: age = 30
- 验证和过滤
Proxy
可以用于验证或过滤对目标对象的操作,例如:
- 验证属性值是否符合要求。
- 防止某些属性被修改或删除。
示例:验证属性值
const target = {
age: 25
};
const handler = {
set(target, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
proxy.age = 30; // 正常
proxy.age = '30'; // 抛出错误: Age must be a number
- 函数代理
Proxy
不仅可以代理对象,还可以代理函数。通过 apply
陷阱函数,可以拦截函数的调用。
示例:拦截函数调用
function sum(a, b) {
return a + b;
}
const handler = {
apply(target, thisArg, args) {
console.log(`Calling function with arguments: ${args}`);
return target(...args);
}
};
const proxy = new Proxy(sum, handler);
console.log(proxy(1, 2)); // 输出: Calling function with arguments: 1,2
// 输出: 3
- 构造函数代理
通过 construct
陷阱函数,可以拦截 new
操作符对构造函数的调用。
示例:拦截构造函数调用
class Person {
constructor(name) {
this.name = name;
}
}
const handler = {
construct(target, args) {
console.log(`Creating instance with arguments: ${args}`);
return new target(...args);
}
};
const ProxyPerson = new Proxy(Person, handler);
const alice = new ProxyPerson('Alice'); // 输出: Creating instance with arguments: Alice
console.log(alice.name); // Alice
- 隐藏私有属性
Proxy
可以用于隐藏对象的私有属性,使其无法被直接访问。
示例:隐藏私有属性
const target = {
name: 'Alice',
_password: '123456'
};
const handler = {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error('Access denied');
}
return target[prop];
},
set(target, prop, value) {
if (prop.startsWith('_')) {
throw new Error('Access denied');
}
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Alice
console.log(proxy._password); // 抛出错误: Access denied
- 应用场景
- 数据验证:拦截属性赋值操作,验证数据是否符合要求。
- 日志记录:记录对象的读写操作。
- 缓存:拦截函数调用,实现缓存机制。
- 隐藏私有属性:防止私有属性被直接访问。
- 观察者模式:监听对象的变化并触发回调。
示例:观察者模式
const target = {
name: 'Alice'
};
const observers = [];
const handler = {
set(target, prop, value) {
target[prop] = value;
observers.forEach(observer => observer(prop, value));
return true;
}
};
const proxy = new Proxy(target, handler);
observers.push((prop, value) => {
console.log(`Property ${prop} changed to ${value}`);
});
proxy.name = 'Bob'; // 输出: Property name changed to Bob
- 与
Reflect
结合使用
Reflect
是 ES6 引入的一个内置对象,提供了与 Proxy
陷阱函数一一对应的方法。通常可以将 Proxy
和 Reflect
结合使用,以简化代码。
示例:
const target = {
name: 'Alice'
};
const handler = {
get(target, prop) {
console.log(`Reading property: ${prop}`);
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.log(`Setting property: ${prop} = ${value}`);
return Reflect.set(target, prop, value);
}
};
const proxy = new Proxy(target, handler);
proxy.name; // 输出: Reading property: name
proxy.name = 'Bob'; // 输出: Setting property: name = Bob
总结
Proxy
是 ES6 引入的一种强大的元编程工具,允许你拦截并自定义对象的基本操作。它的主要作用包括数据验证、日志记录、缓存、隐藏私有属性等。通过 Proxy
,你可以更灵活地控制对象的行为,实现更高级的功能。
ES6 map的作用
ES6 引入了 Map
数据结构,它是一种键值对的集合,与传统的对象(Object
)相比,Map
的键可以是任意类型的值(包括对象),而不仅仅是字符串或 Symbol。Map
提供了更强大的功能和更直观的 API,适用于需要复杂键值对的场景。
以下是 Map
的主要作用和应用场景:
- 键的类型更灵活
Map
的键可以是任意类型的值,包括对象、函数、基本类型等,而 Object
的键只能是字符串或 Symbol。
示例:
const map = new Map();
const key1 = { name: 'Alice' };
const key2 = function() {};
const key3 = 'key3';
map.set(key1, 'value1');
map.set(key2, 'value2');
map.set(key3, 'value3');
console.log(map.get(key1)); // value1
console.log(map.get(key2)); // value2
console.log(map.get(key3)); // value3
- 保持插入顺序
Map
会严格保持键值对的插入顺序,而 Object
的键顺序是不确定的。
示例:
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
for (let [key, value] of map) {
console.log(key, value); // a 1, b 2, c 3
}
- 常用操作
Map
提供了一些常用的方法来操作键值对:
set(key, value)
:添加或更新键值对。get(key)
:获取指定键对应的值。has(key)
:检查Map
中是否包含某个键。delete(key)
:删除指定键的键值对。clear()
:清空Map
中的所有键值对。size
:获取Map
中键值对的数量。
示例:
const map = new Map();
map.set('name', 'Alice');
map.set('age', 25);
console.log(map.size); // 2
console.log(map.get('name')); // Alice
console.log(map.has('age')); // true
map.delete('age');
console.log(map.size); // 1
map.clear();
console.log(map.size); // 0
- 遍历 Map
Map
是可迭代的,可以使用 for...of
循环或 forEach
方法遍历其中的键值对。
示例:
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
// 使用 for...of 遍历
for (let [key, value] of map) {
console.log(key, value); // a 1, b 2, c 3
}
// 使用 forEach 遍历
map.forEach((value, key) => {
console.log(key, value); // a 1, b 2, c 3
});
- 与对象的区别
特性 | Map | Object |
---|---|---|
键的类型 | 任意类型(包括对象) | 字符串或 Symbol |
键的顺序 | 严格保持插入顺序 | 不确定 |
大小 | 通过size 属性获取 | 需要手动计算 |
性能 | 频繁增删键值对时性能更好 | 频繁增删键值对时性能较差 |
默认键 | 无默认键 | 有原型链上的默认键 |
- 应用场景
- 复杂键值对:当键需要是对象或其他复杂类型时,使用
Map
。 - 保持顺序:当需要严格保持键值对的插入顺序时,使用
Map
。 - 频繁增删:当需要频繁添加或删除键值对时,
Map
的性能更好。 - 数据缓存:可以用
Map
实现简单的缓存机制。
示例:数据缓存
const cache = new Map();
function getData(key) {
if (cache.has(key)) {
return cache.get(key);
}
const data = fetchDataFromServer(key); // 假设从服务器获取数据
cache.set(key, data);
return data;
}
- WeakMap
ES6 还引入了 WeakMap
,它与 Map
类似,但有以下区别:
WeakMap
的键必须是对象,不能是基本类型。WeakMap
中的键是弱引用的,不会阻止垃圾回收。WeakMap
不可遍历。
示例:
const weakMap = new WeakMap();
const obj = { name: 'Alice' };
weakMap.set(obj, 'value1');
console.log(weakMap.get(obj)); // value1
// 当 obj 被垃圾回收后,weakMap 中的键值对也会自动消失
- 与数组的转换
Map
可以轻松地与数组相互转换。
示例:
// 数组转 Map
const array = [['a', 1], ['b', 2], ['c', 3]];
const map = new Map(array);
console.log(map.get('a')); // 1
// Map 转数组
const newArray = [...map];
console.log(newArray); // [['a', 1], ['b', 2], ['c', 3]]
总结
Map
是 ES6 引入的一种强大的键值对集合,支持任意类型的键,并保持插入顺序。它比传统的 Object
更适合处理复杂键值对和频繁增删的场景。如果需要存储对象键并且不希望阻止垃圾回收,可以使用 WeakMap
。
ES6 set的作用
ES6 引入了 Set
数据结构,它是一种无序且唯一的值的集合。Set
的主要作用是存储不重复的值,并提供高效的方法来检查某个值是否存在。
以下是 Set
的主要作用和应用场景:
- 存储唯一值
Set
的核心特性是它的值总是唯一的,重复的值会被自动忽略。这使得 Set
非常适合用于去重。
示例:
const numbers = [1, 2, 3, 4, 2, 3, 5];
const uniqueNumbers = new Set(numbers);
console.log(uniqueNumbers); // Set { 1, 2, 3, 4, 5 }
- 高效的值检查
Set
内部使用哈希表实现,因此检查某个值是否存在于 Set
中的时间复杂度是 O(1),比数组的 includes()
方法(时间复杂度为 O(n))更高效。
示例:
const fruits = new Set(['apple', 'banana', 'orange']);
console.log(fruits.has('banana')); // true
console.log(fruits.has('grape')); // false
- 常用操作
Set
提供了一些常用的方法来操作集合:
add(value)
:向Set
中添加一个值。delete(value)
:从Set
中删除一个值。has(value)
:检查Set
中是否包含某个值。clear()
:清空Set
中的所有值。size
:获取Set
中值的数量。
示例:
const colors = new Set();
colors.add('red');
colors.add('green');
colors.add('blue');
colors.add('red'); // 重复值会被忽略
console.log(colors.size); // 3
console.log(colors.has('green')); // true
colors.delete('blue');
console.log(colors); // Set { 'red', 'green' }
colors.clear();
console.log(colors.size); // 0
- 遍历 Set
Set
是可迭代的,可以使用 for...of
循环或 forEach
方法遍历其中的值。
示例:
const letters = new Set(['a', 'b', 'c']);
// 使用 for...of 遍历
for (let letter of letters) {
console.log(letter); // a, b, c
}
// 使用 forEach 遍历
letters.forEach((value) => {
console.log(value); // a, b, c
});
- 与数组的转换
Set
可以轻松地与数组相互转换,常用于数组去重。
示例:
// 数组去重
const array = [1, 2, 3, 3, 4, 5, 5];
const uniqueArray = [...new Set(array)]; // 将 Set 转换为数组
console.log(uniqueArray); // [1, 2, 3, 4, 5]
- 存储非原始类型的值
Set
可以存储任何类型的值,包括对象、函数等。需要注意的是,Set
是通过严格相等(===
)来判断值是否重复的,因此两个看起来相同的对象会被认为是不同的值。
示例:
const obj1 = { name: 'Alice' };
const obj2 = { name: 'Alice' };
const set = new Set();
set.add(obj1);
set.add(obj2);
console.log(set.size); // 2,因为 obj1 和 obj2 是不同的对象
- 应用场景
- 数组去重:快速去除数组中的重复值。
- 值的存在性检查:高效地检查某个值是否存在于集合中。
- 数学集合运算:如并集、交集、差集等。
- 存储唯一数据:如用户 ID、订单号等需要唯一性的数据。
示例:集合运算
// 并集
const set1 = new Set([1, 2, 3]);
const set2 = new Set([3, 4, 5]);
const union = new Set([...set1, ...set2]);
console.log([...union]); // [1, 2, 3, 4, 5]
// 交集
const intersection = new Set([...set1].filter(x => set2.has(x)));
console.log([...intersection]); // [3]
// 差集
const difference = new Set([...set1].filter(x => !set2.has(x)));
console.log([...difference]); // [1, 2]
- WeakSet
ES6 还引入了 WeakSet
,它与 Set
类似,但有以下区别:
WeakSet
只能存储对象,不能存储原始值。WeakSet
中的对象是弱引用的,不会阻止垃圾回收。WeakSet
不可遍历。
示例:
const weakSet = new WeakSet();
const obj = { name: 'Alice' };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
// 当 obj 被垃圾回收后,weakSet 中的引用也会自动消失
总结
Set
是 ES6 引入的一种高效的数据结构,用于存储唯一的值。它的主要作用包括去重、高效的值检查、集合运算等。与数组相比,Set
在处理唯一性和存在性检查时更加高效。如果需要存储对象并且不希望阻止垃圾回收,可以使用 WeakSet
。
ES6 symbol的作用
ES6 引入了 Symbol
类型,它是一种新的原始数据类型,用于创建唯一的、不可变的值。Symbol
的主要作用是作为对象属性的唯一标识符,避免属性名冲突,同时也可以用于定义一些特殊的行为(如自定义迭代器)。
以下是 Symbol
的主要作用和应用场景:
- 创建唯一的属性键
Symbol
的主要用途是作为对象属性的键,确保属性名不会与其他属性名冲突。因为每个 Symbol
值都是唯一的,即使它们的描述相同。
示例:
const id = Symbol('id'); // 创建一个 Symbol,描述为 'id'
const user = {
name: 'Alice',
[id]: 123 // 使用 Symbol 作为属性键
};
console.log(user[id]); // 123
console.log(Object.keys(user)); // ['name'],Symbol 属性不会被枚举
- 避免属性名冲突
在大型项目中,可能会在不同的模块中使用相同的字符串作为属性名,这可能导致属性名冲突。使用 Symbol
可以避免这种问题,因为每个 Symbol
都是唯一的。
示例:
const module1 = {
id: 1
};
const module2 = {
id: 2
};
// 使用 Symbol 避免冲突
const id = Symbol('id');
module1[id] = 1;
module2[id] = 2;
console.log(module1[id]); // 1
console.log(module2[id]); // 2
- 定义对象的私有属性
Symbol
可以用作对象的私有属性,因为 Symbol
属性不会被常规方法(如 for...in
、Object.keys()
、Object.getOwnPropertyNames()
)枚举到。
示例:
const age = Symbol('age');
const person = {
name: 'Alice',
[age]: 25
};
console.log(person[age]); // 25
for (let key in person) {
console.log(key); // 只输出 'name'
}
- 内置 Symbol 值
ES6 提供了一些内置的 Symbol
值,用于定义对象的特殊行为。这些内置 Symbol
值被称为“Well-Known Symbols”,例如:
Symbol.iterator
:定义对象的默认迭代器。Symbol.toStringTag
:定义对象的toString
行为。Symbol.hasInstance
:定义instanceof
的行为。
示例:
// 自定义对象的迭代行为
const myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};
for (let value of myIterable) {
console.log(value); // 1, 2, 3
}
- 全局 Symbol 注册表
ES6 提供了 Symbol.for()
和 Symbol.keyFor()
方法,用于创建和访问全局 Symbol
注册表中的 Symbol
值。
Symbol.for(key)
:如果全局注册表中存在与key
对应的Symbol
,则返回它;否则创建一个新的Symbol
并注册。Symbol.keyFor(sym)
:返回全局注册表中与sym
对应的key
。
示例:
const sym1 = Symbol.for('mySymbol'); // 创建或获取全局 Symbol
const sym2 = Symbol.for('mySymbol');
console.log(sym1 === sym2); // true
console.log(Symbol.keyFor(sym1)); // 'mySymbol'
- Symbol 的特性
- 唯一性:每个
Symbol
值都是唯一的,即使它们的描述相同。 - 不可变性:
Symbol
值是不可变的,不能被修改。 - 不可枚举性:
Symbol
属性不会被for...in
、Object.keys()
等枚举到。 - 类型检测:
typeof Symbol()
返回'symbol'
。
示例:
const sym1 = Symbol('foo');
const sym2 = Symbol('foo');
console.log(sym1 === sym2); // false,Symbol 是唯一的
console.log(typeof sym1); // 'symbol'
- Symbol 的应用场景
- 避免属性名冲突:在大型项目或库中,使用
Symbol
作为属性键可以避免命名冲突。 - 定义私有属性:
Symbol
属性不会被常规方法枚举,适合用于定义私有属性。 - 自定义对象行为:通过内置
Symbol
值(如Symbol.iterator
)可以定义对象的特殊行为。 - 元编程:
Symbol
可以用于定义对象的元数据或特殊行为。
总结
Symbol
是 ES6 引入的一种新的原始数据类型,主要用于创建唯一的属性键,避免属性名冲突,同时也可以用于定义对象的特殊行为(如迭代器)。它的唯一性和不可枚举性使其非常适合用于定义私有属性或元数据。
ES6对function函数类型做的常用升级优化
ES6(ECMAScript 2015)对JavaScript中的function
函数类型进行了多项优化和升级,主要包括以下几个方面:
- 箭头函数(Arrow Functions)
箭头函数是ES6引入的一种新的函数语法,简化了函数的定义,并且自动绑定了this
。
特点:
- 语法简洁:
(参数) => { 函数体 }
- 自动绑定
this
:箭头函数没有自己的this
,它会捕获所在上下文的this
值。 - 不能作为构造函数使用,不能使用
new
调用。 - 没有
arguments
对象,但可以使用剩余参数(...args
)。
示例:
// ES5
var add = function(a, b) {
return a + b;
};
// ES6
const add = (a, b) => a + b;
- 默认参数(Default Parameters)
ES6允许为函数的参数设置默认值,简化了参数处理的逻辑。
示例:
// ES5
function greet(name) {
name = name || 'Guest';
console.log('Hello, ' + name);
}
// ES6
function greet(name = 'Guest') {
console.log(`Hello, ${name}`);
}
- 剩余参数(Rest Parameters)
剩余参数允许将不定数量的参数表示为一个数组,替代了ES5中的arguments
对象。
示例:
// ES5
function sum() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function(a, b) {
return a + b;
}, 0);
}
// ES6
function sum(...args) {
return args.reduce((a, b) => a + b, 0);
}
- 扩展运算符(Spread Operator)
扩展运算符可以将数组或对象展开为多个参数,常用于函数调用时。
示例:
const numbers = [1, 2, 3];
// ES5
Math.max.apply(null, numbers);
// ES6
Math.max(...numbers);
- 函数参数的解构赋值(Destructuring Assignment)
ES6允许在函数参数中使用解构赋值,直接从对象或数组中提取值。
示例:
// 对象解构
function greet({ name, age }) {
console.log(`Hello, ${name}. You are ${age} years old.`);
}
const person = { name: 'Alice', age: 25 };
greet(person);
// 数组解构
function sum([a, b, c]) {
return a + b + c;
}
const nums = [1, 2, 3];
console.log(sum(nums)); // 6
- 函数名称属性(Function Name Property)
ES6为函数添加了name
属性,可以获取函数的名称。
示例:
function foo() {}
console.log(foo.name); // "foo"
const bar = function() {};
console.log(bar.name); // "bar"
const baz = () => {};
console.log(baz.name); // "baz"
- 尾调用优化(Tail Call Optimization)
ES6引入了尾调用优化,允许在函数的最后一步调用另一个函数时,不增加新的栈帧,从而避免栈溢出。
示例:
function factorial(n, acc = 1) {
if (n <= 1) return acc;
return factorial(n - 1, n * acc); // 尾调用优化
}
console.log(factorial(5)); // 120
- 块级作用域函数声明(Block-Scoped Function Declarations)
在ES6中,函数声明在块级作用域内是块级作用域的,而不是像ES5中那样提升到函数或全局作用域的顶部。
示例:
{
function foo() {
console.log('Inside block');
}
foo(); // "Inside block"
}
foo(); // ReferenceError: foo is not defined
总结
ES6对函数类型的优化使得代码更加简洁、易读,并且提供了更多的功能,如箭头函数、默认参数、剩余参数、解构赋值等。这些改进不仅提升了开发效率,还增强了代码的可维护性和可读性。
ECMAScript 7、8、9新特性
ECMAScript 每年都会发布新版本,ES7(2016)、ES8(2017)和 ES9(2018)分别引入了一些新特性。以下是这些版本的主要更新内容:
ECMAScript 2016(ES7)
ES7 是一个较小的更新,只引入了两个新特性。
Array.prototype.includes()
判断数组是否包含某个值,返回布尔值。
示例:
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 输出: true
console.log(arr.includes(4)); // 输出: false
- 指数运算符 (
**
)
用于计算幂运算。
示例:
console.log(2 ** 3); // 输出: 8
console.log(3 ** 2); // 输出: 9
ECMAScript 2017(ES8)
ES8 引入了更多实用的特性,主要集中在异步编程和对象操作上。
async/await
简化异步操作,使异步代码看起来像同步代码。
示例:
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
Object.values()
返回对象所有可枚举属性值的数组。
示例:
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.values(obj)); // 输出: [1, 2, 3]
Object.entries()
返回对象所有可枚举属性的键值对数组。
示例:
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.entries(obj)); // 输出: [['a', 1], ['b', 2], ['c', 3]]
Object.getOwnPropertyDescriptors()
返回对象所有自身属性的描述符(包括 value
、writable
、enumerable
和 configurable
)。
示例:
const obj = { a: 1 };
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// 输出:
// {
// a: { value: 1, writable: true, enumerable: true, configurable: true }
// }
- 字符串填充方法:
padStart()
和padEnd()
在字符串的开头或结尾填充指定字符,直到字符串达到指定长度。
示例:
console.log('5'.padStart(3, '0')); // 输出: "005"
console.log('5'.padEnd(3, '*')); // 输出: "5**"
- 函数参数列表和调用中的尾逗号
允许在函数参数列表和调用时使用尾逗号。
示例:
function foo(a, b, c,) {
console.log(a, b, c);
}
foo(1, 2, 3,);
ECMAScript 2018(ES9)
ES9 进一步增强了异步编程和对象操作的能力。
- 异步迭代器 (
for-await-of
)
用于遍历异步可迭代对象(如异步生成器)。
示例:
async function* asyncGenerator() {
yield 1;
yield 2;
yield 3;
}
(async () => {
for await (const value of asyncGenerator()) {
console.log(value);
}
})();
// 输出: 1, 2, 3
Promise.prototype.finally()
无论 Promise
是成功还是失败,都会执行的回调函数。
示例:
fetch('https://api.example.com/data')
.then(response => response.json())
.catch(error => console.error(error))
.finally(() => console.log('请求完成'));
- Rest/Spread 属性
允许在对象中使用 ...
进行剩余参数和展开操作。
示例:
// Rest 属性
const { a, ...rest } = { a: 1, b: 2, c: 3 };
console.log(rest); // 输出: { b: 2, c: 3 }
// Spread 属性
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log(obj2); // 输出: { a: 1, b: 2, c: 3 }
- 正则表达式的增强
- 命名捕获组:使用
(?<name>...)
语法为捕获组命名。 - 反向断言:支持
(?<=...)
和(?<!...)
语法。 s
标志:使.
匹配任意字符,包括换行符。
示例:
// 命名捕获组
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = regex.exec('2023-10-05');
console.log(match.groups.year); // 输出: 2023
console.log(match.groups.month); // 输出: 10
// s 标志
const regex2 = /a.b/s;
console.log(regex2.test('a\nb')); // 输出: true
总结
- ES7(2016):
Array.prototype.includes()
和指数运算符 (**
)。 - ES8(2017):
async/await
、Object.values()
、Object.entries()
、padStart()
、padEnd()
等。 - ES9(2018):异步迭代器、
Promise.prototype.finally()
、Rest/Spread 属性、正则表达式增强等。
这些新特性使得 JavaScript 更加现代化和强大,尤其是在异步编程和对象操作方面。
ES6对象方法扩展
ES6(ECMAScript 2015)为对象(Object
)引入了许多新的方法和语法糖,使得对象的操作更加简洁和强大。以下是 ES6 中对象方法的主要扩展:
- 属性简写
在对象字面量中,如果属性名和变量名相同,可以省略属性值。
示例:
const name = 'Alice';
const age = 25;
// ES5
const obj1 = {
name: name,
age: age
};
// ES6
const obj2 = { name, age };
console.log(obj2); // 输出: { name: 'Alice', age: 25 }
- 方法简写
在对象字面量中,定义方法时可以省略 function
关键字。
示例:
// ES5
const obj1 = {
sayHello: function() {
console.log('Hello');
}
};
// ES6
const obj2 = {
sayHello() {
console.log('Hello');
}
};
obj2.sayHello(); // 输出: Hello
- 计算属性名
在对象字面量中,可以使用表达式作为属性名,用 []
包裹。
示例:
const prop = 'name';
const obj = {
[prop]: 'Alice',
['age']: 25
};
console.log(obj); // 输出: { name: 'Alice', age: 25 }
Object.is()
用于比较两个值是否严格相等,与 ===
类似,但处理了一些特殊情况(如 NaN
和 +0/-0
)。
示例:
console.log(Object.is(NaN, NaN)); // 输出: true
console.log(Object.is(+0, -0)); // 输出: false
console.log(Object.is(42, 42)); // 输出: true
Object.assign()
用于将一个或多个源对象的属性复制到目标对象,返回目标对象。
示例:
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
const result = Object.assign(target, source1, source2);
console.log(result); // 输出: { a: 1, b: 2, c: 3 }
Object.setPrototypeOf()
设置一个对象的原型(即 __proto__
属性)。
示例:
const obj = {};
const proto = { foo: 'bar' };
Object.setPrototypeOf(obj, proto);
console.log(obj.foo); // 输出: bar
Object.getPrototypeOf()
获取一个对象的原型。
示例:
const obj = {};
const proto = { foo: 'bar' };
Object.setPrototypeOf(obj, proto);
console.log(Object.getPrototypeOf(obj) === proto); // 输出: true
Object.keys()
、Object.values()
和Object.entries()
Object.keys()
:返回对象自身可枚举属性的键名数组。Object.values()
:返回对象自身可枚举属性的值数组。Object.entries()
:返回对象自身可枚举属性的键值对数组。
示例:
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.keys(obj)); // 输出: ['a', 'b', 'c']
console.log(Object.values(obj)); // 输出: [1, 2, 3]
console.log(Object.entries(obj)); // 输出: [['a', 1], ['b', 2], ['c', 3]]
Object.fromEntries()
将键值对数组转换为对象(与 Object.entries()
相反)。
示例:
const entries = [['a', 1], ['b', 2], ['c', 3]];
const obj = Object.fromEntries(entries);
console.log(obj); // 输出: { a: 1, b: 2, c: 3 }
super
关键字
在对象方法中,super
用于调用父对象的方法。
示例:
const parent = {
sayHello() {
console.log('Hello from parent');
}
};
const child = {
sayHello() {
super.sayHello();
console.log('Hello from child');
}
};
Object.setPrototypeOf(child, parent);
child.sayHello();
// 输出:
// Hello from parent
// Hello from child
Object.getOwnPropertyDescriptors()
返回对象所有自身属性的描述符(包括 value
、writable
、enumerable
和 configurable
)。
示例:
const obj = { a: 1 };
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// 输出:
// {
// a: { value: 1, writable: true, enumerable: true, configurable: true }
// }
Symbol
作为属性名
ES6 引入了 Symbol
类型,可以用作对象的唯一属性名。
示例:
const key = Symbol('key');
const obj = {
[key]: 'value'
};
console.log(obj[key]); // 输出: value
总结
ES6 为对象引入了许多实用的扩展和方法,包括:
- 属性简写和方法简写。
- 计算属性名。
- 新的
Object
方法:is()
、assign()
、setPrototypeOf()
、getPrototypeOf()
等。 Object.keys()
、Object.values()
和Object.entries()
。super
关键字。Symbol
作为属性名。
这些扩展使得对象的操作更加简洁和强大,是现代 JavaScript 开发中不可或缺的工具。
ES6的数值扩展
ES6(ECMAScript 2015)为数值(Number)类型引入了许多新的扩展和方法,使得数值操作更加方便和强大。以下是 ES6 中数值的主要扩展:
- 二进制和八进制表示法
ES6 引入了新的字面量表示法,用于直接表示二进制和八进制数值。
- 二进制:前缀
0b
或0B
。 - 八进制:前缀
0o
或0O
。
示例:
const binary = 0b1010; // 二进制,表示十进制的 10
const octal = 0o12; // 八进制,表示十进制的 10
console.log(binary); // 输出: 10
console.log(octal); // 输出: 10
Number.isFinite()
判断一个值是否为有限的数值(即不是 Infinity
、-Infinity
或 NaN
)。
示例:
console.log(Number.isFinite(42)); // 输出: true
console.log(Number.isFinite(Infinity)); // 输出: false
console.log(Number.isFinite(NaN)); // 输出: false
Number.isNaN()
判断一个值是否为 NaN
。与全局的 isNaN()
不同,Number.isNaN()
不会进行类型转换。
示例:
console.log(Number.isNaN(NaN)); // 输出: true
console.log(Number.isNaN('NaN')); // 输出: false
console.log(isNaN('NaN')); // 输出: true(全局 isNaN 会进行类型转换)
Number.parseInt()
和Number.parseFloat()
将全局方法 parseInt()
和 parseFloat()
移植到 Number
对象上,行为与全局方法一致。
示例:
console.log(Number.parseInt('42px')); // 输出: 42
console.log(Number.parseFloat('3.14abc')); // 输出: 3.14
Number.isInteger()
判断一个值是否为整数。
示例:
console.log(Number.isInteger(42)); // 输出: true
console.log(Number.isInteger(42.0)); // 输出: true
console.log(Number.isInteger(42.1)); // 输出: false
Number.EPSILON
表示 JavaScript 中数值的最小精度,用于浮点数比较。
示例:
console.log(Number.EPSILON); // 输出: 2.220446049250313e-16
function isEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(isEqual(0.1 + 0.2, 0.3)); // 输出: true
Number.isSafeInteger()
判断一个值是否为安全整数(即在 Number.MIN_SAFE_INTEGER
和 Number.MAX_SAFE_INTEGER
之间)。
示例:
console.log(Number.isSafeInteger(42)); // 输出: true
console.log(Number.isSafeInteger(Math.pow(2, 53))); // 输出: false
Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
表示 JavaScript 中安全整数的最大值和最小值。
示例:
console.log(Number.MAX_SAFE_INTEGER); // 输出: 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // 输出: -9007199254740991
Math
对象的扩展
ES6 为 Math
对象新增了一些方法,用于数值计算:
(1) Math.trunc()
去除小数部分,返回整数部分。
示例:
console.log(Math.trunc(3.14)); // 输出: 3
console.log(Math.trunc(-3.14)); // 输出: -3
(2) Math.sign()
判断一个数的符号,返回 1
(正数)、-1
(负数)、0
(零)、-0
(负零)或 NaN
(非数值)。
示例:
console.log(Math.sign(42)); // 输出: 1
console.log(Math.sign(-42)); // 输出: -1
console.log(Math.sign(0)); // 输出: 0
console.log(Math.sign(-0)); // 输出: -0
console.log(Math.sign('foo')); // 输出: NaN
(3) Math.cbrt()
计算一个数的立方根。
示例:
console.log(Math.cbrt(27)); // 输出: 3
(4) Math.hypot()
计算所有参数的平方和的平方根(即欧几里得距离)。
示例:
console.log(Math.hypot(3, 4)); // 输出: 5
(5) Math.log2()
和 Math.log10()
分别计算以 2 和 10 为底的对数。
示例:
console.log(Math.log2(8)); // 输出: 3
console.log(Math.log10(100)); // 输出: 2
- 指数运算符 (
**
)
ES6 引入了指数运算符 (**
),用于计算幂运算。
示例:
console.log(2 ** 3); // 输出: 8
console.log(3 ** 2); // 输出: 9
总结
ES6 为数值类型引入了许多实用的扩展和方法,包括:
- 二进制和八进制表示法。
- 新的
Number
方法:isFinite()
、isNaN()
、isInteger()
、isSafeInteger()
等。 - 新的
Math
方法:trunc()
、sign()
、cbrt()
、hypot()
等。 - 指数运算符 (
**
)。
这些扩展使得数值操作更加方便和精确,是现代 JavaScript 开发中不可或缺的工具。
ES6与ECMAScript2015的关系
ES6 和 ECMAScript 2015 是同一个东西,只是名称不同。以下是详细解释:
- ES6 是什么?
- ES6 是 ECMAScript 6 的简称,指的是 ECMAScript 的第 6 个版本。
- 它是 JavaScript 语言的一次重大更新,引入了许多新特性,使 JavaScript 更现代化、更强大。
- ECMAScript 2015 是什么?
- ECMAScript 2015 是 ECMAScript 第 6 个版本的正式名称。
- 从 ES6 开始,Ecma International 组织决定以发布年份命名 ECMAScript 版本,因此 ES6 也被称为 ECMAScript 2015(简称 ES2015)。
- 为什么有两个名字?
- 历史原因:
- 在 ES6 之前,ECMAScript 的版本号是以数字命名的(如 ES3、ES5)。
- 从 ES6 开始,Ecma International 决定改用年份命名(如 ES2015、ES2016),以便更清晰地反映版本的发布时间。
- 习惯问题:
- 由于 ES6 是一次重大更新,开发者习惯性地称其为 ES6,而不是 ES2015。
- 随着时间的推移,ES2015 的名称也逐渐被接受。
- ES6 和 ECMAScript 2015 的关系
- ES6 和 ECMAScript 2015 是同一个版本,只是命名方式不同。
- ES6 是传统的版本号命名。
- ECMAScript 2015 是基于发布年份的命名。
- ES6(ECMAScript 2015)的主要特性
ES6 是一次重大更新,引入了许多新特性,包括但不限于:
let
和const
:块级作用域变量声明。- 箭头函数:
() => {}
。 - 模板字符串:
`Hello, ${name}`
。 - 解构赋值:
const { a, b } = obj;
。 - 默认参数:
function foo(a = 1) {}
。 - 扩展运算符:
...
。 - 类:
class
关键字。 - 模块化:
import
和export
。 - Promise:用于异步编程。
- Symbol:新的原始数据类型。
- 迭代器和生成器:
for...of
和function*
。
- ES6 之后的版本命名
从 ES6 开始,ECMAScript 的版本改为按年份命名:
- ES2015(ES6):2015 年发布。
- ES2016(ES7):2016 年发布。
- ES2017(ES8):2017 年发布。
- ES2018(ES9):2018 年发布。
- ES2019(ES10):2019 年发布。
- ES2020(ES11):2020 年发布。
- ES2021(ES12):2021 年发布。
- ES2022(ES13):2022 年发布。
总结
- ES6 和 ECMAScript 2015 是同一个版本,只是命名方式不同。
- ES6 是 JavaScript 的一次重大更新,引入了许多现代语言特性。
- 从 ES6 开始,ECMAScript 的版本改为按年份命名(如 ES2015、ES2016 等)。
在实际开发中,ES6 和 ECMAScript 2015 可以互换使用,但更推荐使用 ES2015 以符合官方命名规范。
ECMAScript和JavaScript的关系
ECMAScript 和 JavaScript 是紧密相关的概念,但它们并不是完全相同的。以下是它们之间的关系和区别:
- JavaScript 是什么?
- JavaScript 是一种脚本语言,主要用于网页开发,使网页具有动态交互功能。
- 它最初由 Netscape 公司的 Brendan Eich 在 1995 年开发,最初命名为 Mocha,后来改为 LiveScript,最终命名为 JavaScript。
- JavaScript 不仅可以在浏览器中运行,还可以通过 Node.js 在服务器端运行。
- ECMAScript 是什么?
- ECMAScript 是 JavaScript 的标准化规范,由 Ecma International 组织制定。
- JavaScript 是 ECMAScript 规范的一种实现。
- ECMAScript 定义了语言的语法、类型、语句、关键字、操作符、对象等核心特性。
- ECMAScript 和 JavaScript 的关系
- ECMAScript 是标准,JavaScript 是实现:
- ECMAScript 是 JavaScript 的语言规范,规定了 JavaScript 应该如何实现。
- JavaScript 是 ECMAScript 规范的一种具体实现。
- 其他实现:
- 除了 JavaScript,还有其他语言也遵循 ECMAScript 规范,例如 ActionScript(用于 Adobe Flash)。
- 版本演进:
- ECMAScript 的版本号代表了 JavaScript 的核心特性更新。例如,ES5(2009 年发布)、ES6(2015 年发布,也称为 ES2015)、ES7(2016 年发布,也称为 ES2016)等。
- JavaScript 引擎(如 V8、SpiderMonkey)会根据 ECMAScript 规范实现这些特性。
- JavaScript 的组成部分
JavaScript 不仅仅包含 ECMAScript,还包括以下部分:
- ECMAScript:语言的核心(语法、类型、操作符等)。
- DOM(文档对象模型):用于操作 HTML 和 XML 文档的 API。
- BOM(浏览器对象模型):用于操作浏览器窗口的 API(如
window
对象)。
- ECMAScript 的版本历史
ECMAScript 的版本更新推动了 JavaScript 的发展:
- ES3(1999):引入了正则表达式、
try/catch
等特性。 - ES5(2009):引入了严格模式 (
"use strict"
)、JSON
支持、数组方法(如map
、filter
)等。 - ES6(2015,也称为 ES2015):重大更新,引入了
let
、const
、箭头函数、类、模块、解构赋值等。 - ES2016(ES7):引入了
Array.prototype.includes()
和指数运算符 (**
)。 - ES2017(ES8):引入了
async/await
、Object.values()
、Object.entries()
等。 - ES2018(ES9):引入了异步迭代、
Promise.prototype.finally()
、Rest/Spread 属性等。 - ES2019(ES10):引入了
Array.prototype.flat()
、Array.prototype.flatMap()
、Object.fromEntries()
等。 - ES2020(ES11):引入了可选链操作符 (
?.
)、空值合并操作符 (??
)、BigInt
等。 - ES2021(ES12):引入了
String.prototype.replaceAll()
、Promise.any()
、逻辑赋值操作符 (&&=
,||=
,??=
) 等。
总结
- ECMAScript 是 JavaScript 的标准化规范,定义了语言的核心特性。
- JavaScript 是 ECMAScript 的一种实现,同时还包括 DOM 和 BOM 等浏览器相关的 API。
- ECMAScript 的版本更新推动了 JavaScript 的发展,使其功能更加强大和现代化。
简单来说,ECMAScript 是规范,JavaScript 是实践。
ES7对象新增哪些扩展
ES7(ECMAScript 2016)相对于 ES6 来说是一个较小的更新,但它仍然为对象引入了一些实用的扩展。以下是 ES7 中对象的主要新增特性:
Object.values()
返回一个对象的所有可枚举属性值的数组。
语法:
Object.values(obj)
示例:
const obj = { a: 1, b: 2, c: 3 };
const values = Object.values(obj);
console.log(values); // 输出: [1, 2, 3]
Object.entries()
返回一个对象的所有可枚举属性键值对的数组,每个键值对是一个 [key, value]
数组。
语法:
Object.entries(obj)
示例:
const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);
console.log(entries); // 输出: [['a', 1], ['b', 2], ['c', 3]]
Object.getOwnPropertyDescriptors()
返回一个对象的所有自身属性的描述符(包括 value
、writable
、enumerable
和 configurable
)。
语法:
Object.getOwnPropertyDescriptors(obj)
示例:
const obj = { a: 1 };
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// 输出:
// {
// a: { value: 1, writable: true, enumerable: true, configurable: true }
// }
- 指数运算符 (
**
)
虽然不是直接针对对象的扩展,但 ES7 引入了指数运算符 (**
),可以更方便地进行幂运算。
语法:
x ** y
示例:
console.log(2 ** 3); // 输出: 8
console.log(3 ** 2); // 输出: 9
Array.prototype.includes()
虽然不是对象的扩展,但 ES7 引入了 Array.prototype.includes()
方法,用于判断数组是否包含某个值。
语法:
array.includes(value)
示例:
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 输出: true
console.log(arr.includes(4)); // 输出: false
总结
ES7 中对象的主要扩展包括:
Object.values()
:获取对象的所有值。Object.entries()
:获取对象的所有键值对。Object.getOwnPropertyDescriptors()
:获取对象属性的描述符。- 指数运算符 (
**
):方便进行幂运算。 Array.prototype.includes()
:判断数组是否包含某个值。
这些扩展使得对象的操作更加方便和强大,尤其是在处理对象的键值对和属性描述符时非常有用。
ES6中数组新增了哪些扩展
ES6(ECMAScript 2015)为数组引入了许多新的扩展和方法,使得数组的操作更加方便和强大。以下是 ES6 中数组的主要扩展:
- 扩展运算符(Spread Operator)
扩展运算符 (...
) 可以将数组展开为单独的元素,常用于函数调用、数组拼接等场景。
示例:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // 合并数组
console.log(combined); // 输出: [1, 2, 3, 4, 5, 6]
function sum(a, b, c) {
return a + b + c;
}
console.log(sum(...arr1)); // 输出: 6(展开数组作为参数)
Array.from()
将类数组对象(如 arguments
、NodeList
)或可迭代对象(如 Set
、Map
)转换为真正的数组。
示例:
const nodeList = document.querySelectorAll('div');
const arr = Array.from(nodeList); // 将 NodeList 转换为数组
console.log(arr);
Array.of()
创建一个包含任意数量参数的新数组,解决了 new Array()
的行为不一致问题。
示例:
const arr1 = Array.of(1, 2, 3); // 创建数组 [1, 2, 3]
const arr2 = new Array(3); // 创建长度为 3 的空数组
console.log(arr1); // 输出: [1, 2, 3]
console.log(arr2); // 输出: [empty × 3]
- 数组实例的新方法
ES6 为数组实例添加了许多实用的方法:
(1) find()
查找数组中第一个满足条件的元素,返回该元素;如果没有找到,返回 undefined
。
示例:
const arr = [1, 2, 3, 4];
const result = arr.find(item => item > 2);
console.log(result); // 输出: 3
(2) findIndex()
查找数组中第一个满足条件的元素的索引,返回该索引;如果没有找到,返回 -1
。
示例:
const arr = [1, 2, 3, 4];
const index = arr.findIndex(item => item > 2);
console.log(index); // 输出: 2
(3) fill()
用指定的值填充数组。
示例:
const arr = [1, 2, 3];
arr.fill(0); // 填充为 [0, 0, 0]
console.log(arr);
(4) includes()
判断数组是否包含某个值,返回布尔值。
示例:
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 输出: true
console.log(arr.includes(4)); // 输出: false
(5) flat()
将嵌套数组“拉平”,返回一个新数组。默认拉平一层,可以通过参数指定拉平的层数。
示例:
const arr = [1, [2, [3]]];
console.log(arr.flat()); // 输出: [1, 2, [3]]
console.log(arr.flat(2)); // 输出: [1, 2, 3]
(6) flatMap()
先对数组中的每个元素执行映射操作,然后将结果拉平一层。
示例:
const arr = [1, 2, 3];
const result = arr.flatMap(x => [x * 2]);
console.log(result); // 输出: [2, 4, 6]
for...of
循环
for...of
是遍历数组的新方式,直接获取数组的值(而不是索引)。
示例:
const arr = [1, 2, 3];
for (const value of arr) {
console.log(value); // 依次输出: 1, 2, 3
}
entries()
、keys()
和values()
这些方法返回数组的迭代器对象,分别用于获取数组的键值对、键或值。
示例:
const arr = ['a', 'b', 'c'];
// entries() 返回键值对
for (const [index, value] of arr.entries()) {
console.log(index, value); // 输出: 0 'a', 1 'b', 2 'c'
}
// keys() 返回键
for (const key of arr.keys()) {
console.log(key); // 输出: 0, 1, 2
}
// values() 返回值
for (const value of arr.values()) {
console.log(value); // 输出: 'a', 'b', 'c'
}
copyWithin()
将数组的一部分复制到同一数组的另一位置,并返回修改后的数组。
示例:
const arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3); // 从索引 3 开始复制到索引 0
console.log(arr); // 输出: [4, 5, 3, 4, 5]
Array.prototype[Symbol.iterator]
数组默认实现了迭代器协议,可以通过 Symbol.iterator
获取数组的迭代器。
示例:
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // 输出: { value: 1, done: false }
总结
ES6 为数组提供了许多强大的扩展和方法,包括:
- 扩展运算符 (
...
) Array.from()
和Array.of()
- 新方法:
find()
、findIndex()
、fill()
、includes()
、flat()
、flatMap()
、copyWithin()
- 新的遍历方式:
for...of
、entries()
、keys()
、values()
这些扩展使得数组的操作更加简洁和高效,是现代 JavaScript 开发中不可或缺的工具。
ES中var、let、const区别
在 ECMAScript (ES) 中,var
、let
和 const
都用于声明变量,但它们的行为和作用域有显著区别。以下是它们的详细对比:
var
- 作用域:函数作用域(function-scoped)。
- 在函数内部声明的变量只能在函数内部访问。
- 如果在函数外部声明,则成为全局变量。
- 变量提升:
var
声明的变量会被提升到其作用域的顶部。- 变量可以在声明之前访问,但值为
undefined
。
- 变量可以在声明之前访问,但值为
- 重复声明:允许在同一作用域内重复声明。
- 示例:javascript
function example() { if (true) { var x = 10; } console.log(x); // 输出: 10(因为 var 是函数作用域) } example();
let
- 作用域:块级作用域(block-scoped)。
- 在
{}
块(如if
、for
、while
等)中声明的变量只能在该块内访问。
- 在
- 变量提升:
let
声明的变量也会被提升,但在声明之前访问会抛出ReferenceError
(暂时性死区)。 - 重复声明:不允许在同一作用域内重复声明。
- 示例:javascript
function example() { if (true) { let y = 20; console.log(y); // 输出: 20 } console.log(y); // 报错: y is not defined(因为 let 是块级作用域) } example();
const
- 作用域:块级作用域(block-scoped),与
let
相同。 - 变量提升:与
let
相同,存在暂时性死区。 - 重复声明:不允许在同一作用域内重复声明。
- 赋值特性:
const
声明的变量必须初始化,且不能重新赋值。- 如果变量是对象或数组,其内部属性或元素可以修改,但不能重新赋值整个变量。
- 示例:javascript
const z = 30; z = 40; // 报错: Assignment to constant variable const obj = { name: "Alice" }; obj.name = "Bob"; // 允许修改属性 console.log(obj); // 输出: { name: "Bob" } obj = { name: "Charlie" }; // 报错: Assignment to constant variable
对比总结
特性 | var | let | const |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
变量提升 | 是(值为undefined ) | 是(暂时性死区) | 是(暂时性死区) |
重复声明 | 允许 | 不允许 | 不允许 |
重新赋值 | 允许 | 允许 | 不允许 |
初始化要求 | 不需要 | 不需要 | 必须初始化 |
使用建议
- 优先使用
const
:- 如果变量的值不会改变,使用
const
可以避免意外修改。
- 如果变量的值不会改变,使用
- 需要重新赋值时使用
let
:- 如果变量的值需要改变,使用
let
。
- 如果变量的值需要改变,使用
- 避免使用
var
:var
的函数作用域和变量提升容易导致 bug,现代 JavaScript 中推荐使用let
和const
。
示例对比
// var
var a = 1;
var a = 2; // 允许重复声明
console.log(a); // 输出: 2
// let
let b = 1;
let b = 2; // 报错: Identifier 'b' has already been declared
// const
const c = 1;
c = 2; // 报错: Assignment to constant variable
通过理解这些区别,可以更好地选择适合的变量声明方式,避免潜在的问题。
ES中padStart()、padEnd()
在 ECMAScript (ES) 中,padStart()
和 padEnd()
是字符串对象的方法,用于在字符串的开头或结尾填充指定的字符,直到字符串达到指定的长度。
padStart()
padStart()
方法会在字符串的开头填充指定的字符,直到字符串达到指定的长度。
语法:
str.padStart(targetLength [, padString])
targetLength
:目标长度。如果当前字符串长度小于该值,则在开头填充字符,直到字符串长度达到该值。padString
(可选):用于填充的字符。默认为空格 ()。
示例:
const str = '5';
console.log(str.padStart(3, '0')); // 输出: "005"
console.log(str.padStart(5, '*')); // 输出: "****5"
padEnd()
padEnd()
方法会在字符串的结尾填充指定的字符,直到字符串达到指定的长度。
语法:
str.padEnd(targetLength [, padString])
targetLength
:目标长度。如果当前字符串长度小于该值,则在结尾填充字符,直到字符串长度达到该值。padString
(可选):用于填充的字符。默认为空格 ()。
示例:
const str = '5';
console.log(str.padEnd(3, '0')); // 输出: "500"
console.log(str.padEnd(5, '*')); // 输出: "5****"
总结
padStart()
用于在字符串开头填充字符。padEnd()
用于在字符串结尾填充字符。- 如果填充字符 (
padString
) 未提供,默认使用空格填充。 - 如果目标长度 (
targetLength
) 小于或等于字符串的当前长度,则返回原字符串。
这两个方法在处理字符串对齐、格式化输出等场景中非常有用。