admin管理员组

文章数量:1321613

Let's assume that we have the following function:

var a = function(data, type){
  var shift = [].shift;
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  console.log(data);
}

a(1,'test', 2, 3);

Let's assume that we have the following function:

var a = function(data, type){
  var shift = [].shift;
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  console.log(data);
}

a(1,'test', 2, 3);

I understand that data and type are just references to specific values in arguments. But why in the end, data is equal to 3?

Share Improve this question edited Jul 6, 2022 at 19:33 Erman Kadir Kahraman 7211 gold badge7 silver badges9 bronze badges asked May 26, 2015 at 12:49 dimko1dimko1 8727 silver badges16 bronze badges 5
  • The relationship between them are live... the solution is to use a separate array like var arr = [].slice.call(arguments) then shift.call(arr) – Arun P Johny Commented May 26, 2015 at 12:57
  • I know about connection:) I am interested why in the end it is not undefined but 3 – dimko1 Commented May 26, 2015 at 12:59
  • because when you call shift the position of all the values in the formal parameter list changes – Arun P Johny Commented May 26, 2015 at 13:05
  • @LeoCaseiro - "shift" isn't a reserved word. – nnnnnn Commented May 26, 2015 at 13:11
  • After 15 years of writing JavaScript, I just learned something new. Does this mean that JavaScript actually has a reference type? I can't think of any other way to have the (primitive) value of a variable change through a different reference. That is, data and type seem to be actual references to arguments[0] and arguments[1], I'd love to see a definitive reference about this and would love to be able to actually make use of references in JavaScript – Ruan Mendes Commented May 26, 2015 at 13:17
Add a ment  | 

5 Answers 5

Reset to default 4

From https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Strict_mode#Making_eval_and_arguments_simpler:

Strict mode makes arguments less bizarrely magical.

In normal code within a function whose first argument is arg, setting arg also sets arguments[0], and vice versa (unless no arguments were provided or arguments[0] is deleted).

arguments objects for strict mode functions store the original arguments when the function was invoked. arguments[i] does not track the value of the corresponding named argument, nor does a named argument track the value in the corresponding arguments[i].

You actually met the "unless arguments[i] is deleted" case ;)

What you get is an arguments array with [data, type] and two more arguments not being used. Add a console.log before and after each shift and you'll see what happens;

var shift = [].shift;
console.log("args", arguments)
console.log(data, type);
// Outputs [1, 'test', 2, 3] => data = 1, type = 'test'

shift.call(arguments);
console.log("args", arguments)
console.log(data, type);
// Outputs ['test', 2, 3] => data = 'test', type = 2

shift.call(arguments);
console.log("args", arguments)
console.log(data, type);
// Outputs [2, 3] => data = 2, type = 3

shift.call(arguments);
console.log("args", arguments)
console.log(data, type);
// Outputs [3] => data = 3, type = 3 (type is keeping it's old value)

shift.call(arguments);
console.log("args", arguments)
console.log(data, type);
// Outputs [] => data = 3, type = 3 (both are keeping their old value)

Does this help?

In your case 'data' is a reference to the first position of the arguments array. When you call your function the values are

arguments[0] => data => 1
arguments[1] => type => 'test'
arguments[2] => 2
arguments[3] => 3

The docs says that array function shift it removes the first element. But also says

shift is intentionally generic; this method can be called or applied to objects resembling arrays. Objects which do not contain a length property reflecting the last in a series of consecutive, zero-based numerical properties may not behave in any meaningful manner.

Internally the shift functions call the delete operator. After apply four times the arguments array is [undefined, undefined]. Now the thing is that calling delete on a variable removes it but if there still a reference to the variable the value is not removed

delete arguments[0];
delete arguments[1];
console.log(arguments); => [2: 2, 3: 3] // Position 0 and 1 were removed. Lenght is still 4
console.log(data);  => 1
console.log(type);  => 'test'

So you see the variables are still being referenced regardless you deleted the value from arguments.

Here is a step-through of your code.

var a = function(data, type){
  var shift = [].shift;//data points to arguments[0], or 1
  shift.call(arguments); // arguments[0] is now 'test'
  shift.call(arguments); // arguments[0] is now 2
  shift.call(arguments); //arguments[0] is now 3
  shift.call(arguments); // data pointed @ 3, arguments[0] is undefined
  console.log(data);
}

a(1,'test', 2, 3);

JS seems to be doing some work in the background, where if the corresponding index of arguments (which isn't an array) goes from truthy to falsy/undefined, the previous value gets retained.

What you are observing is only true for non-strict JavaScript, so it shouldn't be relied on. See http://www.ecma-international/ecma-262/5.1/#sec-10.6, there's a section that explains the different behavior between strict and non-strict mode.

Sp00m posted a nice explanation from MDN

From the EcmaScript specification

NOTE 1: For non-strict mode functions the array index (defined in 15.4) named data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function’s execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. For strict mode functions, the values of the arguments object’s properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.

var a = function(data, type){
  var shift = [].shift;
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  console.log(data);
}

var b = function(data, type){
  'use strict';
  var shift = [].shift;
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  shift.call(arguments);
  console.log(data);
}

a(1,'test', 2, 3); // 3
b(1,'test', 2, 3); // 1

本文标签: Javascript arguments shiftingStack Overflow