为何使用call/apply而非直接调用?jQuery数组代码疑问解析
Great questions—let's unpack each one clearly, since these are common gotchas in JavaScript!
call() or apply() instead of calling the target function directly? The core reason is controlling what the this keyword refers to inside the target function. JavaScript's this is context-dependent, and sometimes the default context isn't what you need. Here are the most practical use cases:
- Borrow methods from other objects: Suppose you have an array-like object (like the
argumentsobject, or a DOM NodeList) that doesn't have array methods likeslice()ormap(). You can borrow these methods from a real array and make them run against your non-array object by settingthisto point to it. - Fix callback context: When you pass a function as a callback (e.g., to
setTimeoutor a click event),thisinside that function often defaults to the global object (orundefinedin strict mode)—not the object you care about.call()/apply()(orbind(), a related method) lets you lock in the correctthisvalue. - Pass dynamic argument lists:
apply()lets you pass an array of arguments to a function that expects individual parameters. This is perfect when you don't know how many arguments you'll have upfront.
Let's use a simple example to illustrate:
const user = { username: "jane_doe" }; function sayHi(greeting) { console.log(`${greeting}, ${this.username}!`); } // Direct call: `this` points to the global object (or undefined in strict mode) sayHi("Hey"); // Hey, undefined! // Using call() to set `this` to the user object sayHi.call(user, "Hey"); // Hey, jane_doe!
Let's break down why the code uses arr.flat.call(array) and arr.concat.apply([], array) instead of the more direct calls:
First, remember: the array variable here might not be a real JavaScript Array. It could be an array-like object (e.g., a collection of DOM elements, the arguments object, or a custom object with numeric keys and a length property). These objects don't inherit from Array.prototype, so they don't have built-in array methods like flat() or concat().
Why arr.flat.call(array) instead of array.flat()?
arris a real array, soarr.flatis a reference toArray.prototype.flat—the standard array flattening method.- By using
call(array), we tell theflat()method to treatarrayas itsthisvalue. This means even ifarrayis array-like,flat()will process it as if it's a real array, iterating over its elements and flattening them one level. - If we tried
array.flat()directly on an array-like object, we'd get aTypeErrorbecauseflat()doesn't exist on that object's prototype.
Why arr.concat.apply([], array) instead of [].concat(array)?
- Let's compare the two behaviors:
[].concat(array): Ifarrayis array-like, this will just add the entire array-like object as a single element to the new array (e.g.,[].concat({0: [1,2], length:1})returns[ {0: [1,2], length:1} ]—not the flattened elements we want).arr.concat.apply([], array):apply()tellsconcat()to use the empty array asthis, and pass each element ofarrayas individual arguments. This mimics the flattening behavior offlat():concat()will merge each of those elements into the empty array, resulting in a one-level flattened array.
- Again, using
arr.concat(akaArray.prototype.concat) ensures we're using the standard array method, even ifarraydoesn't have its ownconcat()method.
Here's a quick demo of the difference:
const arrayLike = { 0: [1,2], 1: [3,4], length: 2 }; // Direct concat call gives an array containing the array-like object console.log([].concat(arrayLike)); // [ {0: [1,2], 1: [3,4], length: 2} ] // apply() version flattens one level, just like flat() console.log([].concat.apply([], arrayLike)); // [1,2,3,4]
内容的提问来源于stack exchange,提问作者SRINATH Basker

