ES5 vs ES6:JavaScript 演进之路
JavaScript版本演进
ECMAScript是JavaScript的标准规范。
JavaScript是ECMAScript的一个实现,包含了ECMAScript以及DOM和BOM等额外的功能。
ES5 (ECMAScript 5)
- 发布时间:2009年12月
- 主要目标:提高语言的可靠性、安全性和表现力
- 重要特性:
- strict模式:更严格的语法检查
- JSON支持:JSON.parse()和JSON.stringify()
- 数组方法:forEach, map, filter, reduce, some, every等
- 对象属性:getter/setter
- Object方法:Object.create(), Object.defineProperty()等
ES6 (ECMAScript 2015)
- 发布时间:2015年6月
- 又称:ES2015(开始采用年份命名)
- 是JavaScript历史上最重要的版本更新
- 重要意义:
- 使JavaScript更适合编写复杂的大型应用程序
- 提供更好的模块化支持
- 引入类的概念,使面向对象编程更简单
- 增强的异步编程能力
版本演进时间线
版本 | 发布年份 | 重要特性 |
---|---|---|
ES1 | 1997 | 第一个版本 |
ES2 | 1998 | 细微更新 |
ES3 | 1999 | 正则表达式、try/catch |
ES4 | 放弃 | 过于激进,未发布 |
ES5 | 2009 | strict模式、JSON支持、新增数组方法 |
ES6/ES2015 | 2015 | 类、模块、箭头函数、Promise等 |
ES2016 | 2016 | Array.prototype.includes, 指数运算符 |
ES2017 | 2017 | async/await, Object.values/entries |
ES2018 | 2018 | Rest/Spread属性、异步迭代 |
ES2019 | 2019 | Array.prototype.flat, Object.fromEntries |
ES2020 | 2020 | 可选链操作符、空值合并运算符 |
ES2021 | 2021 | String.prototype.replaceAll, Promise.any |
ES2022 | 2022 | Top-level await, Class Fields |
为什么ES6如此重要?
开发效率:
- 简洁的语法(箭头函数、解构赋值)
- 更好的变量作用域控制(let/const)
- 模板字符串提高字符串处理效率
代码质量:
- 类的支持使面向对象更规范
- 模块化支持使代码组织更清晰
- Promise让异步代码更易维护
性能提升:
- 更好的内存管理(块级作用域)
- 优化的数组和对象操作
- 改进的字符串处理
生态系统:
- 统一的模块化标准
- 更好的工具支持
- 框架开发的基础
ES5 vs ES6
1. 变量声明对比
特性 | ES5 | ES6 |
---|---|---|
变量声明 | var | let, const |
变量提升 | 有 | 无 |
暂时性死区 | 无 | 有 |
块级作用域 | 无 | 有 |
重复声明 | 允许 | 不允许 |
示例代码:
// ES5变量声明和提升示例
console.log(x); // 输出: undefined (变量提升,但值未定义)
var x = 1;
var x = 2; // 允许重复声明
console.log(x); // 输出: 2
// ES6变量声明
// console.log(y); // 错误:暂时性死区,不能在声明前访问
let y = 1;
// let y = 2; // 错误:不能重复声明
console.log(y); // 输出: 1
// 块级作用域示例
{
let blockScoped = 'only available in this block';
const CONSTANT = 'cannot be reassigned';
console.log(blockScoped); // 输出: "only available in this block"
console.log(CONSTANT); // 输出: "cannot be reassigned"
}
// console.log(blockScoped); // 错误:blockScoped未定义
// console.log(CONSTANT); // 错误:CONSTANT未定义
// const声明后不能修改引用,但可以修改对象属性
const person = { name: 'John' };
person.name = 'Jane'; // 允许
console.log(person.name); // 输出: "Jane"
// person = {}; // 错误:不能重新赋值
2. 函数特性对比
特性 | ES5 | ES6 |
---|---|---|
默认参数 | 需手动检查 | 直接在参数列表定义 |
箭头函数 | 无 | 有 |
this绑定 | 动态绑定 | 词法绑定 |
参数处理 | arguments对象 | 剩余参数(…rest) |
示例代码:
// ES5默认参数
function greetES5(name) {
// 使用逻辑或运算符设置默认值
name = name || 'Guest';
return 'Hello ' + name;
}
console.log(greetES5()); // 输出: "Hello Guest"
console.log(greetES5('John')); // 输出: "Hello John"
// ES6默认参数和箭头函数
const greetES6 = (name = 'Guest') => `Hello ${name}`;
console.log(greetES6()); // 输出: "Hello Guest"
console.log(greetES6('Jane')); // 输出: "Hello Jane"
// ES5 this绑定问题示例
function Counter() {
var self = this; // 保存this引用
this.count = 0;
setInterval(function() {
self.count++;
console.log(self.count); // 每秒输出递增的数字
}, 1000);
}
// ES6箭头函数自动绑定this
class CounterES6 {
constructor() {
this.count = 0;
setInterval(() => {
this.count++;
console.log(this.count); // 每秒输出递增的数字
}, 1000);
}
}
// 剩余参数示例
// ES5使用arguments
function sumES5() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function(sum, num) {
return sum + num;
}, 0);
}
console.log(sumES5(1, 2, 3)); // 输出: 6
// ES6使用剩余参数
const sumES6 = (...numbers) => numbers.reduce((sum, num) => sum + num, 0);
console.log(sumES6(1, 2, 3)); // 输出: 6
3. 类和对象
特性 | ES5 | ES6 |
---|---|---|
类定义 | 构造函数 | class关键字 |
继承实现 | 原型链 | extends关键字 |
静态方法 | 手动添加 | static关键字 |
构造函数 | 同名函数 | constructor方法 |
示例代码:
// ES5方式定义类
function Animal(name) {
// 构造函数内定义实例属性
this.name = name;
}
// 在原型上定义实例方法
Animal.prototype.speak = function() {
return this.name + ' makes a sound';
};
// ES5方式创建和使用实例
var dog = new Animal('Dog');
console.log(dog.name); // 输出: "Dog"
console.log(dog.speak()); // 输出: "Dog makes a sound"
// ES6方式定义类
class AnimalES6 {
// constructor是类的构造方法
constructor(name) {
// 定义实例属性
this.name = name;
}
// 类方法直接定义在类中,无需使用prototype
speak() {
return `${this.name} makes a sound`;
}
// static关键字定义静态方法,只能通过类本身调用,不能通过实例调用
static create(name) {
return new AnimalES6(name);
}
}
// ES6方式创建和使用实例
const cat = new AnimalES6('Cat');
console.log(cat.name); // 输出: "Cat"
console.log(cat.speak()); // 输出: "Cat makes a sound"
// 使用静态方法创建实例
const bird = AnimalES6.create('Bird');
console.log(bird.speak()); // 输出: "Bird makes a sound"
// 继承示例
// ES5继承
function Dog(name, breed) {
// 调用父类构造函数
Animal.call(this, name);
this.breed = breed;
}
// 设置原型链
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 添加子类特有方法
Dog.prototype.bark = function() {
return this.name + ' barks!';
};
// ES6继承
class DogES6 extends AnimalES6 {
constructor(name, breed) {
// 调用父类构造函数
super(name);
this.breed = breed;
}
// 子类特有方法
bark() {
return `${this.name} barks!`;
}
}
// 使用继承类
const husky = new Dog('Max', 'Husky');
console.log(husky.speak()); // 输出: "Max makes a sound"
console.log(husky.bark()); // 输出: "Max barks!"
const golden = new DogES6('Buddy', 'Golden Retriever');
console.log(golden.speak()); // 输出: "Buddy makes a sound"
console.log(golden.bark()); // 输出: "Buddy barks!"
4. 模块化
特性 | ES5 | ES6 |
---|---|---|
模块定义 | CommonJS/AMD | ES Modules |
导出语法 | module.exports | export/export default |
导入语法 | require() | import |
动态导入 | 无原生支持 | dynamic import() |
示例代码:
// ES5 (CommonJS)
// math.js
module.exports = {
add: function(a, b) {
return a + b;
}
};
// main.js
var math = require('./math');
// ES6
// math.js
export const add = (a, b) => a + b;
export default class Calculator {};
// main.js
import { add } from './math';
import Calculator from './math';
5. 解构和展开
ES6新增特性:
// 数组解构
const [a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 输出: 1
console.log(b); // 输出: 2
console.log(rest); // 输出: [3, 4, 5]
// 对象解构
const person = { name: 'John', age: 30, city: 'New York' };
const { name, age, city: location = 'Unknown' } = person;
console.log(name); // 输出: "John"
console.log(age); // 输出: 30
console.log(location); // 输出: "New York"
// 展开运算符 - 数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // 输出: [1, 2, 3, 4, 5]
// 展开运算符 - 对象
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const clonedObj = { ...obj1 };
const mergedObj = { ...obj1, ...obj2 };
console.log(clonedObj); // 输出: { foo: 'bar', x: 42 }
console.log(mergedObj); // 输出: { foo: 'baz', x: 42, y: 13 }
// 函数参数中使用展开
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 输出: 3
6. 字符串和模板字面量
特性 | ES5 | ES6 |
---|---|---|
多行字符串 | 使用\n和+连接 | 模板字符串 |
字符串插值 | 字符串拼接 | ${expression} |
Unicode支持 | 有限 | 完整支持 |
示例代码:
// ES5字符串拼接
var name = 'John';
var age = 30;
var message = 'My name is ' + name + ' and I am ' + age + ' years old.\n' +
'This is a multi-line string.';
console.log(message);
// ES6模板字符串
const name = 'John';
const age = 30;
const message = `My name is ${name} and I am ${age} years old.
This is a multi-line string.`;
console.log(message);
// 模板字符串中的表达式
const a = 10;
const b = 20;
console.log(`Sum is: ${a + b}`); // 输出: "Sum is: 30"
console.log(`${a} is ${a < b ? 'less than' : 'greater than'} ${b}`);
// 输出: "10 is less than 20"
// 带标签的模板字符串
function myTag(strings, ...values) {
let result = '';
strings.forEach((string, i) => {
result += string;
if (i < values.length) {
result += values[i].toUpperCase();
}
});
return result;
}
const name = 'john';
console.log(myTag`Hello ${name}!`); // 输出: "Hello JOHN!"
7. 数组和对象新方法
示例代码:
// Array.from() - 将类数组对象转换为数组
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const arr = Array.from(arrayLike);
console.log(arr); // 输出: ['a', 'b', 'c']
// Array.of() - 创建新数组
console.log(Array.of(1, 2, 3)); // 输出: [1, 2, 3]
console.log(Array(3)); // 输出: [empty × 3]
// find() 和 findIndex()
const numbers = [1, 2, 3, 4, 5];
const found = numbers.find(num => num > 3);
const foundIndex = numbers.findIndex(num => num > 3);
console.log(found); // 输出: 4
console.log(foundIndex); // 输出: 3
// includes()
console.log(numbers.includes(3)); // 输出: true
console.log(numbers.includes(6)); // 输出: false
// keys(), values(), entries()
const obj = { a: 1, b: 2 };
console.log(Object.keys(obj)); // 输出: ['a', 'b']
console.log(Object.values(obj)); // 输出: [1, 2]
console.log(Object.entries(obj)); // 输出: [['a', 1], ['b', 2]]
// 数组遍历方法示例
const fruits = ['apple', 'banana', 'orange'];
// forEach
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
// 输出:
// 0: apple
// 1: banana
// 2: orange
// map
const upperFruits = fruits.map(fruit => fruit.toUpperCase());
console.log(upperFruits); // 输出: ['APPLE', 'BANANA', 'ORANGE']
8. Promise和异步编程
Promise 是异步编程的一种解决方案
Promise 可以用来封装各种异步操作,如网络请求、文件读写、定时器等。
通过 Promise,可以将异步操作的结果以同步的方式进行处理和管理。
示例代码:
// 创建一个Promise
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 使用Promise
console.log('开始');
delay(2000)
.then(() => {
console.log('2秒后执行'); // 2秒后输出
return delay(1000);
})
.then(() => {
console.log('再过1秒执行'); // 再过1秒后输出
})
.catch(error => {
console.error('发生错误:', error);
});
// async/await语法(ES8引入,但常与Promise一起使用)
async function example() {
try {
console.log('开始');
await delay(2000);
console.log('2秒后执行');
await delay(1000);
console.log('再过1秒执行');
} catch (error) {
console.error('发生错误:', error);
}
}
// Promise.all()示例
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
])
.then(values => {
console.log(values); // 输出: [1, 2, 3]
});
9. 其他新特性
// Set数据结构
const set = new Set([1, 2, 2, 3, 3]);
console.log(set.size); // 输出: 3
console.log([...set]); // 输出: [1, 2, 3]
set.add(4);
console.log(set.has(4)); // 输出: true
set.delete(4);
console.log(set.has(4)); // 输出: false
// Map数据结构
const map = new Map();
const key = { id: 1 };
map.set(key, 'value');
console.log(map.get(key)); // 输出: "value"
console.log(map.has(key)); // 输出: true
console.log(map.size); // 输出: 1
// Symbol
const symbol1 = Symbol('description');
const symbol2 = Symbol('description');
console.log(symbol1 === symbol2); // 输出: false
const obj = {
[symbol1]: 'Hello',
[symbol2]: 'World'
};
console.log(obj[symbol1]); // 输出: "Hello"
console.log(obj[symbol2]); // 输出: "World"
// 生成器函数
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next().value); // 输出: 1
console.log(gen.next().value); // 输出: 2
console.log(gen.next().value); // 输出: 3
console.log(gen.next().done); // 输出: true
总结
ES6相比ES5的主要改进:
- 更好的变量声明机制(let/const)
- 更简洁的函数语法(箭头函数)
- 更强大的面向对象能力(class)
- 更现代的模块化系统
- 更方便的数据处理(解构/展开)
- 更强大的异步编程能力(Promise)
- 更丰富的数据结构(Set/Map)
- 更完善的标准库
这些改进使JavaScript更加强大和易用,推动了现代Web开发的发展。