JavaScript ES6+ 最佳实践

发布于:2025-05-09 ⋅ 阅读:(21) ⋅ 点赞:(0)
1. 变量声明:从 varlet/const

问题代码var 存在变量提升,只有函数作用域,没有块级作用域,容易导致变量污染。

// 变量提升导致意外行为
console.log(num); // undefined 而非报错
var num = 10;
// 没有块级作用域
var name = '张三';
if (true) {
  var name = '李四'; // 覆盖外部作用域的name
}
console.log(name); // 输出 '李四'

for (var i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i); // 输出5个5
  }, 100);
}

ES6+ 解决方案

const name = '张三'; // 不可重新赋值的常量
if (true) {
  let name = '李四'; // 块级作用域
}
console.log(name); // '张三'

for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100); // 0,1,2,3,4
}

最佳实践

  • 默认使用 const
  • 需要重新赋值时使用 let
  • 避免使用 var
2. 函数定义:从函数表达式到箭头函数

问题代码:冗长的语法,this 绑定问题。

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

var obj = {
  value: 1,
  increment: function() {
    var self = this;
    setTimeout(function() {
      self.value++;
    }, 100);
  }
};

ES6+ 解决方案

const add = (a, b) => a + b;

const obj = {
  value: 1,
  increment() {
    setTimeout(() => {
      this.value++;
    }, 100);
  }
};

特性

  • 箭头函数继承外层 this
  • 方法简写语法
  • 隐式返回
3. 异步编程:从回调地狱到 Promise/async-await

问题代码

getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      getFinalData(c, function(result) {
        console.log('Result:', result);
      }, failCallback);
    }, failCallback);
  }, failCallback);
}, failCallback);

ES6+ 解决方案

// Promise 链
getData()
  .then(a => getMoreData(a))
  .then(b => getEvenMoreData(b))
  .then(c => getFinalData(c))
  .then(result => console.log('Result:', result))
  .catch(error => console.error(error));

// async、await
async function processData() {
  try {
    const a = await getData();
    const b = await getMoreData(a);
    const c = await getEvenMoreData(b);
    const result = await getFinalData(c);
    console.log('Result:', result);
  } catch (error) {
    console.error(error);
  }
}

优势

  • 线性代码结构
  • 统一错误处理
  • 更好的可读性
4. 参数处理:从 arguments 到剩余参数

问题代码

function sum() {
  var args = Array.prototype.slice.call(arguments);
  return args.reduce(function(total, num) {
    return total + num;
  }, 0);
}

ES6+ 解决方案

function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

特点

  • 真正的数组
  • 更清晰的参数定义
  • 可与普通参数混用
5. 面向对象:从构造函数到类语法

问题代码

function Person(name) {
  this.name = name;
}
Person.prototype.sayHello = function() {
  console.log('Hello, ' + this.name);
};

ES6+ 解决方案

class Person {
  constructor(name) {
    this.name = name;
  }
  
  sayHello() {
    console.log(`Hello, ${this.name}`);
  }
  
  // 静态方法
  static create(name) {
    return new Person(name);
  }
}

新增特性

  • 继承 (extends)
  • 静态方法
  • 私有字段 (#field)
  • 静态块
6. 字符串处理:从拼接符到模板字符串

问题代码

var name = '张三';
var age = 25;
var message = name + '今年' + age + '岁';

ES6+ 解决方案

const message = `${name}今年${age}`;

高级用法

// 多行字符串
const html = `
<div>
  <h1>${title}</h1>
  <p>${content}</p>
</div>
`;

// 标签模板
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => 
    `${result}${str}<span class="highlight">${values[i] || ''}</span>`, '');
}
const output = highlight`Hello ${name}, welcome to ${siteName}!`;
7. 对象和数组处理:从手动操作到展开运算符

问题代码

var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
var combined = arr1.concat(arr2);

var obj1 = { a: 1, b: 2 };
var obj2 = { b: 3, c: 4 };
var merged = Object.assign({}, obj1, obj2);

ES6+ 解决方案

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];

const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 };

实用技巧

// 数组复制
const copy = [...original];

// 对象属性覆盖
const defaults = { color: 'red', size: 'medium' };
const config = { ...defaults, ...userConfig };

// 剩余参数解构
const { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
8. 数组迭代:从 for 循环到高阶函数

问题代码

var numbers = [1, 2, 3, 4];
var doubled = [];
for (var i = 0; i < numbers.length; i++) {
  doubled.push(numbers[i] * 2);
}

ES6+ 解决方案

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);

常用高阶函数

// 过滤
const evens = numbers.filter(n => n % 2 === 0);

// 累加
const sum = numbers.reduce((total, num) => total + num, 0);

// 查找
const firstEven = numbers.find(n => n % 2 === 0);

// 检查
const allPositive = numbers.every(n => n > 0);
const hasNegative = numbers.some(n => n < 0);
9. 模块系统:从 IIFE 到 ES Modules

问题代码

// math.js
(function() {
  function add(a, b) { return a + b; }
  function subtract(a, b) { return a - b; }
  window.math = { add: add, subtract: subtract };
})();

// app.js
math.add(1, 2);

ES6+ 解决方案

// math.js
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }

// app.js
import { add } from './math.js';
add(1, 2);

现代模块特性

  • 命名导出/默认导出
  • 动态导入 (import())
  • 顶层 await
  • 模块元信息 (import.meta)
10. 其他重要 ES6+ 特性

解构赋值

// 对象解构
const { name, age } = person;
const { name: personName, age: personAge } = person;

// 数组解构
const [first, second] = numbers;
const [head, ...tail] = numbers;

可选链 (?.) 和空值合并 (??)

// 避免 Cannot read property 'x' of undefined/null
const street = user?.address?.street ?? 'Unknown';

// 函数调用可选链
someInterface.customMethod?.();

BigInt

const bigNumber = 9007199254740991n;
const biggerNumber = bigNumber + 1n;

全局对象标准化

// 浏览器环境
globalThis === window; // true

// Node.js 环境
globalThis === global; // true
11.类型系统缺陷

原始问题

// 1. typeof null === 'object'
typeof null; // "object" (从JS诞生就存在的bug)

// 2. 隐式类型转换
[] + {}; // "[object Object]"
{} + []; // 0

// 3. == 的强制类型转换
'0' == false; // true

现代化实践

// 1. 使用 Object.prototype.toString 检测类型
Object.prototype.toString.call(null); // "[object Null]"

// 2. 避免隐式转换,使用显式转换
Number([]) + Number({}); // NaN

// 3. 始终使用 ===
'0' === false; // false

网站公告

今日签到

点亮在社区的每一天
去签到