这段代码展示了 JavaScript 中 apply 方法的一个经典用法,用于将一个类数组对象(array-like object)转换为真正的数组。让我逐步拆解并解释这段代码的工作原理。
代码
|
1 2 |
const arrayLike = { 0: "apple", 1: "banana", length: 2 }; const realArray = Array.prototype.slice.apply(arrayLike); |
逐行解释
1. const arrayLike = { 0: "apple", 1: "banana", length: 2 };
- 定义:
arrayLike是一个普通对象,但它的结构模仿了数组,具有以下特点:- 数字键:
0和1是属性名,对应值"apple"和"banana"。 - length 属性:值为
2,表示这个对象“像”一个长度为 2 的数组。
- 数字键:
- 类数组对象:
- 在 JavaScript 中,类数组对象是指具有数字索引属性(如
0,1, …)和length属性的对象。 - 常见的类数组包括
arguments对象、DOM 节点列表(如document.querySelectorAll的结果)等。 - 但它不是真正的数组,因此没有数组的方法(如
push、slice等)。
- 在 JavaScript 中,类数组对象是指具有数字索引属性(如
- 当前值:
|
1 2 3 4 5 |
arrayLike = { 0: "apple", 1: "banana", length: 2 } |
2. const realArray = Array.prototype.slice.apply(arrayLike);
这行代码的目标是将 arrayLike 转换为真正的数组。分解来看:
(1) Array.prototype.slice
slice方法:- 是数组的内置方法,用于从数组中截取一部分并返回新数组。
- 语法:
array.slice(start, end),不传参数时返回整个数组的浅拷贝。 - 示例:
12const arr = ["a", "b", "c"];console.log(arr.slice()); // ["a", "b", "c"]
Array.prototype:Array.prototype.slice是slice方法的原始定义,可以被借用。- 所有数组实例都继承自
Array.prototype,它是数组方法的来源。
(2) .apply(arrayLike)
apply方法:- 是函数对象的方法,用于调用函数并指定
this和参数。 - 语法:
function.apply(thisArg, [argsArray])。 - 这里:
thisArg:arrayLike,将slice的this绑定到arrayLike。argsArray:未提供,默认是undefined,相当于调用slice()没有参数。
- 是函数对象的方法,用于调用函数并指定
- 作用:
apply让Array.prototype.slice在arrayLike上执行。slice会读取this(即arrayLike)的length属性和数字索引属性(0,1, …),然后返回一个新数组。
(3) 执行过程
- 当
Array.prototype.slice在arrayLike上执行时:slice检查this.length,发现是2。- 从
this[0]到this[length - 1]依次取值:this[0]是"apple"。this[1]是"banana"。
- 返回一个新数组:
["apple", "banana"]。
- 结果:
|
1 |
realArray = ["apple", "banana"]; |
为什么这样做?
- 目的:将类数组对象转为真正的数组,以便使用数组方法。
- 为什么用
apply:arrayLike本身没有slice方法(它不是数组)。- 通过
apply,我们“借用”了数组的slice方法,并将arrayLike作为this传入。
- 历史背景:
- 在 ES5 及更早版本中,这是将类数组转为数组的常见技巧。
- ES6 后可以用
Array.from(arrayLike)或[...arrayLike],更简洁。
等价代码
以下是现代等价的写法,效果相同:
- 使用
Array.from:
|
1 |
const realArray = Array.from(arrayLike); |
- 使用扩展运算符:
|
1 |
const realArray = [...arrayLike]; |
但在这段代码中,展示了 apply 的经典用法,理解它有助于掌握 JavaScript 的底层机制。
验证结果
|
1 2 3 |
console.log(realArray); // ["apple", "banana"] console.log(Array.isArray(realArray)); // true(是真正的数组) console.log(Array.isArray(arrayLike)); // false(不是数组) |
深入理解
slice的行为:
slice不关心this是否真的是数组,只要有length和数字索引,它就能工作。- 如果
length缺失或不正确,结果可能不准确:12const broken = { 0: "x", 1: "y" }; // 无 lengthconsole.log(Array.prototype.slice.apply(broken)); // []
apply的灵活性:
- 可以传递参数给
slice,比如:
1const partial = Array.prototype.slice.apply(arrayLike, [1]); // ["banana"]
- 与
call的区别:
- 如果用
call,需要逐个传递参数:
1const realArray = Array.prototype.slice.call(arrayLike); // 同样是 ["apple", "banana"] apply用数组传递参数,适合动态参数场景。
总结
- 含义:这段代码借用
Array.prototype.slice方法,通过apply将类数组arrayLike转为真正的数组。 - 过程:
arrayLike提供数据(0,1,length)。apply将slice的this绑定到arrayLike。slice根据length提取索引值,生成新数组。
- 结果:
realArray是["apple", "banana"]。 - 意义:展示了
apply的“方法借用”能力,是 JavaScript 函数式编程的基础技巧之一。