MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript赋值表达式的代码优化

2024-12-205.8k 阅读

理解 JavaScript 赋值表达式基础

在 JavaScript 中,赋值表达式是最基本且常用的操作之一。它用于将一个值赋给一个变量。最常见的赋值操作符就是 =。例如:

let num = 5;

这里我们声明了一个变量 num,并使用赋值表达式将值 5 赋给了它。

复合赋值操作符

除了基本的 = 操作符外,JavaScript 还提供了一系列复合赋值操作符。这些操作符将算术或位运算与赋值操作结合在一起,使代码更加简洁。

  1. 加法赋值 +=
let a = 3;
a += 2; // 等价于 a = a + 2;
console.log(a); // 输出 5
  1. 减法赋值 -=
let b = 5;
b -= 3; // 等价于 b = b - 3;
console.log(b); // 输出 2
  1. 乘法赋值 *=
let c = 4;
c *= 3; // 等价于 c = c * 3;
console.log(c); // 输出 12
  1. 除法赋值 /=
let d = 10;
d /= 2; // 等价于 d = d / 2;
console.log(d); // 输出 5
  1. 取模赋值 %=
let e = 7;
e %= 3; // 等价于 e = e % 3;
console.log(e); // 输出 1
  1. 位运算复合赋值
    • 按位与赋值 &=
let f = 5; // 二进制为 101
f &= 3; // 3 的二进制为 011,101 & 011 = 001
console.log(f); // 输出 1
- **按位或赋值 `|=`**:
let g = 2; // 二进制为 010
g |= 3; // 3 的二进制为 011,010 | 011 = 011
console.log(g); // 输出 3
- **按位异或赋值 `^=`**:
let h = 4; // 二进制为 100
h ^= 6; // 6 的二进制为 110,100 ^ 110 = 010
console.log(h); // 输出 2
- **左移赋值 `<<=`**:
let i = 3; // 二进制为 011
i <<= 2; // 左移 2 位,011 变为 1100
console.log(i); // 输出 12
- **右移赋值 `>>=`**:
let j = 12; // 二进制为 1100
j >>= 2; // 右移 2 位,1100 变为 0011
console.log(j); // 输出 3
- **无符号右移赋值 `>>>=`**:
let k = -4; // 二进制补码为 11111111111111111111111111111100
k >>>= 2; // 无符号右移 2 位,变为 00111111111111111111111111111111
console.log(k); // 输出 1073741823

理解这些复合赋值操作符,在合适的场景下使用它们,能够使代码更紧凑,并且在一定程度上提高代码的执行效率。

优化多个变量赋值

在实际开发中,经常会遇到需要同时给多个变量赋值的情况。

传统方式

一种常见的做法是逐个进行赋值:

let a, b, c;
a = 1;
b = 2;
c = 3;

这种方式虽然直观,但代码较为冗长。

链式赋值

在 JavaScript 中,可以使用链式赋值来简化这一过程:

let a, b, c;
a = b = c = 4;

这里 4 首先被赋给 c,然后 c 的值被赋给 b,最后 b 的值被赋给 a。这种方式简洁明了,减少了重复代码。

解构赋值优化

  1. 数组解构赋值: 当需要从数组中提取多个值并赋给不同变量时,解构赋值是一种非常强大的优化方式。
let arr = [1, 2, 3];
let [x, y, z] = arr;
console.log(x); // 输出 1
console.log(y); // 输出 2
console.log(z); // 输出 3

如果只需要数组中的部分值,可以这样做:

let arr2 = [4, 5, 6, 7];
let [m, , n] = arr2;
console.log(m); // 输出 4
console.log(n); // 输出 6
  1. 对象解构赋值: 从对象中提取属性并赋值给变量同样可以使用解构赋值。
let obj = {name: 'John', age: 30, city: 'New York'};
let {name, age} = obj;
console.log(name); // 输出 'John'
console.log(age); // 输出 30

还可以给解构的变量指定默认值:

let obj2 = {name: 'Jane'};
let {name, age = 25} = obj2;
console.log(name); // 输出 'Jane'
console.log(age); // 输出 25

通过使用解构赋值,无论是从数组还是对象中提取值并赋值给变量,代码都变得更加简洁、易读,同时也提高了开发效率。

函数返回值与赋值优化

函数在 JavaScript 中是一等公民,函数的返回值经常会被用于赋值操作。

单一返回值赋值

假设我们有一个简单的函数用于计算两个数的和:

function add(a, b) {
    return a + b;
}
let result = add(3, 5);
console.log(result); // 输出 8

这里函数 add 返回一个值,然后我们将这个返回值赋给变量 result

多返回值优化

在一些情况下,函数可能需要返回多个值。传统方式可能是返回一个数组或对象:

function calculate(a, b) {
    let sum = a + b;
    let difference = a - b;
    return {sum, difference};
}
let resultObj = calculate(5, 3);
console.log(resultObj.sum); // 输出 8
console.log(resultObj.difference); // 输出 2

然而,通过使用解构赋值,可以进一步优化这种情况:

function calculate2(a, b) {
    let sum = a + b;
    let difference = a - b;
    return [sum, difference];
}
let [sumValue, differenceValue] = calculate2(7, 4);
console.log(sumValue); // 输出 11
console.log(differenceValue); // 输出 3

这里函数 calculate2 返回一个数组,然后我们使用数组解构赋值将数组中的值分别赋给 sumValuedifferenceValue,使代码更加简洁直观。

条件赋值优化

在编程中,经常需要根据条件来进行赋值操作。

if - else 条件赋值

最常见的方式是使用 if - else 语句:

let num1 = 5;
let result1;
if (num1 > 10) {
    result1 = '大于 10';
} else {
    result1 = '小于等于 10';
}
console.log(result1); // 输出 '小于等于 10'

这种方式虽然逻辑清晰,但代码较为冗长。

三元运算符优化

JavaScript 中的三元运算符 ? : 可以很好地优化这种条件赋值:

let num2 = 15;
let result2 = num2 > 10? '大于 10' : '小于等于 10';
console.log(result2); // 输出 '大于 10'

三元运算符的语法为 condition? valueIfTrue : valueIfFalse,它根据 condition 的真假来决定返回 valueIfTrue 还是 valueIfFalse,从而简化了条件赋值的代码。

逻辑与(&&)和逻辑或(||)优化

  1. 逻辑与(&&)优化: 逻辑与运算符可以用于有条件地执行赋值操作。例如,当一个对象可能为 nullundefined,但我们只想在对象存在时获取其属性并赋值:
let obj3 = {name: 'Bob'};
let nameValue = obj3 && obj3.name;
console.log(nameValue); // 输出 'Bob'

let obj4 = null;
let nameValue2 = obj4 && obj4.name;
console.log(nameValue2); // 输出 undefined

这里如果 obj3 为真(即存在且不为 nullundefined),才会获取 obj3.name 并赋值给 nameValue

  1. 逻辑或(||)优化: 逻辑或运算符常用于设置默认值。例如:
let value1;
let finalValue1 = value1 || '默认值';
console.log(finalValue1); // 输出 '默认值'

let value2 = '自定义值';
let finalValue2 = value2 || '默认值';
console.log(finalValue2); // 输出 '自定义值'

如果 value1 为假(nullundefinedfalse0''),则 finalValue1 会被赋值为 '默认值';如果 value2 为真,则 finalValue2 会被赋值为 value2

循环中的赋值优化

在循环中进行赋值操作时,优化同样重要。

for 循环中的赋值

  1. 传统 for 循环赋值
let arr3 = [];
for (let i = 0; i < 5; i++) {
    arr3.push(i * 2);
}
console.log(arr3); // 输出 [0, 2, 4, 6, 8]

在这个传统的 for 循环中,每次迭代都向数组 arr3 中添加一个新值。

  1. 优化的 for 循环赋值: 可以预先分配数组的大小,避免多次动态扩展数组,从而提高性能:
let arr4 = new Array(5);
for (let i = 0; i < 5; i++) {
    arr4[i] = i * 2;
}
console.log(arr4); // 输出 [0, 2, 4, 6, 8]

这里我们预先创建了一个长度为 5 的数组 arr4,然后在循环中对其元素进行赋值,减少了数组动态扩展带来的性能开销。

for...of 循环中的赋值

for...of 循环主要用于遍历可迭代对象。在遍历过程中也可以进行赋值操作:

let arr5 = [1, 2, 3];
let newArr = [];
for (let value of arr5) {
    newArr.push(value * 3);
}
console.log(newArr); // 输出 [3, 6, 9]

for...of 循环简洁地遍历了数组 arr5,并将每个元素乘以 3 后添加到新数组 newArr 中。

for...in 循环与对象赋值

for...in 循环用于遍历对象的可枚举属性。在遍历过程中可以对对象的属性进行赋值操作:

let obj5 = {a: 1, b: 2};
for (let key in obj5) {
    obj5[key] = obj5[key] * 2;
}
console.log(obj5); // 输出 {a: 2, b: 4}

这里通过 for...in 循环遍历对象 obj5 的属性,并将每个属性值乘以 2 后重新赋值。

作用域与赋值优化

在 JavaScript 中,作用域对变量的赋值和访问有着重要影响。

全局作用域与局部作用域

  1. 全局变量赋值
let globalVar = '全局变量';
function printGlobal() {
    console.log(globalVar);
}
printGlobal(); // 输出 '全局变量'

这里 globalVar 是在全局作用域中声明并赋值的变量,在函数 printGlobal 中可以访问到。

  1. 局部变量赋值
function localAssignment() {
    let localVar = '局部变量';
    console.log(localVar);
}
localAssignment(); // 输出 '局部变量'
// console.log(localVar); // 报错,localVar 在此作用域未定义

在函数 localAssignment 中声明并赋值的 localVar 是局部变量,只能在函数内部访问。

块级作用域与赋值

ES6 引入了块级作用域,通过 letconst 关键字实现。

{
    let blockVar = '块级变量';
    console.log(blockVar);
}
// console.log(blockVar); // 报错,blockVar 在此作用域未定义

这里在块级作用域中声明并赋值的 blockVar,在块级作用域外部无法访问。合理利用块级作用域进行赋值,可以避免变量污染,提高代码的可维护性。

在函数内部,如果在块级作用域中使用 letconst 声明变量,并且该变量与外部作用域变量同名,不会影响外部变量的值:

let outerVar = '外部变量';
function blockScopeTest() {
    console.log(outerVar); // 输出 '外部变量'
    {
        let outerVar = '内部块级变量';
        console.log(outerVar); // 输出 '内部块级变量'
    }
    console.log(outerVar); // 输出 '外部变量'
}
blockScopeTest();

通过这种方式,可以在函数内部更灵活地进行变量赋值,同时保持外部作用域变量的独立性。

性能角度的赋值优化

从性能角度来看,赋值操作虽然看似简单,但在大规模代码和高频率执行场景下,优化赋值操作可以显著提升程序性能。

避免不必要的中间变量

在一些情况下,开发人员可能会不自觉地创建不必要的中间变量进行赋值。例如:

let num3 = 10;
let temp = num3;
let newNum = temp + 5;

这里 temp 就是一个不必要的中间变量,可以直接这样写:

let num4 = 10;
let newNum2 = num4 + 5;

减少不必要的中间变量可以减少内存占用和赋值操作次数,提高代码执行效率。

减少全局变量赋值

全局变量在 JavaScript 中查找和赋值的性能相对较低,因为 JavaScript 引擎需要在整个作用域链中查找全局变量。例如:

// 全局变量
let globalValue;
function globalAssignment() {
    globalValue = '赋值全局变量';
}

相比之下,局部变量的查找和赋值速度更快:

function localAssignment2() {
    let localValue = '局部变量赋值';
    return localValue;
}

因此,在可能的情况下,尽量将变量声明在函数内部,减少对全局变量的赋值操作。

缓存属性访问结果

当多次访问对象的属性并进行赋值操作时,缓存属性访问结果可以提高性能。例如:

let bigObj = {
    data: [1, 2, 3, 4, 5],
    processData: function() {
        let len = this.data.length;
        for (let i = 0; i < len; i++) {
            this.data[i] = this.data[i] * 2;
        }
        return this.data;
    }
};
console.log(bigObj.processData());

processData 函数中,我们缓存了 this.data.length 的值到 len 变量,避免了每次循环都访问 this.data.length,从而提高了性能。

结合现代 JavaScript 特性的赋值优化

随着 JavaScript 的不断发展,一些新特性为赋值优化提供了更多可能性。

可选链操作符(?.)与赋值

可选链操作符 ?. 用于在访问对象属性或调用对象方法时,如果对象为 nullundefined,不会抛出错误,而是返回 undefined。结合赋值操作,可以避免繁琐的 if 语句判断。例如:

let maybeObj = null;
let subValue = maybeObj?.subProperty;
console.log(subValue); // 输出 undefined

let newObj = {};
newObj.nested = {};
newObj.nested.sub = '子属性值';
let nestedValue = newObj?.nested?.sub;
console.log(nestedValue); // 输出 '子属性值'

在需要根据对象是否存在及属性是否存在进行赋值时,可选链操作符非常有用:

let targetObj;
let finalValue = targetObj?.nestedProp || '默认值';
console.log(finalValue); // 输出 '默认值'

这里如果 targetObjtargetObj.nestedPropnullundefinedfinalValue 会被赋值为 '默认值'

空值合并操作符(??)与赋值

空值合并操作符 ?? 用于在左侧操作数为 nullundefined 时,返回右侧操作数。这在设置默认值的赋值场景下有独特优势。例如:

let value3 = null;
let defaultValue = value3?? '空值合并默认值';
console.log(defaultValue); // 输出 '空值合并默认值'

let value4 = 0;
let anotherValue = value4?? '不应被赋值';
console.log(anotherValue); // 输出 0

与逻辑或 || 不同,?? 只有在左侧为 nullundefined 时才会返回右侧值,而 || 会在左侧为假值(包括 0''false 等)时返回右侧值。在赋值操作中,根据实际需求合理使用 ?? 可以更准确地设置默认值。

通过合理运用这些现代 JavaScript 特性,在赋值操作中可以编写更简洁、健壮的代码。

代码结构与赋值优化

良好的代码结构对于赋值优化也起着重要作用。

模块化与赋值

在 JavaScript 中,模块化编程可以将代码拆分成多个独立的模块。在模块内部进行赋值操作时,要注意模块作用域对变量的影响。例如,在 ES6 模块中:

// module.js
let moduleVar = '模块内变量';
export function getModuleVar() {
    return moduleVar;
}

在另一个模块中引用:

import {getModuleVar} from './module.js';
console.log(getModuleVar()); // 输出 '模块内变量'

通过模块化,变量的作用域更加清晰,赋值操作也更加可控。同时,模块可以避免全局变量污染,提高代码的可维护性和复用性。

函数封装与赋值

将赋值相关的操作封装到函数中,可以使代码结构更清晰,并且方便复用。例如,我们有一个需求是对数组中的每个元素进行特定计算并赋值:

function processArray(arr) {
    return arr.map((value) => value * 2);
}
let originalArr = [1, 2, 3];
let processedArr = processArray(originalArr);
console.log(processedArr); // 输出 [2, 4, 6]

这里将数组处理和赋值操作封装到 processArray 函数中,使得代码逻辑更加清晰,并且如果在其他地方有相同的需求,可以直接调用该函数,避免重复代码。

在编写大型应用程序时,合理的函数封装和模块化设计对于赋值优化以及整体代码的质量和性能都有着深远的影响。

通过对 JavaScript 赋值表达式在各个方面的深入理解和优化,可以编写出更高效、简洁、可维护的代码。无论是从基础的操作符使用,到结合新特性,再到从代码结构和性能角度进行优化,每一个环节都值得开发者仔细琢磨和实践。