- ECMAScript 和 JavaScript 之间的关系可以概括如下:
- ES5和ES6的说明
- 其它版本
- 以下是ES5和ES6之间的一些主要区别:
- 1.变量声明:
- 2.块级作用域中的函数声明
- 3. 箭头函数:
- 4. 字符串模板(模板字面量):
- 5. 解构赋值:
- 6. 类(Class):
- 7. 模块化:
- 8. Promise对象:
- 9. 扩展运算符和剩余运算符:
- 10. 函数参数的默认值和扩展属性:
- 11. 生成器(Generators)
- 12. Symbol 和 迭代器(和异步迭代器)
- 13. 增强的对象字面量
- 14. Map 和 Set 数据结构
- 15. WeakMap 和 WeakSet
- 16. 模块的动态导入(Dynamic Imports)
- 17. 尾调用优化(尾递归函数)
- 18. 二进制和八进制字面量
- 19. Unicode 字符的增强支持
- 20. 新的内置方法和对象
- 21. 符号(Symbol)
- 22. 尾部逗号(Trailing Commas)
- 23. 标签模板(Tagged Templates)
- 24. 尾部扩展
- 25. Reflect API
- 26. Proxy API
- 27. 指数运算符(Exponentiation Operator)
- 28. 新的字符串方法
- 29. 新的数字方法
- 30. 新的 Math 方法
- 31. 新增的数组方法
- 32. Promise.finally
- 33. 异步编程的进一步支持
- 34. 新增的全局对象方法
- 35. 更好的错误处理
ECMAScript 和 JavaScript 之间的关系可以概括如下:
ECMAScript 是标准:ECMAScript 是由欧洲计算机制造商协会(ECMA)制定的一个脚本语言标准。它定义了脚本语言的语法、类型、语句、关键字、保留字、操作符、对象等。
JavaScript 是实现:JavaScript 是 ECMAScript 标准的一种实现。它最初由 Netscape 开发,并在此基础上不断发展。JavaScript 不仅遵循 ECMAScript 标准,还包括一些特定于浏览器的功能和 API,例如 DOM 操作、事件处理等。
版本关系:ECMAScript 的版本更新通常会引入新的语言特性,而 JavaScript 会随着这些标准的更新而发展。例如,ES6(ECMAScript 2015)引入了许多新特性,如箭头函数、类、模块、let 和 const 关键字等,这些特性随后在现代 JavaScript 中得到了广泛应用。
其他实现:除了 JavaScript,ECMAScript 还有其他实现,如 JScript(由微软开发)和 ActionScript(用于 Adobe Flash)。
ES5和ES6的说明
ES6 的新特性对于现代前端开发尤为重要,因为许多现代框架和库(如 Vue.js、React 和 Angular)都充分利用了 ES6 及更高版本的特性来构建复杂和高效的应用程序。 ES5和ES6(也称为ECMAScript 2015)是JavaScript语言的两个重要版本,它们在语法和功能上有许多显著的区别。
其它版本
ES7 (ECMAScript 2016):发布于2016年。ES7引入了一些较小的特性,例如指数运算符(**),Array.prototype.includes 方法用于检查数组中是否包含某个元素,以及async和await关键字,使得异步代码的编写更加容易。
ES8 (ECMAScript 2017):发布于2017年。ES8带来了诸如异步迭代(for-await-of循环)、Object.values()和Object.entries()方法、字符串填充方法(String.prototype.padStart和String.prototype.padEnd)以及Rest/Spread属性等特性。
ES9 (ECMAScript 2018):发布于2018年。ES9引入的特性包括异步迭代和生成器、Rest/Spread属性、Promise.finally()方法、正则表达式改进等。
ES10 (ECMAScript 2019):发布于2019年。ES10的新特性包括Array.prototype.flat()和Array.prototype.flatMap()方法、String.prototype.trimStart和String.prototype.trimEnd方法、可选的catch绑定等。
ES11 (ECMAScript 2020):发布于2020年。ES11带来了可选链(Optional Chaining)、空值合并运算符(Nullish Coalescing)、国际化增强、String.prototype.replaceAll方法、逻辑赋值运算符等。
ES12 (ECMAScript 2021):发布于2021年。ES12引入了Promise.any方法、String.prototype.replaceAll方法、逻辑赋值运算符、Intl.DateTimeFormat和Intl.DisplayNames的改进等。
以下是ES5和ES6之间的一些主要区别:
1.变量声明:
- ES5:仅使用var关键字声明变量,存在变量提升(hoisting)的现象。
var name = 'Alice';
- ES6:引入了let和const关键字。let用于声明块级作用域的变量,而const用于声明不变的常量。这两个关键字解决了变量提升和作用域的问题。
let age = 25;const PI = 3.14159;
2.块级作用域中的函数声明
ES5: 在块级作用域中声明函数可能导致不可预测的行为,特别是在严格模式下。
ES6: 明确支持在块级作用域中声明函数,遵循块级作用域规则。
{function foo() {console.log('ES6 块级作用域中的函数');}foo(); // 正常调用}// foo 在块外不可访问
3. 箭头函数:
ES5:使用function关键字或函数表达式声明函数。
var add = function(a, b) {return a + b;};
ES6:引入了箭头函数((args) => { / … / }),提供了更简洁的函数写法,并且不绑定自己的this上下文。
const add = (a, b) => a + b;
4. 字符串模板(模板字面量):
- ES5:字符串拼接通常使用+操作符。
var greeting = 'Hello, ' + name + '!';
- ES6:引入了模板字符串(使用反引号`),支持多行字符串和表达式插值。
const greeting = `Hello, ${name}!`;
5. 解构赋值:
- ES5:没有直接的解构赋值语法。
var person = { name: 'Bob', age: 30 };var name = person.name;var age = person.age;
- ES6:允许从数组或对象中提取值,并直接赋值给变量。
const { name, age } = person;
6. 类(Class):
- ES5:使用构造函数和原型链实现面向对象编程。
function Person(name) {this.name = name;}Person.prototype.sayHello = function() {console.log('Hello, ' + this.name);};
- ES6:引入了class关键字,提供了更简洁的语法来定义类和方法。
class Person {constructor(name) {this.name = name;}sayHello() {console.log(`Hello, ${this.name}`);}}
7. 模块化:
- ES5:没有原生的模块系统,通常使用CommonJS(Node.js)或AMD(RequireJS)等模块规范。
// 使用 IIFEvar myModule = (function() {var privateVar = 'secret';return {getSecret: function() {return privateVar;}};})();
- ES6:引入了模块化(使用import和export),支持原生的模块导入和导出。
// 导出export const pi = 3.14159;// 导入import { pi } from './math.js';
8. Promise对象:
- ES5:没有原生的Promise对象。
asyncFunction1(function(result1) {asyncFunction2(result1, function(result2) {// 继续嵌套});});
- ES6:引入了Promise,提供了一种更优雅的方式来处理异步操作。
asyncFunction1().then(result1 => asyncFunction2(result1)).then(result2 => {// 继续处理}).catch(error => {// 错误处理});
9. 扩展运算符和剩余运算符:
- ES5:没有这些运算符。使用 apply 或手动拼接数组。
function sum(a, b, c) {return a + b + c;}var numbers = [1, 2, 3];sum.apply(null, numbers);
- ES6:引入了扩展运算符(…),允许将数组或对象展开到另一个数组或对象中,以及剩余运算符,允许将剩余的参数收集到一个数组中。
const sum = (a, b, c) => a + b + c;const numbers = [1, 2, 3];sum(...numbers);const func = (...args) => {console.log(args);};
10. 函数参数的默认值和扩展属性:
- ES5:函数参数没有默认值,也没有扩展属性。
function greet(name) {name = name || 'Guest';console.log('Hello, ' + name);}
- ES6:允许为函数参数设置默认值,并且对象字面量可以扩展属性。
function greet(name = 'Guest') {console.log(`Hello, ${name}`);}
11. 生成器(Generators)
ES5: 没有内置生成器功能。
ES6: 引入了生成器函数,通过 function* 和 yield 实现迭代器模式。
function* generator() {yield 1;yield 2;yield 3;}const gen = generator();console.log(gen.next().value); // 1console.log(gen.next().value); // 2console.log(gen.next().value); // 3
12. Symbol 和 迭代器(和异步迭代器)
ES5: 没有 Symbol 类型,迭代器需要手动实现。
ES6: 引入了 Symbol 类型和内置的迭代器协议,支持更高级的迭代功能。
const sym = Symbol('description');const obj = {[sym]: 'value'};for (let key in obj) {console.log(key); // 不会输出 Symbol 类型的属性}// ES6 迭代器示例(同步)const iterable = {*[Symbol.iterator]() {yield 1;yield 2;yield 3;}};for (const value of iterable) {console.log(value);}// ES2018 异步迭代器示例async function* asyncGenerator() {yield 'a';yield 'b';yield 'c';}(async () => {for await (const value of asyncGenerator()) {console.log(value); // 'a', 'b', 'c'}})();
13. 增强的对象字面量
- ES5: 对象属性和方法需要完整的语法。
var obj = {name: name,sayHello: function() {console.log('Hello');}};
- ES6: 支持属性和值相同的简写、方法定义更简洁。
const obj = {name,sayHello() {console.log('Hello');}};
14. Map 和 Set 数据结构
- ES5: 使用对象和数组来模拟键值对和集合,但效率较低,且不支持非字符串类型的键。
// 使用对象模拟 Mapvar map = {};map['key1'] = 'value1';// 使用数组模拟 Setvar set = [];set.push(1);if (set.indexOf(1) === -1) {set.push(1);}
- ES6: 引入了 Map 和 Set,提供了更高效和便捷的键值对和集合操作。
// 使用 Mapconst map = new Map();map.set('key1', 'value1');console.log(map.get('key1')); // 'value1'// 使用 Setconst set = new Set();set.add(1);set.add(1); // 重复添加不会生效console.log(set.has(1)); // true
15. WeakMap 和 WeakSet
ES5: 没有弱引用的数据结构,所有键和值都被强引用,无法垃圾回收。
ES6: 引入了 WeakMap 和 WeakSet,允许键或值为弱引用,有助于内存管理。
// WeakMap 示例const wm = new WeakMap();let obj = {};wm.set(obj, 'value');obj = null; // obj 可以被垃圾回收// WeakSet 示例const ws = new WeakSet();let obj2 = {};ws.add(obj2);obj2 = null; // obj2 可以被垃圾回收
16. 模块的动态导入(Dynamic Imports)
- 模块加载通常依赖于同步或异步加载器,如 RequireJS,但不支持动态导入。
-ES6: 除了静态的 import 语法,还可以使用动态导入(需要结合 Promise 使用)。
// 静态导入import { moduleA } from './moduleA.js';// 动态导入import('./moduleB.js').then(moduleB => {moduleB.doSomething();}).catch(err => {console.error('模块加载失败', err);});
17. 尾调用优化(尾递归函数)
ES5: 不支持尾调用优化,递归调用可能导致堆栈溢出。
ES6: 规范中引入了尾调用优化,允许编译器优化尾递归调用,防止堆栈溢出(虽然 ES6 规范支持尾递归优化,但实际应用中,由于多数 JavaScript 引擎尚未完全实现这一特性,开发者在编写递归函数时仍需谨慎,避免深度递归导致的堆栈溢出问题。)。
// 尾递归函数function factorial(n, acc = 1) {if (n <= 1) return acc;return factorial(n - 1, n * acc); // 尾调用}console.log(factorial(5)); // 120
18. 二进制和八进制字面量
- ES5: 不支持二进制和八进制字面量,需使用字符串或十六进制表示。
// 使用十六进制var num = 0xFF; // 255
- ES6: 支持二进制(0b)和八进制(0o)字面量。
const binary = 0b11111111; // 255const octal = 0o377; // 255
19. Unicode 字符的增强支持
ES5: 对 Unicode 支持有限,无法直接表示所有 Unicode 字符。
ES6: 引入了 Unicode 转义序列,可以使用 \u{} 表示任意 Unicode 字符。
const smile = '\u{1F600}'; // 😀console.log(smile);
20. 新的内置方法和对象
ES5: 内置方法相对较少,功能有限。
ES6: 增加了许多新的内置方法和对象,如 Array.from、Array.find、Object.assign、Math 方法的扩展等。
// Array.fromconst arrayLike = { length: 2, 0: 'a', 1: 'b' };const arr = Array.from(arrayLike); // ['a', 'b']// Array.findconst numbers = [1, 2, 3, 4];const found = numbers.find(num => num > 2); // 3// Object.assignconst target = { a: 1 };const source = { b: 2 };const merged = Object.assign(target, source); // { a: 1, b: 2 }
21. 符号(Symbol)
ES5: 没有原生支持符号类型,无法创建独一无二的标识符。
ES6: 引入 Symbol 类型,用于创建唯一且不可变的标识符,常用于对象属性的私有化。
const sym1 = Symbol('desc');const sym2 = Symbol('desc');console.log(sym1 === sym2); // falseconst obj = {[sym1]: 'value1'};console.log(obj[sym1]); // 'value1'
22. 尾部逗号(Trailing Commas)
ES5: 在对象和数组字面量中,尾部逗号可能导致某些浏览器解析错误。
ES6: 允许在对象和数组的最后一个元素后添加尾部逗号,提升代码的可维护性。
const obj = {a: 1,b: 2,};const arr = [1,2,3,];
23. 标签模板(Tagged Templates)
ES5: 只能使用普通的模板字符串,无法对模板字符串进行处理。
ES6: 引入标签模板,允许在解析模板字符串时调用函数,实现更复杂的字符串处理。
function tag(strings, ...values) {console.log(strings);console.log(values);return 'Tagged Template Result';}const name = 'Alice';const age = 25;const result = tag`Name: ${name}, Age: ${age}`;console.log(result); // 'Tagged Template Result'
24. 尾部扩展
ES5: 函数参数列表中的尾部逗号可能导致语法错误。
ES6: 允许在函数参数列表中使用尾部逗号,提升代码的可维护性和可扩展性。
function foo(param1,param2,) {// 函数体}
25. Reflect API
ES5: 没有 Reflect 对象,无法统一处理对象的操作。
ES6: 引入了 Reflect 对象,提供了一组用于操作对象的方法。这些方法与 Object 的方法类似,但返回布尔值或其他更合理的结果,有助于元编程和代理操作。
// 使用 Reflect.setconst obj = {};Reflect.set(obj, 'name', 'Alice');console.log(obj.name); // 'Alice'// 使用 Reflect.getconsole.log(Reflect.get(obj, 'name')); // 'Alice'
26. Proxy API
- ES5: 无法拦截和定义对象的底层操作,如属性访问、赋值、函数调用等。
- ES6: 引入了 Proxy 对象,允许开发者创建一个对象的代理,从而拦截和自定义基本操作(如属性查找、赋值、枚举、函数调用等)。
const target = {};const handler = {get: function(target, prop, receiver) {console.log(`获取属性 ${prop}`);return Reflect.get(target, prop, receiver);},set: function(target, prop, value, receiver) {console.log(`设置属性 ${prop} 为 ${value}`);return Reflect.set(target, prop, value, receiver);}};const proxy = new Proxy(target, handler);proxy.name = 'Bob'; // 控制台输出: 设置属性 name 为 Bobconsole.log(proxy.name); // 控制台输出: 获取属性 name,结果: 'Bob'
27. 指数运算符(Exponentiation Operator)
- ES5: 使用 Math.pow 方法进行指数运算。
var result = Math.pow(2, 3); // 8
- ES6: 引入了 ** 运算符,使指数运算更加简洁。
const result = 2 ** 3; // 8
28. 新的字符串方法
- ES5: 字符串操作功能有限,主要依赖于字符串方法如 indexOf、substr 等。
- ES6: 增加了许多新的字符串方法,提升了字符串处理的便利性。
// includes: 判断字符串是否包含指定子串。const str = 'Hello, world!';console.log(str.includes('world')); // true// startsWith 和 endsWith: 判断字符串是否以指定子串开始或结束。console.log(str.startsWith('Hello')); // trueconsole.log(str.endsWith('!')); // true//repeat: 重复字符串指定次数。console.log('ha'.repeat(3)); // 'hahaha'//padStart 和 padEnd: 在字符串前后填充指定字符,以达到指定长度。console.log('5'.padStart(2, '0')); // '05'console.log('5'.padEnd(3, '0')); // '500'
29. 新的数字方法
- ES5: 数字方法相对有限,主要依赖于 Math 对象的方法。
- ES6: 增加了一些新的数字相关的方法,提升了数值处理的能力。
// Number.isNaN: 判断值是否为 NaN。console.log(Number.isNaN(NaN)); // trueconsole.log(Number.isNaN('NaN')); // false// Number.isInteger: 判断值是否为整数。console.log(Number.isInteger(4)); // trueconsole.log(Number.isInteger(4.5)); // false// Number.isFinite: 判断值是否为有限数。console.log(Number.isFinite(10)); // trueconsole.log(Number.isFinite(Infinity)); // false
30. 新的 Math 方法
- ES5: Math 对象的方法主要包括基本的数学运算,如 Math.max、Math.min、Math.floor 等。
- ES6: 增加了一些新的数学方法,扩展了 Math 对象的功能。
// Math.trunc: 去除数字的小数部分,返回整数部分。console.log(Math.trunc(4.9)); // 4console.log(Math.trunc(-4.9)); // -4// Math.sign: 判断数字的符号,返回 1、-1 或 0。console.log(Math.sign(10)); // 1console.log(Math.sign(-10)); // -1console.log(Math.sign(0)); // 0// Math.cbrt: 计算数字的立方根。console.log(Math.cbrt(27)); // 3// Math.hypot: 计算所有参数的平方和的平方根,类似于欧几里得距离。console.log(Math.hypot(3, 4)); // 5
31. 新增的数组方法
- ES5: 数组方法主要包括 forEach、map、filter、reduce 等。
- ES6: 增加了一些新的数组方法,进一步增强了数组的操作能力。
// Array.findIndex: 返回满足提供的测试函数的第一个元素的索引,否则返回 -1。const array = [5, 12, 8, 130, 44];const foundIndex = array.findIndex(element => element > 10);console.log(foundIndex); // 1// Array.fill: 用静态值填充数组中的元素,从起始索引到结束索引(不包括)。const array1 = [1, 2, 3];array1.fill(4, 1, 3);console.log(array1); // [1, 4, 4]// Array.copyWithin: 在数组内部将指定位置的元素复制到另一个位置,并返回该数组。const array2 = ['a', 'b', 'c', 'd', 'e'];array2.copyWithin(0, 3, 4);console.log(array2); // ['d', 'b', 'c', 'd', 'e']
32. Promise.finally
- ES5: 处理异步操作时,通常需要在 then 或 catch 中处理完成后的逻辑,可能导致代码重复。
- ES6: 虽然 Promise.finally 是在 ES2018 中引入的,但许多现代项目也将其视为 ES6+ 的一部分。finally 方法允许在 Promise 结束后执行某些操作,无论是成功还是失败。
fetch('https://api.example.com/data').then(response => response.json()).then(data => {// 处理数据}).catch(error => {// 处理错误}).finally(() => {// 无论成功或失败,都执行的逻辑console.log('请求完成');});
33. 异步编程的进一步支持
虽然 Promise 已在 ES6 中引入,但 ES6 对异步编程的支持为后续的异步特性(如 async/await)奠定了基础
- ES5: 异步操作主要依赖回调函数,容易导致“回调地狱”。
- ES6: 引入 Promise 对象,简化了异步操作的处理,提高了代码的可读性和可维护性。
// ES5 回调示例function fetchData(callback) {setTimeout(() => {callback('Data received');}, 1000);}fetchData(function(data) {console.log(data);});// ES6 Promise 示例function fetchData() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('Data received');}, 1000);});}fetchData().then(data => console.log(data)).catch(error => console.error(error));
34. 新增的全局对象方法
- ES5: 全局对象方法较少,主要包括 parseInt、parseFloat 等。
- ES6: 增加了一些新的全局对象方法,提升了全局环境的功能。
// globalThis: 提供了一个统一的方式来访问全局对象,无论是在浏览器、Node.js 还是其他环境中。console.log(globalThis === window); // 在浏览器中为 trueconsole.log(globalThis === global); // 在 Node.js 中为 true// isFinite 和 isNaN: ES6 对这两个全局方法进行了改进,更加严格地判断值的有限性和是否为 NaN。console.log(isFinite('15')); // trueconsole.log(Number.isFinite('15')); // falseconsole.log(isNaN('NaN')); // trueconsole.log(Number.isNaN('NaN')); // false
35. 更好的错误处理
- ES6: 引入了更丰富的错误类型,如 RangeError、ReferenceError 等,帮助开发者更精确地捕捉和处理不同类型的错误。
try {throw new RangeError('超出范围');} catch (error) {if (error instanceof RangeError) {console.log('范围错误:', error.message);} else {console.log('其他错误:', error.message);}}
