Consider this JavaScript function:
var f = function (a) {
console.log(a+" "+arguments[0]);
a = 3;
console.log(a+" "+arguments[0]);
}
I would expect that a and arguments[0] reference the same value only up to the second statement of the function. Instead they appear to always refer the same value: f(2) causes
2 2
3 3
and f({foo: 'bar'}) causes:
[object Object] [object Object]
3 3
Are argument identifiers and the arguments identifier linked in a special way?
Are argument identifiers and the arguments identifier linked in a special way?
Yes (but only in non-strict mode).
From the specification (ES6, ES5):
For non-strict mode functions the integer indexed 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.
Related
While working on another problem, I created this fiddle:
http://jsfiddle.net/tr2by/
function foo() {
// console.log(_.isBoolean(this));
console.log(this === true);
}
foo.call(true); // object:[object Boolean]
foo.apply(true); // object:[object Boolean]
Is this an example of auto-boxing?
Going from a value type to a reference type.
Here is a wikipedia def.
First of all I assume you are talking about automatic conversion of primitive values to objects. This happens in two cases in JavaScript:
When you pass a primitive value as the this value to .call or .apply (not in strict mode though).
When you are trying to access a "property" of a primitive value, e.g. "foo bar".split().
In the first case the conversion is permanent, i.e. this will indeed reference an object, in the second the conversion only takes place internally for the duration of the evaluation
If you are not interested in the details of the conversion, you can ignore the rest of the answer.
1. Primitive value as this
When a function is exectued and its this value is not an object, it is converted to one, at least in non-strict mode. This is described in §10.4.3 Entering Function Code [spec] in the ECMAScript 5.1 documentation:
The following steps are performed when control enters the execution context for function code contained in function object F, a caller provided thisArg, and a caller provided argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
[...]
As you can see in step three the value is converted to an object by calling ToObject [spec].
2. Property access
Something similar happens when you are trying to access properties (§11.2.1 Property Accessors [spec]). The quoted part here explains how the expression foo[bar] is evaluated, i.e. how property access with the bracket notation is evaluated. The part we are interested in applies to dot notation as well.
The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:
Let baseReference be the result of evaluating MemberExpression.
Let baseValue be GetValue(baseReference).
[...]
8. Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
The important step is the last one: No matter to what MemberExpression evaluates, it is converted to a value of type Reference [spec]. This is a datatype only used in the specification and contains additional information about how the actual value should be retrieved from the reference (not to be confused with object references in actual JavaScript code!).
To get the "real" value/result from such a reference, the internal function GetValue(V) (§8.7.1) [spec] is called (just like in step 2 in the above algorithm), where it says:
The following [[Get]] internal method is used by GetValue when V is a property reference with a primitive base value. It is called using base as its this value and with property P as its argument. The following steps are taken:
Let O be ToObject(base).
[...]
Example:
Assume we have the expression
var foo = "BAR".toLowerCase();
This is an assignment expression which is evaluated as follows:
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
Let rval be GetValue(rref).
[...]
Step 1: The left hand side is evaluated, which is the identifier foo. How exactly identifiers are resolved is not important for this.
Step 2: The right hand side is evaluated, i.e. "BAR".toLowerCase(). The internal result of that evaluation will be a reference value, similar to:
REFERENCE = {
base: "BAR",
propertyNameString: "toLowerCase",
strict: false
}
and stored in rref.
Step 3: GetValue(rref) is called. The base of the reference is the value "BAR". Since this is a primitive value, ToObject will be called to convert it to a temporary String object. Furthermore, the reference is actually a property access, so GetValue will eventually call the method toLowerCase on the String object and return the method's result.
Javascript boxes the this argument provided to call and apply in non-strict mode. From MDN:
if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
The other answers provide detailed information about when autoboxing occurs, but here's a couple more things to remember:
Autoboxing does not occur when using the in operator, which throws a TypeError if the value received is not an object. A simple solution is to manually box the object with Object(value).
Some form of autoboxing occurs when iterating using for...of or the spread syntax [...value] which allows strings to be iterated.
Since ECMAScript 6, most function objects have a name property defined.
Now, if an anonymous function expression is assigned to a variable or is part of a property definition inside an object initializer, the identifier of the variable or the name of the property will be the value of the name property of the function object.
const a = function () {};
console.log(a.name); // a
const object = {
b : function () {}
};
console.log(object.b.name); // b
I don't have problems to understand the spec regarding the documented semantic of this behaviour, but I can't really see, why an assignment to a left hand side member expression as in the following example does not affect the name property of the function, which seems to be the case.
const object = {};
object.c = function () {};
console.log(object.c.name); //
As part of the member expression, there is obviously an indentifier which could (and should?) be used as the value of the name property. Even if the property name would be an expression inside brackets, this should be no problem, since using computed property names inside an object initializer does not prevent the name property of the anonymous function to be defined either.
It would be great, if someone could confirm that the observed behaviour is conforming to the spec and anyway, explain in short the specific semantics that apply to this syntax.
This first snippet is described under assignment operators:
e. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
i. Let hasNameProperty be HasOwnProperty(rval, "name").
ii. ReturnIfAbrupt(hasNameProperty).
iii. If hasNameProperty is false, perform SetFunctionName(rval, GetReferencedName(lref)).
When you assign to a MemberExpression, as in your last snippet, IsIdentifierRef(LeftHandSideExpression) is false and no conversion takes place.
If you search the standard for IsAnonymousFunctionDefinition you'll find a couple of other cases where this logic is used (object initializers, destructuring assignments).
IsIdentifierRef is defined twice (here and here), and both definitions boil down to "if an expression is IdentifierReference return true otherwise return false", where IdentifierReference is an Identifier (the yield stuff is for backward compatibility with non-strict code).
I am a newbie to js although years of experience with Java
I suppose when I declare a function,it is essentially a special type of object,and got some builtin fields that are accessible directly such as "arguments" and "length"
I notice I can access something like "arguments" inside the scope of a function
i.e.
function add(a,b) {
return arguments[0]+arguments[1]
}
Also I can access something like "length" outside the scope
//2
alert(add.length)
the above snippet should be the right way to use
however
function sum(a,b) {
// error
return length
}
// null
alert(sum.arguments)
I suppose arguments and length are not of the same basic, is it right idea?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After some research,I got the root cause of the confusion.
The issue revolves around property VS variable in JavaScript
Property belong to Object while Variable belong to Context.
The two ideas can be interchangeable sometimes:
The global context happen to be window
<script>
//property
window.foo="a"
//variable
var bar="b"
//a
alert(foo)
//b
alert(bar)
</script>
In most scenario,say,function context,they are completely different ideas,partly because you can never access the function object in your code.Thus,contrary to a global setting ,assign a property is impossible! What is possible is just declare a variable in scope
In my question
"arguments" is a variable
while "length" is a property
I failed to distinguish the two
For more information,please refer to
this post
Functions are objects in JavaScript. Proper, real objects.
length
The length property of a function is the number of declared arguments it has (this is also called the "arity" of the function). The arity (length) of add is two because you declared two formal arguments for it: a and b.
arguments
The arguments pseudo-array is not part of the function object. It's an object created when the function is called, and only in scope within that function call's context. It contains all of the arguments that the function was actually called with, which can be different from the number of arguments it declares. Each separate call to the function gets its own separate arguments object.
In JavaScript's "loose mode" (the only mode it had before 2009's ECMAScript 5th edition specification), there's a live connection between the arguments pseudo-array and the declared arguments:
// In loose mode only
function foo(a) {
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
a = 42;
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
}
foo(67);
In loose mode, that outputs:
a = 67
arguments[0] = 67
a = 42
arguments[0] = 42
In "strict" mode (which is the preferred mode to use), that link doesn't exist (we'd see arguments[0] = 67 at the end), which is useful for JavaScript engines for optimization purposes.
Yes, length and arguments are different concepts.
Contrary to Java, where fields (attributes) of your object, methods, and local variables are all accessible in the same scope (just via an identifier), JavaScript does distinguish heavily between scope variables (with closures, lexically referenced) and object properties (possibly inherited) - not only in concept, but also in syntax. Properties are always1 accessed via . or [] notation.
The .length of Function instances is such a property. You will always have to write add.length or sum.length (or …["length"]). If you just use length inside of the function, it doesn't automatically refer to the property - it's a variable reference, in this case to an undeclared one.
The arguments object in contrast works like a variable. It cannot be accessed as a property of the function2. Admittedly, it's a bit special, because you cannot see the declaration of the variable - it is created implicitly in every function scope. It works a bit as if there was a const arguments = {0: a, 1: b}; statement in the first line of the function.
1: Not exactly true, there's a with statement that explicitly lifts object properties into a scope. It's despised due to its ambiguity and deprecated in strict mode though.
2: There also is a deprecated .arguments property on functions, accessing it on strict mode functions throws. If not accessed during the invocation, it's null.
I'm writing a function that checks if arguments are zero, and it doesn't seem to work correctly. Note: Using Chrome as my browser, but this code should be cross-browser supported.
// check all arguments, and make sure they aren't zero
function zeroCheck(arg1, arg2) {
var i, argsLen = arguments.length;
for (i = 0; i <= argsLen; i += 1) {
if (arguments[i] === 0) {
// This is where it doesn't behave as I expected
arguments[i] = 1; // make arg1 = 1
}
}
console.log(arg1); // arg1 = 0
}
zeroCheck(0, 2);
I was expecting arg1 to be equal to 1, but it is still equal to 0.
From the ECMA-262 spec:
"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."
But if you read the technical details of how the arguments object is set I think you'll find it is based on how many arguments are actually passed to the function when it is called, not how many named arguments are declared, so using arguments and a loop to check the value of each named parameter might not work if they're not all passed in. Though in your case if you're testing specifically for 0 it should work since parameters that are not passed will be undefined rather than 0.
Having said that, exactly how the arguments object actually behaves depends on the browser. Chrome doesn't follow the spec.
Though some browsers appear to work the way you want (Chrome and Firefox), it isn't obvious to me from the ECMAScript spec that it will always be this way. It makes it sounds like the arguments array is probably just a reference to the named arguments in non-strict mode and it specifically says that the two should have no connection to one another in strict mode (in other words what you want to do is specifically NOT supposed to work in strict mode).
You can see in this jsFiddle http://jsfiddle.net/jfriend00/bG5xp/ that Chrome appears to implement it as the spec describes. There is linkage between arguments[0] and arg1 in non strict mode and there is no linkage between them in strict mode. A careful reading of the spec doesn't say that javascript is required to have linkage between the two in non-strict mode, but it does make it sound like it is likely. If you wanted to rely on that and you were sure you never needed your code to work in strict mode, then you would have to test a bunch of browsers to see if the behavior you desire is widely supported.
It is also not clear from the spec if the arguments array is always meant to be modifiable though that seems more likely given that it's implemented with a javascript object (not an actual array).
The safe way to modify the arguments array is to make a copy first and modify the copy. That will, of course, not modify any named arguments. You could modify those manually if you wanted to.
A common way to make a copy of the arguments array is:
var args = Array.prototype.slice.call(arguments, 0);
One generally uses either the arguments array or the named arguments and not both since any named argument is also in a known position in the arguments array so you don't really need to worry about a named argument changing value when you modify the arguments array.
Try it like this. arg1 with value 0 evaluates to false/falsy, so you can use this shortcut boolean evaluation:
function zeroCheck(arg1,arg2) {
arg1 = arg1 || 1;
console.log(arg1); //=> 1
}
zeroCheck(0,2);
A generic function to check for all arguments (returns an Array)
function zeroCheckArgs(args){
return [].slice.call(args).map(function(a){return a || 1;});
}
//more conservative
function zeroCheckArgsAlt(args){
var retArgs = [];
for (var i=0;i<args.length;i+=1){
retArgs.push(args[i] || 1);
}
return retArgs;
}
function some(){
var args = zeroCheckArgs(arguments);
console.log(args);
}
function someAlt(){
var args = zeroCheckArgsAlt(arguments);
console.log(args);
}
some(1,0,0,12,12,14,0,1); //=> [1, 1, 1, 12, 12, 14, 1, 1]
someAlt(1,0,0,12,12,14,0,1); //=> [1, 1, 1, 12, 12, 14, 1, 1]
It works for me, check this example fiddle
#nnnnnn -
"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."
Your citation actually answers my original question. The reason that my code as posted below does not work as expected is because I was actually using "strict" mode.
// check all arguments, and make sure they aren't zero
function zeroCheck(arg1,arg2) {
var i, argsLen = arguments.length;
for (i = 0; i <= argsLen; i += 1) {
if (arguments[i] === 0) {
// This is where it doesn't behave as I expected
arguments[i] = 1; // make arg1 = 1
}
}
console.log(arg1); // arg1 = 0
}
zeroCheck(0,2);
It worked in for xdazz, Jeroen Moons, and jFriend00 - because they did not include the strict:
http://jsfiddle.net/nSJGV/ (non-strict)
http://jsfiddle.net/HDjWx/ (strict)
Consider the following code
function add(x, y) {
alert(arguments.length);
var total = x + y;
return total;
}
add(); // NaN alerts 0
add(2,3); // 5 alerts 2
add(3,3,5); //6 alerts 3
Where is arguments defined? How come it is available inside my add function?
It is automatically created for all functions.
See https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments
A detailed explanation about when and how the arguments object is created:
From the ECMAScript specification:
10.1.8 Arguments Object
When control enters an execution context for function code, an arguments object is created and
initialised as follows:
The value of the internal [[Prototype]] property of the arguments object is the original Object prototype object, the one that is the initial value of Object.prototype (see 15.2.3.1).
A property is created with name callee and property attributes { DontEnum }. The initial value of this property is the Function object being executed. This allows anonymous functions to be recursive.
A property is created with name length and property attributes { DontEnum }. The initial value of this property is the number of actual parameter values supplied by the caller.
For each non-negative integer, arg, less than the value of the length property, a property is created with name ToString(arg) and property attributes { DontEnum }. The initial value of this property is the value of the corresponding actual parameter supplied by the caller. The first actual parameter value corresponds to arg = 0, the second to arg = 1, and so on. In the case when arg is less than the number of formal parameters for the Function object, this property shares its value with the corresponding property of the activation object. This means that changing this property changes the corresponding property of the activation object and vice versa.
That is just a standard feature of Javascript. Whenever any function is called, there is an arguments array automatically added to the local scope. This allows a function to receive a variable or unknown amount of parameters from the caller, and dynamically use these as necessary.
Commonly this is used when one function is placed as a wrapper around another where the exact parameters are unknown and arbitrary to the wrapper, who simply performs an action and then passes the provided arguments directly into the wrapped function.
arguments is a property of function objects. See Using the arguments object or Property: Function: arguments for more information.
It's worth noting that arguments is not a "real" array, the documentation calls it an "array-like object" - more in Turning JavaScript's arguments object into an array.