跳转到内容

ES相关2.0

ES6 prox的作用

ES6 引入了 Proxy(代理),它是一种元编程特性,允许你拦截并自定义对象的基本操作(如属性查找、赋值、枚举等)。Proxy 的作用是为对象创建一个代理层,通过这个代理层可以控制对目标对象的访问和操作。

以下是 Proxy 的主要作用和应用场景:

  1. 基本用法

Proxy 的基本语法是:

javascript
const proxy = new Proxy(target, handler);
  • target:目标对象,即需要被代理的对象。
  • handler:一个对象,定义了拦截操作的“陷阱”(trap)函数。

示例:

javascript
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
  1. 常用的陷阱函数

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)获取对象自身属性键时调用

示例:拦截属性读取和设置

javascript
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
  1. 验证和过滤

Proxy 可以用于验证或过滤对目标对象的操作,例如:

  • 验证属性值是否符合要求。
  • 防止某些属性被修改或删除。

示例:验证属性值

javascript
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
  1. 函数代理

Proxy 不仅可以代理对象,还可以代理函数。通过 apply 陷阱函数,可以拦截函数的调用。

示例:拦截函数调用

javascript
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
  1. 构造函数代理

通过 construct 陷阱函数,可以拦截 new 操作符对构造函数的调用。

示例:拦截构造函数调用

javascript
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
  1. 隐藏私有属性

Proxy 可以用于隐藏对象的私有属性,使其无法被直接访问。

示例:隐藏私有属性

javascript
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
  1. 应用场景
  • 数据验证:拦截属性赋值操作,验证数据是否符合要求。
  • 日志记录:记录对象的读写操作。
  • 缓存:拦截函数调用,实现缓存机制。
  • 隐藏私有属性:防止私有属性被直接访问。
  • 观察者模式:监听对象的变化并触发回调。

示例:观察者模式

javascript
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
  1. Reflect 结合使用

Reflect 是 ES6 引入的一个内置对象,提供了与 Proxy 陷阱函数一一对应的方法。通常可以将 ProxyReflect 结合使用,以简化代码。

示例:

javascript
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 的主要作用和应用场景:

  1. 键的类型更灵活

Map 的键可以是任意类型的值,包括对象、函数、基本类型等,而 Object 的键只能是字符串或 Symbol。

示例:

javascript
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
  1. 保持插入顺序

Map 会严格保持键值对的插入顺序,而 Object 的键顺序是不确定的。

示例:

javascript
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
}
  1. 常用操作

Map 提供了一些常用的方法来操作键值对:

  • set(key, value):添加或更新键值对。
  • get(key):获取指定键对应的值。
  • has(key):检查 Map 中是否包含某个键。
  • delete(key):删除指定键的键值对。
  • clear():清空 Map 中的所有键值对。
  • size:获取 Map 中键值对的数量。

示例:

javascript
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
  1. 遍历 Map

Map 是可迭代的,可以使用 for...of 循环或 forEach 方法遍历其中的键值对。

示例:

javascript
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
});
  1. 与对象的区别
特性MapObject
键的类型任意类型(包括对象)字符串或 Symbol
键的顺序严格保持插入顺序不确定
大小通过size 属性获取需要手动计算
性能频繁增删键值对时性能更好频繁增删键值对时性能较差
默认键无默认键有原型链上的默认键
  1. 应用场景
  • 复杂键值对:当键需要是对象或其他复杂类型时,使用 Map
  • 保持顺序:当需要严格保持键值对的插入顺序时,使用 Map
  • 频繁增删:当需要频繁添加或删除键值对时,Map 的性能更好。
  • 数据缓存:可以用 Map 实现简单的缓存机制。

示例:数据缓存

javascript
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;
}
  1. WeakMap

ES6 还引入了 WeakMap,它与 Map 类似,但有以下区别:

  • WeakMap 的键必须是对象,不能是基本类型。
  • WeakMap 中的键是弱引用的,不会阻止垃圾回收。
  • WeakMap 不可遍历。

示例:

javascript
const weakMap = new WeakMap();
const obj = { name: 'Alice' };

weakMap.set(obj, 'value1');
console.log(weakMap.get(obj)); // value1

// 当 obj 被垃圾回收后,weakMap 中的键值对也会自动消失
  1. 与数组的转换

Map 可以轻松地与数组相互转换。

示例:

javascript
// 数组转 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 的主要作用和应用场景:

  1. 存储唯一值

Set 的核心特性是它的值总是唯一的,重复的值会被自动忽略。这使得 Set 非常适合用于去重。

示例:

javascript
const numbers = [1, 2, 3, 4, 2, 3, 5];
const uniqueNumbers = new Set(numbers);

console.log(uniqueNumbers); // Set { 1, 2, 3, 4, 5 }
  1. 高效的值检查

Set 内部使用哈希表实现,因此检查某个值是否存在于 Set 中的时间复杂度是 O(1),比数组的 includes() 方法(时间复杂度为 O(n))更高效。

示例:

javascript
const fruits = new Set(['apple', 'banana', 'orange']);

console.log(fruits.has('banana')); // true
console.log(fruits.has('grape')); // false
  1. 常用操作

Set 提供了一些常用的方法来操作集合:

  • add(value):向 Set 中添加一个值。
  • delete(value):从 Set 中删除一个值。
  • has(value):检查 Set 中是否包含某个值。
  • clear():清空 Set 中的所有值。
  • size:获取 Set 中值的数量。

示例:

javascript
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
  1. 遍历 Set

Set 是可迭代的,可以使用 for...of 循环或 forEach 方法遍历其中的值。

示例:

javascript
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
});
  1. 与数组的转换

Set 可以轻松地与数组相互转换,常用于数组去重。

示例:

javascript
// 数组去重
const array = [1, 2, 3, 3, 4, 5, 5];
const uniqueArray = [...new Set(array)]; // 将 Set 转换为数组

console.log(uniqueArray); // [1, 2, 3, 4, 5]
  1. 存储非原始类型的值

Set 可以存储任何类型的值,包括对象、函数等。需要注意的是,Set 是通过严格相等(===)来判断值是否重复的,因此两个看起来相同的对象会被认为是不同的值。

示例:

javascript
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 是不同的对象
  1. 应用场景
  • 数组去重:快速去除数组中的重复值。
  • 值的存在性检查:高效地检查某个值是否存在于集合中。
  • 数学集合运算:如并集、交集、差集等。
  • 存储唯一数据:如用户 ID、订单号等需要唯一性的数据。

示例:集合运算

javascript
// 并集
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]
  1. WeakSet

ES6 还引入了 WeakSet,它与 Set 类似,但有以下区别:

  • WeakSet 只能存储对象,不能存储原始值。
  • WeakSet 中的对象是弱引用的,不会阻止垃圾回收。
  • WeakSet 不可遍历。

示例:

javascript
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 的主要作用和应用场景:

  1. 创建唯一的属性键

Symbol 的主要用途是作为对象属性的键,确保属性名不会与其他属性名冲突。因为每个 Symbol 值都是唯一的,即使它们的描述相同。

示例:

javascript
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 属性不会被枚举
  1. 避免属性名冲突

在大型项目中,可能会在不同的模块中使用相同的字符串作为属性名,这可能导致属性名冲突。使用 Symbol 可以避免这种问题,因为每个 Symbol 都是唯一的。

示例:

javascript
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
  1. 定义对象的私有属性

Symbol 可以用作对象的私有属性,因为 Symbol 属性不会被常规方法(如 for...inObject.keys()Object.getOwnPropertyNames())枚举到。

示例:

javascript
const age = Symbol('age');

const person = {
    name: 'Alice',
    [age]: 25
};

console.log(person[age]); // 25

for (let key in person) {
    console.log(key); // 只输出 'name'
}
  1. 内置 Symbol 值

ES6 提供了一些内置的 Symbol 值,用于定义对象的特殊行为。这些内置 Symbol 值被称为“Well-Known Symbols”,例如:

  • Symbol.iterator:定义对象的默认迭代器。
  • Symbol.toStringTag:定义对象的 toString 行为。
  • Symbol.hasInstance:定义 instanceof 的行为。

示例:

javascript
// 自定义对象的迭代行为
const myIterable = {
    [Symbol.iterator]: function* () {
        yield 1;
        yield 2;
        yield 3;
    }
};

for (let value of myIterable) {
    console.log(value); // 1, 2, 3
}
  1. 全局 Symbol 注册表

ES6 提供了 Symbol.for()Symbol.keyFor() 方法,用于创建和访问全局 Symbol 注册表中的 Symbol 值。

  • Symbol.for(key):如果全局注册表中存在与 key 对应的 Symbol,则返回它;否则创建一个新的 Symbol 并注册。
  • Symbol.keyFor(sym):返回全局注册表中与 sym 对应的 key

示例:

javascript
const sym1 = Symbol.for('mySymbol'); // 创建或获取全局 Symbol
const sym2 = Symbol.for('mySymbol');

console.log(sym1 === sym2); // true

console.log(Symbol.keyFor(sym1)); // 'mySymbol'
  1. Symbol 的特性
  • 唯一性:每个 Symbol 值都是唯一的,即使它们的描述相同。
  • 不可变性Symbol 值是不可变的,不能被修改。
  • 不可枚举性Symbol 属性不会被 for...inObject.keys() 等枚举到。
  • 类型检测typeof Symbol() 返回 'symbol'

示例:

javascript
const sym1 = Symbol('foo');
const sym2 = Symbol('foo');

console.log(sym1 === sym2); // false,Symbol 是唯一的
console.log(typeof sym1); // 'symbol'
  1. Symbol 的应用场景
  • 避免属性名冲突:在大型项目或库中,使用 Symbol 作为属性键可以避免命名冲突。
  • 定义私有属性Symbol 属性不会被常规方法枚举,适合用于定义私有属性。
  • 自定义对象行为:通过内置 Symbol 值(如 Symbol.iterator)可以定义对象的特殊行为。
  • 元编程Symbol 可以用于定义对象的元数据或特殊行为。

总结

Symbol 是 ES6 引入的一种新的原始数据类型,主要用于创建唯一的属性键,避免属性名冲突,同时也可以用于定义对象的特殊行为(如迭代器)。它的唯一性和不可枚举性使其非常适合用于定义私有属性或元数据。

ES6对function函数类型做的常用升级优化

ES6(ECMAScript 2015)对JavaScript中的function函数类型进行了多项优化和升级,主要包括以下几个方面:

  1. 箭头函数(Arrow Functions)

箭头函数是ES6引入的一种新的函数语法,简化了函数的定义,并且自动绑定了this

特点:

  • 语法简洁:(参数) => { 函数体 }
  • 自动绑定this:箭头函数没有自己的this,它会捕获所在上下文的this值。
  • 不能作为构造函数使用,不能使用new调用。
  • 没有arguments对象,但可以使用剩余参数(...args)。

示例:

javascript
// ES5
var add = function(a, b) {
    return a + b;
};

// ES6
const add = (a, b) => a + b;
  1. 默认参数(Default Parameters)

ES6允许为函数的参数设置默认值,简化了参数处理的逻辑。

示例:

javascript
// ES5
function greet(name) {
    name = name || 'Guest';
    console.log('Hello, ' + name);
}

// ES6
function greet(name = 'Guest') {
    console.log(`Hello, ${name}`);
}
  1. 剩余参数(Rest Parameters)

剩余参数允许将不定数量的参数表示为一个数组,替代了ES5中的arguments对象。

示例:

javascript
// 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);
}
  1. 扩展运算符(Spread Operator)

扩展运算符可以将数组或对象展开为多个参数,常用于函数调用时。

示例:

javascript
const numbers = [1, 2, 3];

// ES5
Math.max.apply(null, numbers);

// ES6
Math.max(...numbers);
  1. 函数参数的解构赋值(Destructuring Assignment)

ES6允许在函数参数中使用解构赋值,直接从对象或数组中提取值。

示例:

javascript
// 对象解构
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
  1. 函数名称属性(Function Name Property)

ES6为函数添加了name属性,可以获取函数的名称。

示例:

javascript
function foo() {}
console.log(foo.name); // "foo"

const bar = function() {};
console.log(bar.name); // "bar"

const baz = () => {};
console.log(baz.name); // "baz"
  1. 尾调用优化(Tail Call Optimization)

ES6引入了尾调用优化,允许在函数的最后一步调用另一个函数时,不增加新的栈帧,从而避免栈溢出。

示例:

javascript
function factorial(n, acc = 1) {
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc); // 尾调用优化
}

console.log(factorial(5)); // 120
  1. 块级作用域函数声明(Block-Scoped Function Declarations)

在ES6中,函数声明在块级作用域内是块级作用域的,而不是像ES5中那样提升到函数或全局作用域的顶部。

示例:

javascript
{
    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 是一个较小的更新,只引入了两个新特性。

  1. Array.prototype.includes()

判断数组是否包含某个值,返回布尔值。

示例:

javascript
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 输出: true
console.log(arr.includes(4)); // 输出: false
  1. 指数运算符 (**)

用于计算幂运算。

示例:

javascript
console.log(2 ** 3); // 输出: 8
console.log(3 ** 2); // 输出: 9

ECMAScript 2017(ES8)

ES8 引入了更多实用的特性,主要集中在异步编程和对象操作上。

  1. async/await

简化异步操作,使异步代码看起来像同步代码。

示例:

javascript
async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
}
  1. Object.values()

返回对象所有可枚举属性值的数组。

示例:

javascript
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.values(obj)); // 输出: [1, 2, 3]
  1. Object.entries()

返回对象所有可枚举属性的键值对数组。

示例:

javascript
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.entries(obj)); // 输出: [['a', 1], ['b', 2], ['c', 3]]
  1. Object.getOwnPropertyDescriptors()

返回对象所有自身属性的描述符(包括 valuewritableenumerableconfigurable)。

示例:

javascript
const obj = { a: 1 };
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// 输出:
// {
//   a: { value: 1, writable: true, enumerable: true, configurable: true }
// }
  1. 字符串填充方法:padStart()padEnd()

在字符串的开头或结尾填充指定字符,直到字符串达到指定长度。

示例:

javascript
console.log('5'.padStart(3, '0')); // 输出: "005"
console.log('5'.padEnd(3, '*'));   // 输出: "5**"
  1. 函数参数列表和调用中的尾逗号

允许在函数参数列表和调用时使用尾逗号。

示例:

javascript
function foo(a, b, c,) {
    console.log(a, b, c);
}
foo(1, 2, 3,);

ECMAScript 2018(ES9)

ES9 进一步增强了异步编程和对象操作的能力。

  1. 异步迭代器 (for-await-of)

用于遍历异步可迭代对象(如异步生成器)。

示例:

javascript
async function* asyncGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

(async () => {
    for await (const value of asyncGenerator()) {
        console.log(value);
    }
})();
// 输出: 1, 2, 3
  1. Promise.prototype.finally()

无论 Promise 是成功还是失败,都会执行的回调函数。

示例:

javascript
fetch('https://api.example.com/data')
    .then(response => response.json())
    .catch(error => console.error(error))
    .finally(() => console.log('请求完成'));
  1. Rest/Spread 属性

允许在对象中使用 ... 进行剩余参数和展开操作。

示例:

javascript
// 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 }
  1. 正则表达式的增强
  • 命名捕获组:使用 (?<name>...) 语法为捕获组命名。
  • 反向断言:支持 (?<=...)(?<!...) 语法。
  • s 标志:使 . 匹配任意字符,包括换行符。

示例:

javascript
// 命名捕获组
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/awaitObject.values()Object.entries()padStart()padEnd() 等。
  • ES9(2018):异步迭代器、Promise.prototype.finally()、Rest/Spread 属性、正则表达式增强等。

这些新特性使得 JavaScript 更加现代化和强大,尤其是在异步编程和对象操作方面。

ES6对象方法扩展

ES6(ECMAScript 2015)为对象(Object)引入了许多新的方法和语法糖,使得对象的操作更加简洁和强大。以下是 ES6 中对象方法的主要扩展:

  1. 属性简写

在对象字面量中,如果属性名和变量名相同,可以省略属性值。

示例:

javascript
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 }
  1. 方法简写

在对象字面量中,定义方法时可以省略 function 关键字。

示例:

javascript
// ES5
const obj1 = {
    sayHello: function() {
        console.log('Hello');
    }
};

// ES6
const obj2 = {
    sayHello() {
        console.log('Hello');
    }
};

obj2.sayHello(); // 输出: Hello
  1. 计算属性名

在对象字面量中,可以使用表达式作为属性名,用 [] 包裹。

示例:

javascript
const prop = 'name';

const obj = {
    [prop]: 'Alice',
    ['age']: 25
};

console.log(obj); // 输出: { name: 'Alice', age: 25 }
  1. Object.is()

用于比较两个值是否严格相等,与 === 类似,但处理了一些特殊情况(如 NaN+0/-0)。

示例:

javascript
console.log(Object.is(NaN, NaN)); // 输出: true
console.log(Object.is(+0, -0));   // 输出: false
console.log(Object.is(42, 42));   // 输出: true
  1. Object.assign()

用于将一个或多个源对象的属性复制到目标对象,返回目标对象。

示例:

javascript
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 }
  1. Object.setPrototypeOf()

设置一个对象的原型(即 __proto__ 属性)。

示例:

javascript
const obj = {};
const proto = { foo: 'bar' };

Object.setPrototypeOf(obj, proto);
console.log(obj.foo); // 输出: bar
  1. Object.getPrototypeOf()

获取一个对象的原型。

示例:

javascript
const obj = {};
const proto = { foo: 'bar' };

Object.setPrototypeOf(obj, proto);
console.log(Object.getPrototypeOf(obj) === proto); // 输出: true
  1. Object.keys()Object.values()Object.entries()
  • Object.keys():返回对象自身可枚举属性的键名数组。
  • Object.values():返回对象自身可枚举属性的值数组。
  • Object.entries():返回对象自身可枚举属性的键值对数组。

示例:

javascript
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]]
  1. Object.fromEntries()

将键值对数组转换为对象(与 Object.entries() 相反)。

示例:

javascript
const entries = [['a', 1], ['b', 2], ['c', 3]];
const obj = Object.fromEntries(entries);

console.log(obj); // 输出: { a: 1, b: 2, c: 3 }
  1. super 关键字

在对象方法中,super 用于调用父对象的方法。

示例:

javascript
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
  1. Object.getOwnPropertyDescriptors()

返回对象所有自身属性的描述符(包括 valuewritableenumerableconfigurable)。

示例:

javascript
const obj = { a: 1 };
const descriptors = Object.getOwnPropertyDescriptors(obj);

console.log(descriptors);
// 输出:
// {
//   a: { value: 1, writable: true, enumerable: true, configurable: true }
// }
  1. Symbol 作为属性名

ES6 引入了 Symbol 类型,可以用作对象的唯一属性名。

示例:

javascript
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 中数值的主要扩展:

  1. 二进制和八进制表示法

ES6 引入了新的字面量表示法,用于直接表示二进制和八进制数值。

  • 二进制:前缀 0b0B
  • 八进制:前缀 0o0O

示例:

javascript
const binary = 0b1010; // 二进制,表示十进制的 10
const octal = 0o12;    // 八进制,表示十进制的 10
console.log(binary);   // 输出: 10
console.log(octal);    // 输出: 10
  1. Number.isFinite()

判断一个值是否为有限的数值(即不是 Infinity-InfinityNaN)。

示例:

javascript
console.log(Number.isFinite(42));      // 输出: true
console.log(Number.isFinite(Infinity)); // 输出: false
console.log(Number.isFinite(NaN));     // 输出: false
  1. Number.isNaN()

判断一个值是否为 NaN。与全局的 isNaN() 不同,Number.isNaN() 不会进行类型转换。

示例:

javascript
console.log(Number.isNaN(NaN));      // 输出: true
console.log(Number.isNaN('NaN'));    // 输出: false
console.log(isNaN('NaN'));           // 输出: true(全局 isNaN 会进行类型转换)
  1. Number.parseInt()Number.parseFloat()

将全局方法 parseInt()parseFloat() 移植到 Number 对象上,行为与全局方法一致。

示例:

javascript
console.log(Number.parseInt('42px')); // 输出: 42
console.log(Number.parseFloat('3.14abc')); // 输出: 3.14
  1. Number.isInteger()

判断一个值是否为整数。

示例:

javascript
console.log(Number.isInteger(42));    // 输出: true
console.log(Number.isInteger(42.0));  // 输出: true
console.log(Number.isInteger(42.1));  // 输出: false
  1. Number.EPSILON

表示 JavaScript 中数值的最小精度,用于浮点数比较。

示例:

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
  1. Number.isSafeInteger()

判断一个值是否为安全整数(即在 Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGER 之间)。

示例:

javascript
console.log(Number.isSafeInteger(42)); // 输出: true
console.log(Number.isSafeInteger(Math.pow(2, 53))); // 输出: false
  1. Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER

表示 JavaScript 中安全整数的最大值和最小值。

示例:

javascript
console.log(Number.MAX_SAFE_INTEGER); // 输出: 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // 输出: -9007199254740991
  1. Math 对象的扩展

ES6 为 Math 对象新增了一些方法,用于数值计算:

(1) Math.trunc()

去除小数部分,返回整数部分。

示例:

javascript
console.log(Math.trunc(3.14)); // 输出: 3
console.log(Math.trunc(-3.14)); // 输出: -3

(2) Math.sign()

判断一个数的符号,返回 1(正数)、-1(负数)、0(零)、-0(负零)或 NaN(非数值)。

示例:

javascript
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()

计算一个数的立方根。

示例:

javascript
console.log(Math.cbrt(27)); // 输出: 3

(4) Math.hypot()

计算所有参数的平方和的平方根(即欧几里得距离)。

示例:

javascript
console.log(Math.hypot(3, 4)); // 输出: 5

(5) Math.log2()Math.log10()

分别计算以 2 和 10 为底的对数。

示例:

javascript
console.log(Math.log2(8));  // 输出: 3
console.log(Math.log10(100)); // 输出: 2
  1. 指数运算符 (**)

ES6 引入了指数运算符 (**),用于计算幂运算。

示例:

javascript
console.log(2 ** 3); // 输出: 8
console.log(3 ** 2); // 输出: 9

总结

ES6 为数值类型引入了许多实用的扩展和方法,包括:

  • 二进制和八进制表示法。
  • 新的 Number 方法:isFinite()isNaN()isInteger()isSafeInteger() 等。
  • 新的 Math 方法:trunc()sign()cbrt()hypot() 等。
  • 指数运算符 (**)。

这些扩展使得数值操作更加方便和精确,是现代 JavaScript 开发中不可或缺的工具。

ES6与ECMAScript2015的关系

ES6ECMAScript 2015 是同一个东西,只是名称不同。以下是详细解释:

  1. ES6 是什么?
  • ES6ECMAScript 6 的简称,指的是 ECMAScript 的第 6 个版本。
  • 它是 JavaScript 语言的一次重大更新,引入了许多新特性,使 JavaScript 更现代化、更强大。
  1. ECMAScript 2015 是什么?
  • ECMAScript 2015 是 ECMAScript 第 6 个版本的正式名称。
  • 从 ES6 开始,Ecma International 组织决定以发布年份命名 ECMAScript 版本,因此 ES6 也被称为 ECMAScript 2015(简称 ES2015)。
  1. 为什么有两个名字?
  • 历史原因
    • 在 ES6 之前,ECMAScript 的版本号是以数字命名的(如 ES3、ES5)。
    • 从 ES6 开始,Ecma International 决定改用年份命名(如 ES2015、ES2016),以便更清晰地反映版本的发布时间。
  • 习惯问题
    • 由于 ES6 是一次重大更新,开发者习惯性地称其为 ES6,而不是 ES2015。
    • 随着时间的推移,ES2015 的名称也逐渐被接受。
  1. ES6 和 ECMAScript 2015 的关系
  • ES6 和 ECMAScript 2015 是同一个版本,只是命名方式不同。
  • ES6 是传统的版本号命名。
  • ECMAScript 2015 是基于发布年份的命名。
  1. ES6(ECMAScript 2015)的主要特性

ES6 是一次重大更新,引入了许多新特性,包括但不限于:

  • letconst:块级作用域变量声明。
  • 箭头函数() => {}
  • 模板字符串`Hello, ${name}`
  • 解构赋值const { a, b } = obj;
  • 默认参数function foo(a = 1) {}
  • 扩展运算符...
  • class 关键字。
  • 模块化importexport
  • Promise:用于异步编程。
  • Symbol:新的原始数据类型。
  • 迭代器和生成器for...offunction*
  1. 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 年发布。

总结

  • ES6ECMAScript 2015 是同一个版本,只是命名方式不同。
  • ES6 是 JavaScript 的一次重大更新,引入了许多现代语言特性。
  • 从 ES6 开始,ECMAScript 的版本改为按年份命名(如 ES2015、ES2016 等)。

在实际开发中,ES6ECMAScript 2015 可以互换使用,但更推荐使用 ES2015 以符合官方命名规范。

ECMAScript和JavaScript的关系

ECMAScriptJavaScript 是紧密相关的概念,但它们并不是完全相同的。以下是它们之间的关系和区别:

  1. JavaScript 是什么?
  • JavaScript 是一种脚本语言,主要用于网页开发,使网页具有动态交互功能。
  • 它最初由 Netscape 公司的 Brendan Eich 在 1995 年开发,最初命名为 Mocha,后来改为 LiveScript,最终命名为 JavaScript
  • JavaScript 不仅可以在浏览器中运行,还可以通过 Node.js 在服务器端运行。
  1. ECMAScript 是什么?
  • ECMAScript 是 JavaScript 的标准化规范,由 Ecma International 组织制定。
  • JavaScript 是 ECMAScript 规范的一种实现。
  • ECMAScript 定义了语言的语法、类型、语句、关键字、操作符、对象等核心特性。
  1. 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 规范实现这些特性。
  1. JavaScript 的组成部分

JavaScript 不仅仅包含 ECMAScript,还包括以下部分:

  • ECMAScript:语言的核心(语法、类型、操作符等)。
  • DOM(文档对象模型):用于操作 HTML 和 XML 文档的 API。
  • BOM(浏览器对象模型):用于操作浏览器窗口的 API(如 window 对象)。
  1. ECMAScript 的版本历史

ECMAScript 的版本更新推动了 JavaScript 的发展:

  • ES3(1999):引入了正则表达式、try/catch 等特性。
  • ES5(2009):引入了严格模式 ("use strict")、JSON 支持、数组方法(如 mapfilter)等。
  • ES6(2015,也称为 ES2015):重大更新,引入了 letconst、箭头函数、类、模块、解构赋值等。
  • ES2016(ES7):引入了 Array.prototype.includes() 和指数运算符 (**)。
  • ES2017(ES8):引入了 async/awaitObject.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 中对象的主要新增特性:

  1. Object.values()

返回一个对象的所有可枚举属性值的数组。

语法:

javascript
Object.values(obj)

示例:

javascript
const obj = { a: 1, b: 2, c: 3 };
const values = Object.values(obj);
console.log(values); // 输出: [1, 2, 3]
  1. Object.entries()

返回一个对象的所有可枚举属性键值对的数组,每个键值对是一个 [key, value] 数组。

语法:

javascript
Object.entries(obj)

示例:

javascript
const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);
console.log(entries); // 输出: [['a', 1], ['b', 2], ['c', 3]]
  1. Object.getOwnPropertyDescriptors()

返回一个对象的所有自身属性的描述符(包括 valuewritableenumerableconfigurable)。

语法:

javascript
Object.getOwnPropertyDescriptors(obj)

示例:

javascript
const obj = { a: 1 };
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// 输出:
// {
//   a: { value: 1, writable: true, enumerable: true, configurable: true }
// }
  1. 指数运算符 (**)

虽然不是直接针对对象的扩展,但 ES7 引入了指数运算符 (**),可以更方便地进行幂运算。

语法:

javascript
x ** y

示例:

javascript
console.log(2 ** 3); // 输出: 8
console.log(3 ** 2); // 输出: 9
  1. Array.prototype.includes()

虽然不是对象的扩展,但 ES7 引入了 Array.prototype.includes() 方法,用于判断数组是否包含某个值。

语法:

javascript
array.includes(value)

示例:

javascript
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 输出: true
console.log(arr.includes(4)); // 输出: false

总结

ES7 中对象的主要扩展包括:

  1. Object.values():获取对象的所有值。
  2. Object.entries():获取对象的所有键值对。
  3. Object.getOwnPropertyDescriptors():获取对象属性的描述符。
  4. 指数运算符 (**):方便进行幂运算。
  5. Array.prototype.includes():判断数组是否包含某个值。

这些扩展使得对象的操作更加方便和强大,尤其是在处理对象的键值对和属性描述符时非常有用。

ES6中数组新增了哪些扩展

ES6(ECMAScript 2015)为数组引入了许多新的扩展和方法,使得数组的操作更加方便和强大。以下是 ES6 中数组的主要扩展:

  1. 扩展运算符(Spread Operator)

扩展运算符 (...) 可以将数组展开为单独的元素,常用于函数调用、数组拼接等场景。

示例:

javascript
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(展开数组作为参数)
  1. Array.from()

将类数组对象(如 argumentsNodeList)或可迭代对象(如 SetMap)转换为真正的数组。

示例:

javascript
const nodeList = document.querySelectorAll('div');
const arr = Array.from(nodeList); // 将 NodeList 转换为数组
console.log(arr);
  1. Array.of()

创建一个包含任意数量参数的新数组,解决了 new Array() 的行为不一致问题。

示例:

javascript
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]
  1. 数组实例的新方法

ES6 为数组实例添加了许多实用的方法:

(1) find()

查找数组中第一个满足条件的元素,返回该元素;如果没有找到,返回 undefined

示例:

javascript
const arr = [1, 2, 3, 4];
const result = arr.find(item => item > 2);
console.log(result); // 输出: 3

(2) findIndex()

查找数组中第一个满足条件的元素的索引,返回该索引;如果没有找到,返回 -1

示例:

javascript
const arr = [1, 2, 3, 4];
const index = arr.findIndex(item => item > 2);
console.log(index); // 输出: 2

(3) fill()

用指定的值填充数组。

示例:

javascript
const arr = [1, 2, 3];
arr.fill(0); // 填充为 [0, 0, 0]
console.log(arr);

(4) includes()

判断数组是否包含某个值,返回布尔值。

示例:

javascript
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 输出: true
console.log(arr.includes(4)); // 输出: false

(5) flat()

将嵌套数组“拉平”,返回一个新数组。默认拉平一层,可以通过参数指定拉平的层数。

示例:

javascript
const arr = [1, [2, [3]]];
console.log(arr.flat()); // 输出: [1, 2, [3]]
console.log(arr.flat(2)); // 输出: [1, 2, 3]

(6) flatMap()

先对数组中的每个元素执行映射操作,然后将结果拉平一层。

示例:

javascript
const arr = [1, 2, 3];
const result = arr.flatMap(x => [x * 2]);
console.log(result); // 输出: [2, 4, 6]
  1. for...of 循环

for...of 是遍历数组的新方式,直接获取数组的值(而不是索引)。

示例:

javascript
const arr = [1, 2, 3];
for (const value of arr) {
    console.log(value); // 依次输出: 1, 2, 3
}
  1. entries()keys()values()

这些方法返回数组的迭代器对象,分别用于获取数组的键值对、键或值。

示例:

javascript
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'
}
  1. copyWithin()

将数组的一部分复制到同一数组的另一位置,并返回修改后的数组。

示例:

javascript
const arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3); // 从索引 3 开始复制到索引 0
console.log(arr); // 输出: [4, 5, 3, 4, 5]
  1. Array.prototype[Symbol.iterator]

数组默认实现了迭代器协议,可以通过 Symbol.iterator 获取数组的迭代器。

示例:

javascript
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...ofentries()keys()values()

这些扩展使得数组的操作更加简洁和高效,是现代 JavaScript 开发中不可或缺的工具。

ES中var、let、const区别

在 ECMAScript (ES) 中,varletconst 都用于声明变量,但它们的行为和作用域有显著区别。以下是它们的详细对比:

  1. var
  • 作用域:函数作用域(function-scoped)。
    • 在函数内部声明的变量只能在函数内部访问。
    • 如果在函数外部声明,则成为全局变量。
  • 变量提升var 声明的变量会被提升到其作用域的顶部。
    • 变量可以在声明之前访问,但值为 undefined
  • 重复声明:允许在同一作用域内重复声明。
  • 示例
    javascript
    function example() {
        if (true) {
            var x = 10;
        }
        console.log(x); // 输出: 10(因为 var 是函数作用域)
    }
    example();
  1. let
  • 作用域:块级作用域(block-scoped)。
    • {} 块(如 ifforwhile 等)中声明的变量只能在该块内访问。
  • 变量提升let 声明的变量也会被提升,但在声明之前访问会抛出 ReferenceError(暂时性死区)。
  • 重复声明:不允许在同一作用域内重复声明。
  • 示例
    javascript
    function example() {
        if (true) {
            let y = 20;
            console.log(y); // 输出: 20
        }
        console.log(y); // 报错: y is not defined(因为 let 是块级作用域)
    }
    example();
  1. 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

对比总结

特性varletconst
作用域函数作用域块级作用域块级作用域
变量提升是(值为undefined是(暂时性死区)是(暂时性死区)
重复声明允许不允许不允许
重新赋值允许允许不允许
初始化要求不需要不需要必须初始化

使用建议

  1. 优先使用 const
    • 如果变量的值不会改变,使用 const 可以避免意外修改。
  2. 需要重新赋值时使用 let
    • 如果变量的值需要改变,使用 let
  3. 避免使用 var
    • var 的函数作用域和变量提升容易导致 bug,现代 JavaScript 中推荐使用 letconst

示例对比

javascript
// 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() 方法会在字符串的开头填充指定的字符,直到字符串达到指定的长度。

语法:

javascript
str.padStart(targetLength [, padString])
  • targetLength:目标长度。如果当前字符串长度小于该值,则在开头填充字符,直到字符串长度达到该值。
  • padString(可选):用于填充的字符。默认为空格 ()。

示例:

javascript
const str = '5';
console.log(str.padStart(3, '0')); // 输出: "005"
console.log(str.padStart(5, '*')); // 输出: "****5"

padEnd()

padEnd() 方法会在字符串的结尾填充指定的字符,直到字符串达到指定的长度。

语法:

javascript
str.padEnd(targetLength [, padString])
  • targetLength:目标长度。如果当前字符串长度小于该值,则在结尾填充字符,直到字符串长度达到该值。
  • padString(可选):用于填充的字符。默认为空格 ()。

示例:

javascript
const str = '5';
console.log(str.padEnd(3, '0')); // 输出: "500"
console.log(str.padEnd(5, '*')); // 输出: "5****"

总结

  • padStart() 用于在字符串开头填充字符。
  • padEnd() 用于在字符串结尾填充字符。
  • 如果填充字符 (padString) 未提供,默认使用空格填充。
  • 如果目标长度 (targetLength) 小于或等于字符串的当前长度,则返回原字符串。

这两个方法在处理字符串对齐、格式化输出等场景中非常有用。