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.
Related
I'm trying to understand this unexplained code from Eloquent Javascript :
function trackKeys(keys) {
let down = Object.create(null);
function track(event) {
if (keys.includes(event.key)) {
down[event.key] = event.type == "keydown";
event.preventDefault();
}
}
window.addEventListener("keydown", track);
window.addEventListener("keyup", track);
return down;
}
var arrowKeys =
trackKeys(["ArrowLeft", "ArrowRight", "ArrowUp"]);
I think I understand how the inner function track will maintain a reference to down and keys because it is a closure, but I'm lost at return down;
Will that return value be a reference to the exact same down the callbacks are accessing? I don't understand; I thought local variables could only be accessed by inner functions?
Follow-up: what if down were a primitive data type and not an object, would this still work?
The function initializes the variable down as an object.
So within the function, down is a variable that points to a certain object.
When the function returns down, it returns a reference to the object, meaning that the object becomes accessible through the return of the function.
If down were primitive, then this would not work since the function would only return a primitive value rather than a reference to an object.
TLDR: Saying "a variable is returned" is ambiguous due to the inherent imprecision of human language. It does not mean that the variable-pairing of identifier and value is returned. It means the value of the variable is returned. In this case the value of the reference to the object (created by Object.create(null)) is returned.
Will that return value be a reference to the exact same down the
callbacks are accessing?
Yes.
I thought local variables could only be accessed by inner functions?
A variable is a pairing of an identifier (ie. the name of the variable) and a value.
Scope is the region of code over which an identifier (in this case "down") has a specific meaning.
The scope of an identifier is determined by the way in which the variable is declared (const/let/var/implicit in non-strict mode). Variable down is declared using let; its scope is therefore the nearest containing block, which is the function trackKeys.
Functions declared inside trackKeys will close over down via the built-in closure-forming mechanism of JavaScript, and will therefore share the meaning of the identifier "down".
Note: if inner functions or blocks also declare their own variable named "down" these inner variables will either result in an error (var in a non-function block in strict mode, duplicate declaration), do nothing (var in non-strict mode in a non-function block), or shadow (hide from view) the outer variable (let/const).
Returning down does not change the scope of the identifier down. Trying to refer to "down" outside trackKeys will continue to error or return undefined depending on whether you are in strict mode. Returning the value of down does, however, make this value available outside trackKeys.
I'm currently studying javascript by following the "you dont know js" series.
In section "this & object prototype", the author came up with an way to soft bind this.
However, I am extremely confused by the code. So I was wondering if someone could kindly explain it to me, steps by steps, what the code really does?
//step 1: if "softBind" property does not exist on `Function.prototye`
if (!Function.prototype.softBind) {
//step 2: create a property named "softBind" on "Function.prototype" and assign to "softBind" the following function
Function.prototype.softBind = function(obj) {
//step 3: what is the point of assigning "this" to the variable "fn"?
//what does "this" represent at this point in time?
var fn = this,
//step 4: I understand that "arguments" is an array-like object, i.e. "arguments" is not a true array.
//But you can convert "arguments" to an true array by using "[].slice.call(arguments)".
//The thing I dont understand here is, why the 1?
//I understand it tells "slice" method to start slicing at index 1, but why 1?
//And what is the purpose of "curried" variable?
curried = [].slice.call( arguments, 1 ),
bound = function bound() {
//step 5: I understand what "apply" function does
return fn.apply(
//step 6: I dont really understand how "!this" works.
(!this ||
//step 7: utterly confused...
(typeof window !== "undefined" &&
this === window) ||
(typeof global !== "undefined" &&
this === global)
//step 8: if the above statements evaluates to "true", then use "obj",
//otherwise, use "this"
) ? obj : this,
//step 9: can't I write "curried.concat(arguments)" instead?
//why the convoluted syntax?
curried.concat.apply( curried, arguments )
);
};
//step 10: Why assign the "fn.prototype" as the prototype to "bound.prototype"?
bound.prototype = Object.create( fn.prototype );
return bound;
};
}
I'm very sorry for the long question, but I thought instead of dividing the question into several posts, it is more convenient if they are put into one place.
step 3: what is the point of assigning "this" to the variable "fn"?
The value of this holds a pointer to the currently executing function object. Only function objects can be held, so only things actually created through new() or equivalent notation. This can be useful for passing a reference to an outer object to an inner object created within said outer object.
Here's a minimal example:
function fn1() {
var x = this; // x = fn1.
this.u = 5;
function fn2() {
this.u = 10;
console.log(this.u); // Prints the member of fn2.
console.log(x.u); // Prints the member of fn1.
};
var W = new fn2();
}
var V = new fn1();
The output should be:
10
5
First, an object of type fn1 is created called V. It has a member variable u holding the value 5. Then, we create an object of type fn2 called W within fn1. It also has a member variable u, but here it holds the value 10. If we wanted to print the value of V.u within W, then we need a pointer to V. Calling this.u within W would output its u value (10), which is not what we want. So we define a variable x within the scope of the class fn1, holding the this pointer for us. Now it's possible to access the members of fn1 within fn2.
Step 4
The first argument is the object being bound to. You don't want to pass that to the function being bound, that would break its functionality, for it does not expect an extra argument prepended to its normal list of arguments. So, the first argument has to be removed.
step 6: I dont really understand how "!this" works.
!this is simply a way of checking whether this is defined. If it is not, then the value will be true. Otherwise, since this
would be an object (which evaluate to true when cast to a boolean), then it is false.
step 7: utterly confused...
Here, the original author checks if this is equal to either window, or global. Note; In modern browsers, checking for just window is enough, but IE exists (as do non-browser javascript environments). So, the full statement evaluates to this thing:
If I am not called from within an object, or if I'm called from the object window or global, then return the object softbind was created with. Otherwise, return the object I was called from
Note that this is exactly what the author of the original article wants. When a library function is called with this special binding, then we can be sure that whatever the library does; it can't access the global context through the use of the this variable. But, it can access any other object, allowing you to interface with the library.
step 9: can't I write "curried.concat(arguments)" instead?
Curried holds all arguments the original softbind function was called with, except for the first argument. arguments , at this point, is not equal to arguments in the earlier call. Here, it refers to the arguments the bound function is called with, not those it was bound with. This line integrates the two sets of arguments, allowing you to provide default arguments. The trick used here is to concatenate the arguments, e.g. Suppose your function has default arguments [1,2,3,4] and you supply [5,6]:
[1,2,3,4].concat([5,6]) produces [1,2,3,4,5,6].
Why not simply concatenate, and use the prototype? Arrays are passed by reference in javascript, so this will keep curried the same, while concatenating arguments to the call. Equivalently, you could write this:
curried2 = curried.concat(arguments);
return fn.apply(
(.....)
curried2);
Admittedly, the terseness does not help the understandability of this example. Simply re-naming arguments to calledArguments and curried (an advanced math term not relevant to the explanation) to be defaultArguments and using a simple for loop over each argument would be far easier to understand, if a little more verbose. I guess the author wanted to be fancy.
step 10: Why assign the "fn.prototype" as the prototype to "bound.prototype"?
Go up a bit on the article to the part where the author talks about the default bind function and how it works: basically, the end result of replacing the prototype back with the default prototype during the function call means that when your softbind enabled function is called with the new operator this will be set to itself, rather than the default bound object. prototype will not work when simply calling the bound function.
It also enables inheritance, meaning that creating things for a softbind enabled function using its prototype will not have that prototype be overruled by that of softbind when it is bound. (That would make softbind incompatible with prototypes). Instead, both prototypes are used.
Also see this reddit post.
A word of warning
We're extending the language with new features here. Features that aren't exactly necessary, and are largely concerned with semantics. If you're just interested in learning the language, this really goes way too far, you don't exactly need special binding semantics. Worse, it can be confusing if this does not behave in a way you expect it to.
A simpler alternative
Enable strict mode. Now this will default to undefined whenever it points to the global object. Prevents the problem this convoluted code is trying to solve (by usually resulting in errors from functions trying to access member variables or functions of undefined), while at the same time being far easier to use, and at the same time it will complain about a lot of syntax that is valid regular javascript, but a bug in any normal use-case. Also see the MDN article about it. It will catch a lot of potential errors for you instead of silently doing nonsensical things.
Another alternative
bind tries to resolve 'losing' the object when you pass a member function of it to another function, such as setTimeout. Another way of doing it is by using an anonymous function. Instead of using (soft) binding, assuming obj is an object holding a function fn being passed a parameter param;
setTimeout(obj.fn(param), 500);
You can use:
setTimeout(function(param){obj.fn(param);}, 500);
Which avoids the problem through a layer of indirection by passing an anonymous function. Also see this question.
SPOILER
I'm trying to solve problem #8 of this Javascript injection game.
In one of the comments by Erling Ellingsen, I found this interesting snippet.
(_=[].concat)()[0]
What's the difference between the above snippet, and this,
([].concat)()[0]
What changes when you assign [].concat to a variable? Clearly, he's just trying to access the global window object, but how do these two evaluate differently?
The reason why this previously worked was because it was specified in a previous version of ECMAScript that the this value would be the global object (i.e. window). However, from ECMAScript 5 onwards, the this value is now undefined, which make Array.prototype.concat throws an error.
In ES3, if a native function got called with a this value of undefined or null (such as when you call it using func()), it would provide the global object to the function.
In ES5, the behaviour of native functions has been changed so that it gets the actual undefined or null value, even though your code is not in strict mode.
The difference between the two is that one is the value of the function, and therefore is indirectly called, while the other is a reference to the function.
GetValue() is an internal function that gets the value of a reference from a variable - this is not called when calling "directly", but when assigning to another variable and using the result of that, it is indeed called (source).
An infamous example of the difference between the two is when using eval():
var a = 0;
function test()
{ var a = 1, b;
console.log(eval("a")); // 1
console.log((b=eval)("a")); // 0
}
test();
In your example though, it works like the following:
var a = [].concat;
// called as variable, not property
a(); // therefore it's global
// same as the following, as all these expressions call GetValue() interally
(0, [].concat)();
(random = [].concat)();
([].concat || 0)();
// but this doesn't work
[].concat(); // `this` is the new array, not global object
I have heard different opinions about the usage of var at a situation like this:
function(maybeUndefined) {
if(typeof maybeUndefined === 'undefined')
var maybeUndefined = 'bob';
}
Is it necessary to notate var, or not, because maybeUndefined is an argument of function?
You do not need the var in this case, as mayBeUndefined is already allocated within the scope of the function (hint: listing argument variables in a function definition causes those variables to be declared locally). That var is therefore completely optional, though completely pointless (and a drain on readability).
Example:
function func ( arg1, arg2 ) {
var local1, local2;
function nested1 () {}
function nested2 () {}
// other code
}
Here we have a function declaration. When this declaration is parsed into a function object, a lexical environment (= scope) is created for that function with the following bindings:
arg1
arg2
local1
local2
nested1
nested2
this
arguments
(Notice how there also are two special, built-in bindings: this and arguments. These are always created for all function objects.)
These names are defined as local bindings. (This process is specified in "Declaration binding instantiation". Warning: this algorithm is not meant to be read by humans :-)) Therefore, when a name is defined as a parameter, it is not necessary to declare it as a local variable. This mechanism is independent of whether a value (argument) is passed for that parameter when the function is invoked.
So, even if you invoke the function like so:
func(123);
the name arg2 will still be defined (as a binding in the function's environment), although its value will initially be undefined for that particular invocation.
Btw, if you use the strict language (recommended!), function environments are static which means that the above bindings are garanteed to be the only bindings in the function's environment. The default language, on the other hand, provides certain mechanisms to, dynamically, add/remove bindings from the function's environment. Example:
(function () {
// the name "temp" does not exist as a binding in the function's environment
eval('var temp');
// now it does
delete temp;
// and it's gone again
}());
You should not use var again, it is bad for readability, and the variable will already be scoped locally as a result of being an argument.
Also, you should note that it is not a part of this. this will only be scoped to the function object if the new keyword has been used, and as you do not have a named function, that seems unlikely in this case. Without new, this refers to window (or is undefined if use strict; is used), of which your variable is definitely not a part of as a result of the argument having a local scope.
Interfacing
Including a function argument is effectively the same as scoping a variable (in other words, it's effectively the same thing as defining a function-level reference using the var keyword). The main reason for providing function arguments (in JavaScript) is for your own interfacing preference.
The arguments object
Arguments may still be passed to functions without parameters, and will still be accessible in the 'hidden' arguments object -- which is sort of a "pseudo-array" (if you will), in that it is functionally an array, but is not equipped with the same APIs JavaScript equips the Array (pseudo-type) with:
// The following functions do the same thing, but one is "more readable"
function foo() {
return arguments;
}
function bar(baz, qux) {
return arguments;
}
Evaluation (interface) vs Execution (implement)
When both functions are evaluated (on file 'load'), the arguments object is undefined in every function definition; the object doesn't become "defined" until the function body executes the code therein; to visualize that using pseudo-code, it'd look something like this:
// Function bodies (as objects)
foo : {
arguments : new Array // [undefined]
__proto__ : Empty() // the super-object that allows this object to inherit "functionality"
}
bar : {
arguments : new Array(baz, qux) // [undefined, undefined]
__proto__ : Empty()
}
Function invocation
So when you invoke a function, it "implements" or "executes" its body (its "object"). When it does that, if the objects that have been pushed into the arguments object are defined, then the function can reference them. If not, a reference error will be thrown, logging that the variables are undefined in that scope.
In short:
It isn't necessary to interface function-scope-level variables (aka "private members") using var because the language already attaches the arguments object to all function body objects.
More reading:
JavaScript Memoization: "Function-caching" multiple arguments for better performance:
http://decodize.com/javascript/javascript-memoization-caching-results-for-better-performance/
We have received some JavaScript from an agency that looks wrong, but works.
For some reason they are adding square brackets ([, ]) around variables, like this:
var some_variable = 'to=' + [other_variable];
This works, but the square brackets seem completely superfluous.
Is there a purpose of using this syntax or is it technically incorrect, but ignored by the browser?
Just in case anyone else arrives here while trying to find out what some weird/new syntax involving [square brackets] (seen in someone else's Javascript) might possibly be, like I was...
Nowadays, with ES6, we also have [] used on the left-hand side for destructuring arrays, e.g.
const names = ['Luke', 'Eva', 'Phil'];
const [first] = names;
console.log(first); // 'Luke'
const [first, second] = names;
console.log(first, second); // 'Luke' 'Eva'
For further info see http://www.deadcoderising.com/2017-03-28-es6-destructuring-an-elegant-way-of-extracting-data-from-arrays-and-objects-in-javascript/ or google 'es6 destructuring'.
Square brackets means new Array.
var ar=new Array("a","b");
var ar=["a","b"]; //Equal to the syntax above
in that situation there's no difference if you use square brackets or not because if it's an array it is converted to string, but if you delete the brackets it takes less time because it doesn't have to build a new array and convert it but it works with a simple string.
Even without changing the Array prototype there are differences:
var other_variable;
var some_variable = 'to=' + [other_variable];
If other_variable is undefined, the return value with the array is "to=".
Without the array, the return value is "to=undefined".
b = "bar" + ["foo"]
This is syntactically correct, but indeed very, very, superfluous. This is how it works:
["foo"]
JavaScript takes the string "foo" and converts it to an array with one element, "foo":
"bar" + ["foo"]
when + is used, and one of the operands is a string, "bar" in this case, JavaScript converts the second one to a string. Since operand two is an Array, the Array.toString method is called, which, by default, returns all elements joined by a comma. We have one element, and the result will be equal to this element, i.e., in this context "foo" is equivalent to ["foo"].
If you redefine Array.toString you can see better what's going on:
alert("bar" + ["foo"])
Array.prototype.toString = function() { return "???"; }
alert("bar" + ["foo"])
I bet someone told that person:
"Do the string concatenation with an array, it's faster!"
Meaning:
var some_variable = ['to=', other_variable].join("");
Which is apparently faster for lots of concatenations, but totally irrelevant, as that code will probably run just once anyway. Premature_optimization = sqrt(all_evil)!
And the poor chap did that other irrelevant thing...
I love people.
It's possible to construct a situation where this:
var some_variable = 'to=' + other_variable;
and this:
var some_variable = 'to=' + [other_variable];
produce different results. Specifically, if the Array.prototype.toString() method (or, I suppose, the Array.prototype.join() method) has been changed from its default, anything could happen. For example, extra functionality could be added to the Array.prototype.toString() method to generate logging information, do some lookups, or anything really.
The likelihood that this has been done is slim, I'd imagine, but it needs to be mentioned for completeness.
May be this ..
Global Variable access with the Square Bracket Notation
The square bracket notation requires that there be some sort of object reference to the left of the brackets.
["document"] //Array literal, not a Property Accessor!
-will produce an error if an attempt is made to assign a value to it, as it will be treated as an Array literal, if an attempt to read from it is made the one element array containing the string within the brackets is returned. Global variables are normally referenced by their one identifier alone. This would seem to exclude global variables from the possibility of being referenced using a string that held their identifier name or an expression that built, or returned, their name. However, javascript global variables (and global function names for that matter) are properties of a global object. Any identifier that holds a reference to the global object can be used to the left of the square brackets to form a property accessor that refers to a global variable.
In a web browser the global object is the window (or frame) in which the script is running. Each window (or frame) object contains a number of properties, at least two of which are references to the window (global object) itself. These properties are 'window' and 'self'. These property names can be used as the identifier to the left of the square brackets when referring to global variables. So given a global variable defined as:-
var anyName = 0;
that global variable can be referenced as:-
window["anyName"]
As with any other use of the square bracket notation, the string within the brackets can be held in a variable or constructed/returned by an expression.
Code that is executing in the global context, the code within global functions (except Object constructors invoked with the new keyword) and inline code outside of any functions, could also use the this keyword to refer to the global object. The this keyword refers to an object depending on the execution context. For code executing in the global context this is the global object (on a web browser, the window object). As a result, the above variable could be referred to as this["anyName"], but only in code that is executing in the global context.
However, using the this keyword is very likely to be confusing, especially in scripts that include custom javascript objects where the methods (and constructors) of those objects would be using this to refer to their own object instances.
Some javascript implementations do not have a property of the global object that refers to the global object. Rather than trying to use the this keyword to access global variables it is possible to create your own global variable that refers to the global object.
var myGlobal = this;
executed as inline code at the start of a script will assign a reference to the global object (this in that context). From then on all global variables can be referenced with square bracket notation as:-
myGlobal["anyName"];
and expect myGlobal to refer to the global object from any execution context.