用一道面试题带你走进JS中的装箱机制和解构赋值

345 阅读6分钟

前言

其实个人一直觉得学习一种新的知识概念,用一道题目引进会更好,这样不仅可以激起你的求知欲,还能带你认识这种机制可能考察的面试场景。可能以前你对他们两有所耳闻,或者轻微的了解,但是它不足以让你去正确的使用它,请大家和我一起走进这道面试题。

面试题

这道题目很简单粗暴,就是给你一段代码,让你判断他会输出什么,往往这种题目都充满了坑,等着你去跳,其实这就是考察你的基本功是否扎实。

下面代码的输出结果是?

const s = '123'
s.c = '4';
s.d = '5';

const [a, b] = s;
const { c, d } = s;

console.log(a, b, c, d);

我们这里不着急知道答案,一步步来分析。首先我们声明了一个常量字符串 s ,它的值为 123,然后是给 字符串 s 添加了一个属性 c 它的值为 4。众所周知,在JavaScript中,字符串是一个基本数据类型,他是没有方法和属性的,那是不是这个代码直接就报错了呢?其实他还是可以正常运行的,这归功于JavaScipt提供的装箱机制

装箱机制

在 JavaScript 中,原始类型(如字符串、数字、布尔值等)是不可变的,并且没有方法或属性。为了方便操作这些原始类型,JavaScript 提供了对应的包装对象(wrapper objects),例如 StringNumberBoolean。当你尝试调用一个原始类型的属性或方法时,JavaScript 会自动将该原始值“装箱”为相应的包装对象,以便你可以访问这些属性和方法。这就是为什么你可以直接在字符串后面加上 .length直接就可以访问字符串的长度,这个属性了。

const str = 'hello';
console.log(str.length);  // 输出: 5

它的过程是str 是一个原始字符串 'hello',但它没有 length 这个属性,为了让你能够访问 length,JavaScript 会在后台临时创建一个 String 包装对象,一旦属性或方法被访问完毕,这个临时的包装对象就会被销毁,原始字符串保持不变。

我们现在知道了当你触发装箱机制的时候,他会创建一个临时的字符串包装对象,然后给他添加上 c 这个属性,值为 4,然后就被销毁了,同理 s.d 也是一样的。所以这两句代码

s.c = '4';
s.d = '5';

写了等于没写,他就是为了迷惑你的,顺便考察你对装箱机制的了解。

解构

我们继续分析代码,发现下面就是我们大名鼎鼎的ES6新增的结构赋值机制。

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring),它允许你从数组或对象中提取数据,并将它们赋值给变量。

这里我们分为数组解构和对象解构来介绍,正好题目也是分别用到了这两种。

数组解构

数组解构允许你从数组中按顺序提取元素,并将它们赋值给变量。你可以选择提取部分元素,也可以跳过某些元素。

基本用法
const arr = [1, 2, 3, 4, 5];
const [a, b, c] = arr;

console.log(a, b, c);  // 输出: 1 2 3

在这个例子中,abc 分别被赋值为数组 arr 的前三个元素。

跳过元素

你可以使用逗号来跳过不需要的元素:

const arr = [1, 2, 3, 4, 5];
const [a, , c] = arr;

console.log(a, c);  // 输出: 1 3

在这里,b 被跳过,ac 分别被赋值为 13

默认值

如果数组中的某些元素不存在,你可以为变量提供默认值:

const arr = [1, 2];
const [a, b, c = 3] = arr;

console.log(a, b, c);  // 输出: 1 2 3

在这里,c 的默认值是 3,因为数组中没有第三个元素。

嵌套数组解构

你还可以对嵌套的数组进行解构:

const arr = [1, [2, 3], 4];
const [a, [b, c], d] = arr;

console.log(a, b, c, d);  // 输出: 1 2 3 4

对象解构

对象解构允许你从对象中提取属性,并将它们赋值给变量。你可以选择提取部分属性,也可以为变量指定不同的名称。

基本用法
const obj = { name: 'Alice', age: 25, city: 'New York' };
const { name, age, city } = obj;

console.log(name, age, city);  // 输出: Alice 25 New York

在这个例子中,nameagecity 分别被赋值为对象 obj 中对应的属性值。

提取部分属性

你可以只提取你需要的属性,而忽略其他属性:

const obj = { name: 'Alice', age: 25, city: 'New York' };
const { name, city } = obj;

console.log(name, city);  // 输出: Alice New York
重命名变量

你可以通过在解构时指定不同的变量名来重命名提取的属性:

const obj = { name: 'Alice', age: 25, city: 'New York' };
const { name: userName, age: userAge } = obj;

console.log(userName, userAge);  // 输出: Alice 25

在这里,name 属性被赋值给 userNameage 属性被赋值给 userAge

默认值

如果对象中某些属性不存在,你可以为变量提供默认值:

const obj = { name: 'Alice' };
const { name, age = 30 } = obj;

console.log(name, age);  // 输出: Alice 30

在这里,age 的默认值是 30,因为对象中没有 age 属性。

嵌套对象解构

你还可以对嵌套的对象进行解构:

const obj = {
  user: {
    name: 'Alice',
    address: {
      city: 'New York',
      zip: '10001'
    }
  }
};

const { user: { name, address: { city, zip } } } = obj;

console.log(name, city, zip);  // 输出: Alice New York 10001

结果水落石出

其实讲到这里大家心里已经有了答案了, 没错他就是 1 2 undefined undefined 。 这就是因为 他们两的区别 一个是数组解构,一个是对象解构。

  • 数组解构[a, b] = s 会从字符串 '123' 中提取前两个字符,因此 ab 分别是 '1''2'
  • 对象解构{ c, d } = s 试图从 s 中提取 cd 属性。然而,由于 s 是一个原始字符串,它并没有 cd 这些属性。即使你之前尝试给 s 添加了这些属性,它们也只存在于那个短暂的包装对象上,而不会保留在原始字符串中。因此,cd 都会被赋值为 undefined

END

到这里恭喜大家,你已经解锁了一道来自 OPPO 的秋招题目,难度不算难,但是很考察你基本功是否扎实,大家是否从中收获了呢,可以在评论区交流,这道题的分享就到这里了。