JavaScript 是 Web 前端开发中必不可少的一部分,但是随着 Web 应用越来越复杂,JavaScript 代码量也越来越大,而且代码的性能和可读性也变得越来越重要。本文将介绍 30 个优化 JavaScript 代码的技巧,帮助你写出更高效、更可读、更可维护的代码。
1. 使用 const 和 let
使用 const 和 let 代替 var 可以使代码更加可读,也可以避免变量提升带来的问题。在 ES6 中,const 和 let 也分别代表常量和块级作用域变量,可以提高代码的性能。
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
2. 避免全局变量
全局变量会污染全局命名空间,导致命名冲突和不可预测的行为。可以使用模块化的方式来避免全局变量。
// www.javascriptcn.com code example
// bad
var a = 1;
function foo() {
console.log(a);
}
// good
const a = 1;
export function foo() {
console.log(a);
}3. 避免 eval()
eval() 可以执行任意字符串代码,但是会带来安全问题和性能问题。可以使用 Function() 来替代 eval()。
// bad
eval('console.log("hello world")');
// good
Function('console.log("hello world")')();4. 使用模板字符串
模板字符串可以方便地拼接字符串和变量,避免使用 + 连接字符串。同时也可以使用模板字符串的多行文本特性,使代码更易读。
// bad
const name = 'Alice';
const message = 'Hello, ' + name + '!';
// good
const name = 'Alice';
const message = `Hello, ${name}!`;5. 使用箭头函数
箭头函数可以简化函数的定义和调用,同时也可以避免 this 指向问题。在 ES6 中,箭头函数也可以更好地与 Promise 和 async/await 配合使用。
// bad
function foo(a, b) {
return a + b;
}
// good
const foo = (a, b) => a + b;6. 避免 with 语句
with 语句可以使代码更简洁,但是会引起命名冲突和不可预测的行为。可以使用对象解构或者变量声明来代替 with 语句。
// bad
with (obj) {
console.log(a);
}
// good
const { a } = obj;
console.log(a);7. 使用数组方法
数组方法可以使代码更简洁、更易读。同时也可以避免使用 for 循环带来的性能问题。
// www.javascriptcn.com code example
// bad
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// good
const arr = [1, 2, 3];
arr.forEach((item) => {
console.log(item);
});8. 使用对象方法
对象方法可以使代码更简洁、更易读。同时也可以避免使用 if-else 和 switch-case 带来的代码冗长和可读性问题。
// www.javascriptcn.com code example
// bad
const obj = { a: 1, b: 2 };
if (obj.a === 1) {
console.log('a is 1');
} else {
console.log('a is not 1');
}
// good
const obj = { a: 1, b: 2 };
obj.isAOne = function() {
return this.a === 1;
};
if (obj.isAOne()) {
console.log('a is 1');
} else {
console.log('a is not 1');
}9. 使用解构赋值
解构赋值可以方便地从数组或者对象中取出需要的值,避免使用多个变量和 if-else 带来的代码冗长和可读性问题。
// bad const arr = [1, 2, 3]; const a = arr[0]; const b = arr[1]; // good const arr = [1, 2, 3]; const [a, b] = arr;
10. 使用默认参数
默认参数可以方便地设置函数参数的默认值,避免使用 if-else 和 || 运算符带来的代码冗长和可读性问题。
// www.javascriptcn.com code example
// bad
function foo(a, b) {
a = a || 1;
b = b || 2;
}
// good
function foo(a = 1, b = 2) {
}11. 避免使用 arguments
arguments 对象可以方便地获取函数的参数,但是会带来性能问题和可读性问题。可以使用 rest 运算符和默认参数来代替 arguments。
// www.javascriptcn.com code example
// bad
function foo() {
console.log(arguments[0]);
}
// good
function foo(...args) {
console.log(args[0]);
}
// better
function foo(arg) {
console.log(arg);
}12. 使用数组展开运算符
数组展开运算符可以方便地将数组打散成多个参数,避免使用 apply 带来的可读性问题和性能问题。
// bad const arr = [1, 2, 3]; console.log.apply(console, arr); // good const arr = [1, 2, 3]; console.log(...arr);
13. 使用对象展开运算符
对象展开运算符可以方便地将对象合并或者拷贝,避免使用 Object.assign() 带来的可读性问题和代码冗长问题。
// www.javascriptcn.com code example
// bad
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const obj3 = Object.assign({}, obj1, obj2);
// good
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const obj3 = { ...obj1, ...obj2 };14. 使用模块化
模块化可以使代码更易于维护和扩展,避免全局变量带来的命名冲突和不可预测的行为。可以使用 ES6 的模块化或者 CommonJS 的模块化。
// www.javascriptcn.com code example
// ES6
export function foo() {
}
import { foo } from './foo.js';
// CommonJS
module.exports = {
foo: function() {
}
};
const foo = require('./foo.js');15. 避免重复计算
重复计算会带来性能问题,可以使用变量缓存或者函数缓存来避免重复计算。
// www.javascriptcn.com code example
// bad
function foo(a, b) {
return a + b + Math.random();
}
// good
function foo(a, b) {
const random = Math.random();
return a + b + random;
}
// better
function foo(a, b) {
const cache = {};
return function() {
const key = JSON.stringify(arguments);
if (cache[key]) {
return cache[key];
} else {
const result = a + b;
cache[key] = result;
return result;
}
};
}16. 避免多次查询 DOM
查询 DOM 会带来性能问题,可以使用变量缓存或者函数缓存来避免多次查询 DOM。
// www.javascriptcn.com code example
// bad
function foo() {
const el = document.getElementById('my-element');
el.style.color = 'red';
el.style.backgroundColor = 'blue';
}
// good
function foo() {
const el = document.getElementById('my-element');
el.style.color = 'red';
el.style.backgroundColor = 'blue';
}
// better
function foo() {
const el = document.getElementById('my-element');
return function() {
el.style.color = 'red';
el.style.backgroundColor = 'blue';
};
}17. 使用事件委托
事件委托可以避免给每个子元素添加事件监听器带来的性能问题,可以在父元素上添加事件监听器,然后判断事件源是否为子元素。
// www.javascriptcn.com code example
// bad
const list = document.getElementById('my-list');
const items = list.getElementsByTagName('li');
for (let i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
console.log('clicked');
});
}
// good
const list = document.getElementById('my-list');
list.addEventListener('click', function(event) {
const target = event.target;
if (target.tagName === 'LI') {
console.log('clicked');
}
});18. 使用事件缓存
事件缓存可以避免重复添加事件监听器带来的性能问题,可以在元素上添加一个属性来缓存事件监听器。
// www.javascriptcn.com code example
// bad
const button = document.getElementById('my-button');
button.addEventListener('click', function() {
console.log('clicked');
});
// good
const button = document.getElementById('my-button');
if (!button.clickHandler) {
button.clickHandler = function() {
console.log('clicked');
};
}
button.addEventListener('click', button.clickHandler);19. 使用 requestAnimationFrame
requestAnimationFrame 可以避免使用 setInterval 和 setTimeout 带来的性能问题和可读性问题,可以更好地控制动画的帧率和流畅度。
// www.javascriptcn.com code example
// bad
function animate() {
setTimeout(animate, 16);
}
// good
function animate() {
requestAnimationFrame(animate);
}20. 使用 Web Worker
Web Worker 可以将一些耗时的操作放到后台线程中执行,避免阻塞主线程带来的性能问题和可读性问题。
// www.javascriptcn.com code example
// bad
function foo() {
for (let i = 0; i < 1000000000; i++) {
// do some heavy computation
}
}
// good
function foo() {
const worker = new Worker('worker.js');
worker.postMessage('start');
worker.addEventListener('message', function(event) {
console.log(event.data);
});
}21. 使用 Class
Class 可以使代码更加面向对象,避免使用 prototype 带来的可读性问题和代码冗长问题。
// www.javascriptcn.com code example
// bad
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}!`);
};
// good
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}!`);
}
}22. 使用 Promise
Promise 可以更好地处理异步操作,避免使用回调函数带来的可读性问题和代码冗长问题。
// www.javascriptcn.com code example
// bad
function foo(callback) {
setTimeout(function() {
callback('result');
}, 1000);
}
// good
function foo() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('result');
}, 1000);
});
}23. 使用 async/await
async/await 可以更好地处理异步操作,避免使用 Promise.then() 带来的可读性问题和代码冗长问题。
// www.javascriptcn.com code example
// bad
function foo() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('result');
}, 1000);
}).then(function(result) {
console.log(result);
});
}
// good
async function foo() {
const result = await new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('result');
}, 1000);
});
console.log(result);
}24. 使用 Map 和 Set
Map 和 Set 可以更好地处理键值对和集合,避免使用对象和数组带来的可读性问题和代码冗长问题。
// www.javascriptcn.com code example
// bad
const obj = {};
obj['a'] = 1;
obj['b'] = 2;
// good
const map = new Map();
map.set('a', 1);
map.set('b', 2);
// bad
const arr = [1, 2, 3];
const index = arr.indexOf(2);
// good
const set = new Set([1, 2, 3]);
const hasTwo = set.has(2);25. 使用 Symbol
Symbol 可以创建唯一的标识符,避免使用字符串带来的命名冲突和可读性问题。
// www.javascriptcn.com code example
// bad
const obj = {
a: 'a',
b: 'b',
c: 'c'
};
// good
const a = Symbol();
const b = Symbol();
const c = Symbol();
const obj = {
[a]: 'a',
[b]: 'b',
[c]: 'c'
};26. 使用 Proxy
Proxy 可以在对象和函数调用之间添加一层拦截,实现动态修改和校验,避免使用 if-else 和 try-catch 带来的代码冗长和可读性问题。
// www.javascriptcn.com code example
// bad
const obj = {
a: 1,
b: 2
};
if (obj.a !== undefined) {
console.log(obj.a);
} else {
console.log('a is undefined');
}
// good
const handler = {
get(target, key) {
if (key in target) {
return target[key];
} else {
return `key ${key} is not defined`;
}
}
};
const obj = new Proxy({ a: 1, b: 2 }, handler);
console.log(obj.a);
console.log(obj.c);27. 使用 Reflect
Reflect 可以更好地处理对象的属性和方法,避免使用 Object 带来的可读性问题和代码冗长问题。
// bad
const obj = { a: 1 };
Object.defineProperty(obj, 'b', { value: 2 });
// good
const obj = { a: 1 };
Reflect.defineProperty(obj, 'b', { value: 2 });28. 使用 bind
bind 可以更好地处理函数的 this 指向问题,避免使用 self 或者 that 带来的可读性问题和代码冗长问题。
// www.javascriptcn.com code example
// bad
const that = this;
function foo() {
console.log(that);
}
// good
function foo() {
console.log(this);
}
foo.bind(this)();29. 使用 try-catch-finally
try-catch-finally 可以更好地处理异常和清理资源,避免使用 if-else 和 return 带来的可读性问题和代码冗长问题。
// www.javascriptcn.com code example
// bad
function foo() {
if (condition) {
return 'result';
} else {
throw new Error('error');
}
}
// good
function foo() {
try {
if (condition) {
return 'result';
} else {
throw new Error('error');
}
} catch (error) {
console.log(error);
} finally {
console.log('cleanup');
}
}30. 使用注释
注释可以使代码更易读、更易维护,避免使用复杂的逻辑和算法带来的可读性问题。
// www.javascriptcn.com code example
// bad
function foo(a, b) {
// calculate the sum of a and b
const sum = a + b;
// calculate the average of sum and 2
const avg = sum / 2;
return avg;
}
// good
function foo(a, b) {
const sum = a + b; // calculate the sum
const avg = sum / 2; // calculate the average
return avg;
}以上是优化 JavaScript 代码的 30 个技巧,希望能帮助你写出更高效、更可读、更可维护的代码。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d38b94a941bf71346c06d0