ES6 核心特性详解(对比ES5)
一、变量声明:let 和 const
ES5 的问题
// 变量提升(hoisting)
console.log(a); // undefined
var a = 10;
// 重复声明
var b = 20;
var b = 30; // 允许重复声明
// 函数作用域
if (true) {
var c = 40;
}
console.log(c); // 40 - 变量泄漏到外部作用域
ES6 解决方案
// 块级作用域
let d = 10;
// let d = 20; // 报错:Identifier 'd' has already been declared
if (true) {
let e = 30;
const PI = 3.14;
// PI = 3.15; // 报错:Assignment to constant variable
}
// console.log(e); // 报错:e is not defined
// 暂时性死区(TDZ)
// console.log(f); // 报错:Cannot access 'f' before initialization
let f = 50;
二、箭头函数(Arrow Functions)
ES5 函数
var numbers = [1, 2, 3];
var doubled = numbers.map(function(n) {
return n * 2;
});
var obj = {
value: 42,
getValue: function() {
var self = this; // 需要保存this引用
setTimeout(function() {
console.log(self.value); // 42
}, 100);
}
};
ES6 箭头函数
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2); // 单行隐式返回
const obj = {
value: 42,
getValue() {
setTimeout(() => {
console.log(this.value); // 42 - 自动绑定外部this
}, 100);
}
};
// 多行函数体
const sum = (a, b) => {
const result = a + b;
return result;
};
三、模板字符串(Template Literals)
ES5 字符串拼接
var name = '张三';
var age = 28;
var message = '姓名: ' + name + '\n' +
'年龄: ' + age + '\n' +
'明年他就' + (age + 1) + '岁了';
var html = '<div class="card">' +
'<h2>' + name + '</h2>' +
'<p>年龄: ' + age + '</p>' +
'</div>';
ES6 模板字符串
const name = '张三';
const age = 28;
const message = `姓名: ${name}
年龄: ${age}
明年他就${age + 1}岁了`;
const html = `
<div class="card">
<h2>${name}</h2>
<p>年龄: ${age}</p>
</div>
`;
// 标签模板(高级用法)
function highlight(strings, ...values) {
return strings.reduce((result, str, i) =>
`${result}${str}<mark>${values[i] || ''}</mark>`, '');
}
const marked = highlight`姓名: ${name},年龄: ${age}`;
四、解构赋值(Destructuring)
ES5 赋值方式
var person = {
firstName: '李',
lastName: '四',
age: 35,
address: { city: '北京' }
};
var firstName = person.firstName;
var lastName = person.lastName;
var city = person.address.city;
var colors = ['红', '绿', '蓝'];
var firstColor = colors[0];
var thirdColor = colors[2];
ES6 解构赋值
const person = {
firstName: '李',
lastName: '四',
age: 35,
address: { city: '北京' }
};
// 对象解构
const { firstName="aaa", lastName, address: { city } } = person;
// 重命名
const { firstName: fName } = person;
// 默认值
const { middleName = '无' } = person;
// 数组解构
const colors = ['红', '绿', '蓝'];
const [firstColor, , thirdColor] = colors;
// 交换变量
let x = 1, y = 2;
[x, y] = [y, x]; // x=2, y=1
// 函数参数解构
function printUser({ firstName, lastName }) {
console.log(`${firstName} ${lastName}`);
}
printUser(person)
五、默认参数和Rest参数
ES5 处理方式
function greet(name) {
name = name || '访客'; // 默认值处理
console.log('你好, ' + name);
}
greet()
function sum() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function(acc, val) {
return acc + val;
}, 0);
}
sum(1,2,3,4)
ES6 解决方案
// 默认参数
function greet(name = '访客') {
console.log(`你好, ${name}`);
}
// Rest参数
const sum = (...args) => {
return args.reduce((acc, val) => acc + val, 0);
};
// 解构结合默认值
function createPerson({ name = '匿名', age = 0 } = {name = '匿名', age = 0}) {
console.log(`姓名: ${name}, 年龄: ${age}`);
}
createPerson({a:1})
// Spread操作符(与Rest相反)
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1,2,3,4,5,6]
六、类(Class)
ES5 面向对象
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
let p1 = new Person("aa",12)
let p2 = new Person()
// 原型方法
Person.prototype.greet = function() {
console.log('你好, 我是 ' + this.name);
};
p1.greet()
// 继承
function Student(name, age, grade) {
Person.call(this, name, age); // 调用父类构造函数
this.grade = grade;
}
// 设置原型链
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
// 子类方法
Student.prototype.study = function() {
console.log(this.name + '正在学习');
};
ES6 类语法
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
console.log(`你好, 我是 ${this.name}`);
}
// 静态方法
static info() {
console.log('这是一个Person类');
}
}
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类构造函数
this.grade = grade;
}
// 重写方法
greet() {
super.greet(); // 调用父类方法
console.log(`我是${this.grade}年级学生`);
}
study() {
console.log(`${this.name}正在学习`);
}
// Getter/Setter
get studentInfo() {
return `${this.name}-${this.grade}年级`;
}
set studentInfo(value) {
[this.name, this.grade] = value.split('-');
}
}
七、模块化(Modules)
ES5 模块化(CommonJS)
// math.js
module.exports = {
add: function(a, b) { return a + b; },
PI: 3.14
};
// app.js
var math = require('./math.js');
console.log(math.add(2, 3));
ES6 模块化
// math.js
export function add(a, b) {
return a + b;
}
export const PI = 3.14;
// 默认导出
export default function multiply(a, b) {
return a * b;
}
// app.js
import { add, PI } from './math.js';
import multiply from './math.js'; // 导入默认导出
console.log(add(2, 3)); // 5
console.log(multiply(2, 3)); // 6
// 重命名导入
import { PI as piValue } from './math.js';
// 全部导入
import * as math from './math.js';
console.log(math.PI);
八、Promise 和异步处理
ES5 回调地狱(Callback Hell)
function fetchData(callback) {
setTimeout(function() {
callback(null, '数据');
}, 1000);
}
fetchData(function(err, data) {
if (err) {
console.error(err);
} else {
console.log('第一步:', data);
fetchData(function(err, data2) {
if (err) {
console.error(err);
} else {
console.log('第二步:', data2);
// 更多嵌套...
}
});
}
});
ES6 Promise
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5
? resolve('数据')
: reject(new Error('请求失败'));
}, 1000);
});
}
fetchData()
.then(data => {
console.log('第一步:', data);
return fetchData();
})
.then(data => {
console.log('第二步:', data);
})
.catch(err => {
console.error('错误:', err.message);
});
// Promise.all - 并行处理
Promise.all([fetchData(), fetchData()])
.then(results => {
console.log('所有结果:', results);
});
// Promise.race - 竞速
Promise.race([fetchData(), fetchData()])
.then(firstResult => {
console.log('第一个结果:', firstResult);
});
ES7 async/await
async function processData() {
try {
const data1 = await fetchData();
console.log('第一步:', data1);
const data2 = await fetchData();
console.log('第二步:', data2);
const [result1, result2] = await Promise.all([fetchData(), fetchData()]);
console.log('并行结果:', result1, result2);
} catch (err) {
console.error('错误:', err.message);
}
}
processData();
九、其他重要特性
1. 对象字面量增强
// ES5
var name = '张三';
var age = 30;
var person = {
name: name,
age: age,
sayHello: function() {
console.log('你好');
}
};
// ES6
const name = '张三';
const age = 30;
const person = {
name, // 属性简写
age, // 属性简写
sayHello() { // 方法简写
console.log('你好');
},
// 计算属性名
[prefix + 'Info']: '用户信息'
};
2. 迭代器和生成器
// 迭代器
const iterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step <= 5) {
return { value: step, done: false };
}
return { done: true };
}
};
}
};
for (const value of iterable) {
console.log(value); // 1,2,3,4,5
}
// 生成器
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const gen = idGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
3. 新增数据结构
// Set - 唯一值集合
const set = new Set();
set.add(1);
set.add(2);
set.add(1); // 重复值被忽略
console.log(set.size); // 2
// Map - 键值对集合
const map = new Map();
map.set('name', '张三');
map.set('age', 30);
console.log(map.get('name')); // 张三
// WeakSet 和 WeakMap - 弱引用集合
十、ES6+ 兼容性和转换
1. Babel 转译
# 安装Babel
npm install @babel/core @babel/cli @babel/preset-env --save-dev
# .babelrc 配置
{
"presets": ["@babel/preset-env"]
}
# 转译文件
npx babel src -d dist
2. Polyfill 服务
<!-- 使用polyfill.io动态提供polyfill -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6,es7"></script>
3. 浏览器支持情况
- 现代浏览器(Chrome、Firefox、Edge)基本支持ES6
- IE11需要转译
- 使用 Can I use 检查特性支持