Original source: http://twitter.com/tobeytailor/status/8998006366
(x=[].reverse)() === window // true
I've noticed that this behavior affects all the native types. What exactly is happening here?
This is to do with the weird way this binding works in JavaScript.
[].reverse
is the method reverse on an empty list. If you call it, through one of:
[].reverse();
[]['reverse']();
([].reverse)();
then it executes with this bound to the list instance []. But if you detach it:
x= [].reverse;
x();
it executes with no this-binding, so this in the function points to the global (window) object, in one of JavaScript's worst, most misleading design mistakes.
(x=[].reverse)()
Is also doing the detach. The assignment operator returns the same function object it was passed so it looks like it's doing nothing, but it has the side-effect of breaking the limited special case that causes JavaScript to bind this.
So you are saying:
Array.prototype.reverse.call(window)
reverse, like many other Array.prototype methods, is defined by ECMAScript to work on any native sequence-like object. It reverses the items with number-string keys (up to object.length) and returns the object. So it'll return the object that was passed in for any type that has a length property.
window has a length property, which corresponds to window.frames.length, so calling this method with this pointing at window will work and return the window. In theory it may still fail, because:
window is allowed to be a “host object” rather than a “native object”; in this case the guarantees about what you can pass to other prototypes' methods don't necessarily apply; and
if the window actually has frames/iframes, it would try to reverse their order, which wouldn't work because the frame collection is read-only.
However, in current browsers the former case does work and the latter fails silently without an error, so you still get the ===window behaviour and not an Exception.
Related
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 23 days ago.
I would expect [1].some([1].includes) to return true, instead I get an error saying:
Uncaught TypeError: Cannot convert undefined or null to object.
Any ideas what would cause this? As I understand, Array.some accepts a function invoked per array item, which [1].includes should fulfill.
You lose the context when you pass that function as is: when it's invoked as a callback, value of this inside it is undefined in strict mode (and global object in non-strict mode), not [1]. To address this issue, you may fix the context:
[1].some([].includes.bind([1]))
Note that it doesn't matter which array is used to access includes function; you might as well write that as...
[1].some( Array.prototype.includes.bind([1]) )
That's be a bit less concise, but a bit more efficient (as no immediate array is created). Still, it almost never should be a bottleneck; thus you should better optimize for readability.
Unfortunately, this won't be enough. See, Array.includes() uses two parameters:
arr.includes(searchElement[, fromIndex])
... and Array.some() does supply it with those two (even three in fact, but only two are used by includes). That's why this...
[1,2,3].some([].includes.bind([1])); // true
... works, but this...
[2,1,3].some([].includes.bind([1])); // false
... doesn't: the lookups in [1] array start from 0th, 1st, 2nd elements - and apparently fail after the first one.
To fix this, you might either create a function that takes exactly one argument with something like lodash's _.unary:
[2,1,3].some(_.unary([].includes.bind([1]))) // now we're talking!
... or bite the bullet and use arrow function instead. Note that you can still use a function with a bound context here:
const checker = [].includes.bind([1]);
[2,1,3].some(el => checker(el));
... to make this more flexible.
It depends on the specific implementation of includes in whatever JS engine you are using, but usually the standard library functions are not very co-operative for point-free style programming.
Generally this is because the context (this) is not assigned as intended. This can be shown by trying the following:
[1].some([1].includes); // Error
[1].some([1].includes.bind([1])) // true
Edit: This answer is not entirely correct. You should probably read rain77ow's answer above
I was wondering if it is safe to assume that in JavaScript a variable will always have a "constructor" property if its value is not "null" or "undefined".
I ran into a situation where I had to check if a variable is defined and that if it is an array and if so check if its length is > 0:
if(variable && variable.constructor === Array && variable.length > 0) {
...loop through the array
}
Am I right to assume that variable will always have a constructor if it is not "undefined" or "null"?
Your if statement won't throw because you've made that assumption, because anything that passes your first check will support your second, even if there is no constructor property (you'll just get undefined), and in fact even if it's not an object (it'll get temporarily promoted). So in that sense yes, you can happily do what you're doing — unless you could be dealing with an array from another window, in which case the === Array will fail (as dandavis pointed out) because different windows have different instances of the Array constructor.
Note that this is not the same as every object having a constructor property. It's just that if the object doesn't, you'll get undefined, rather than an error. (You can get an object with no constructor property by creating an object with no prototype: var obj = Object.create(null); or by using an object whose prototype is null as the object's prototype.)
However, somewhat tangentially, I wouldn't use that mechanism to check whether something is an array. In any modern environment, I'd use Array.isArray; and I'd shim/polyfill it on older environments. This also has the happy effect of working with arrays from other windows.
undefined or null do not have constructors and will error if you try to get it. Just try it out on Ctrl+Shift+I, and play around with it. Though, I doubt your if statement would cause much trouble, and it'd most likely error on pratical use.
You do NOT need to check length or anything, only see if it exists (or if you want specifically array, you could check that aswell).
I tried googling but couldn't find a precise answer, so allow me to try and ask here. If the question does not seem proper, please let me know and I'll delete it.
In JS you've got three different way of writing certain build in functionalities:
str.length
str.toString()
parseInt(str)
I wonder if there is a reason behind these different ways of writing. As a new user I don't grasp why it couldn't be streamlined as: length(str) / toString(str) / parseInt(str) or with dot formulation.
I however think if I do know the reason behind these differences, it would give me a better understanding of JavaScript.
Length is one of the attributes of string in JavaScript. Hence you use string.length to get the length of the string.
toString is a function for string objects, hence we use stringobj.toString().
parsInt(str) is a global function which takes string as a parameter.
JavaScript is object-oriented, so there are functions or procedures which require first an object to use as this in their bodies. str.length is a property, both syntactically and semantically. It doesn't require any parameters and represents some quality of the object. obj.toString() is a method (a function attached to an object), which doesn't represent any characteristics of the object, but rather operates on its state, computes some new values, or changes the state of the object a lot. parseInt(str) is a "global" function, which represents an operation not attached to any type or object.
Under the hood, these three ways may be well implemented with just calling a function, passing this as the first parameter (like C# does, for example). The semantic difference is the important one.
So why not use just the third syntax, like for example PHP does? First, it doesn't bloat the global environment with lots of functions which only work for one specific case and type, allowing you to specify any new function you want without breaking the old functionality. Second, it ecourages you to use object-oriented concepts, because you can already see working objects and methods in the language, and can try to make something similar.
And why isn't parseInt a method? It can as well be str.toInt() without any issues, it's just the way JavaScript designers wanted it to be, although it seems also a bit logical to me to make it a static method Number.parseInt(str), because the behaviour of the function is relevant more to the Number type than the String type.
JavaScript is based around objects. Objects have properties (e.g. a User object may have name and age properties). These are what define the user and are related to the user. Properties are accessed via dot-notation or brackets notation (to access Eliott’s age, we’ll use either eliott.age or eliott['age'] — these are equivalent).
These properties can be of any type — String, Number, Object, you name it — even functions. Now the proper syntax to call a function in JS is to put round brackets: eliott.sayHello(). This snippet actually fetches Eliott’s sayHello property, and calls it right away.
You can see Eliott as a box of properties, some of which can be functions. They only exist within the box and have no meaning out of the box: what would age be? Whose age? Who’s saying hello?
Now some functions are defined at the global level: parseInt or isNaN for instance. These functions actually belong to the global box, named window (because legacy). You can also call them like that: window.parseInt(a, 10) or window.isNaN(a). Omitting window is allowed for brevity.
var eliott = {
name: 'Eliott',
age: 32,
sayHello: function () { console.log('Hello, I’m Eliott'); }
};
eliott.name; // access the `name` property
eliott.age; // access the `age` property
eliott.sayHello; // access the `sayHello` property
eliott.sayHello(); // access the `sayHello` property and calls the function
sayHello(eliott); // Reference error: `window.sayHello` is undefined!
Note: Some types (String, Number, Boolean, etc.) are not real objects but do have properties. That’s how you can fetch the length of a string ("hello".length) and reword stuff ("hello, Eliott".replace("Eliott", "Henry")).
Behaviour of these expressions is defined in ECMAScript grammar. You could read the specification to understand it thoroughly: ECMAScript2015 specification. However, as pointed out by Bergi, it's probably not the best resource for beginners because it doesn't explain anything, it just states how things are. Moreover I think it might be too difficult for you to be able to grasp concepts described in this specification because of the very formal language used.
Therefore I recommend to start with something way simpler, such as a very basic introduction to JavaScript: JavaScript Basics on MDN. MDN is a great resource.
But to answer your question just briefly:
str.length is accessing a property of the str object.
parseInt(str) is a function call
str.toString() is a call of a function which is a property of the str object. Such functions are usually named methods.
Functions and methods are in fact very similar but one of the differences (except for the obvious syntax difference) is that methods by default have context (this) set to refer to the object which they're part of. In this case inside of toString function this equals to str.
Note: Accessing a property (as in str.length) could in effect call a getter function but it depends on how the object is defined, and is in fact transparent for the user.
Is it acceptable to add an attribute or value to a JavaScript function?
Example:
var f = 1;
function foo (param) {
f++;
}
var fooFunc = foo;
fooFunc.dummy = f;
console.log('fooFunc: ' + fooFunc);
console.log('fooFunc.dummy: ' + fooFunc.dummy);
The above example creates a function (foo), then assigns it to a new variable (fooFunc) and then adds a dummy attribute to fooFunc.
When run, this example prints the text of the function first, and then it prints the expected value (1 in this case). When printing the function, it doesn't show any indication of the dummy value:
fooFunc: function foo(param) {
f++;
}
fooFunc.dummy: 1
JsFiddle here - open the browser's JavaScript console to see the log messages: http://jsfiddle.net/nwinkler/BwvLf/
Why does this work? And where is the dummy attribute stored, and why isn't it printed when I log the function?
Lastly, even if this works, is it a good idea (or an acceptable practice) to use this? I don't want to start an open ended discussion on this, but rather see if there's documented uses of this, or people discouraging this in JavaScript coding guidelines.
Everything except primitives ( null, undefined, number, string, boolean ) in JavaScript are objects. So functions are basically objects.
Objects in JavaScript can have properties and methods, hence functions too.
all functions inherit from Function.prototype and has certain properties ( name, length ) and methods ( .call, .apply ) coming through this chain.
It is sometimes very useful to keep properties attached to the function itself, like cache information, number of invocations etc. There is nothing wrong out in using it this way.
More details : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
Let's have a look at ECMAScript documentation (Which is the standard JavaScript is based on). Here's the 3rd. version of it:
http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf
Go to chapter 15, Native ECMAScript Objects.
15.3 > Function objects.
There's a lot of interesting information there concerning your question, but the first thing worth noticing is that function is an object.
As an object, it has attributes (predefined and that you can assign yourself).
For example, try:
console.log('fooFunc.name: ' + fooFunc.name);
It should display "foo" in your case.
Since it's documented quite well, you can use it as a standard way, though it is not so well-spread and may seem a bit unusual.
Hope this helps.
It is normal object behavior, whether "acceptable" or not.
By using the function keyword you are actually calling the native predefined Function() constructor. Like any object constructor it returns an object after building it. Like any object, the returned object can have properties, including other functions as method properties.
var adder = function(a, b){return a+b};
adder.subtracter = function(a, b){return a-b};
console.log(adder(1,2)); // 3
console.log(adder.subtracter(1,2)); // -1
TIP: if you want to see the adder object and its subtracter method, switch to DOM view from Console view after running the above code in console and then search for "adder". You'll see the object there, and then you can collapse to see what it's made from, including a subtracter object.
Of course, a function object is a special native object, which makes it possible to make calls like this: adder() and actually run some code. The fact that a function object is harder to inspect for custom attached properties, combined with its native special object treats (read built-in restrictive behavior), should give you a hint that, while it's possible, attaching custom properties is not the intended nor a good use of a function object.
var test=(a=1,[b=2]["sort"])();
This code works in Firefox resulting test=window (window object),
Is it valid JavaScript code? (I failed to find it in JavaScript references)
It's "valid" but looks completely pathological to me. From the name of the var, I'd guess someone came up with this as a feature test at some point, but failed to add a comment explaining why.
So here's what it's doing. First, the two assignments will resolve to the assigned value, so we can replace them (they do assign variables, which is a side effect, but that doesn't affect the evaluation of this expression):
var test=(1, [2]["sort"])();
["sort"] is just .sort:
var test=(1, [2].sort)();
The comma operator will return the last value in the brackets, so we can lose that 1:
var test=([2].sort)();
So now the bracketed part is creating an array with the number 2 in it, and finding the sort method of that array. It then calls that method, but because of the first set of brackets it calls it without a specified context.
In non-strict mode, a function called with no context gets window as its this.
So it tries to sort window and returns the result, which is window, as you saw.
In strict mode, which the JS consoles in Firebug and Chrome are, functions called without context get undefined as their this, which means this example throws an error, as mplungjan noted above. https://developer.mozilla.org/en/JavaScript/Strict_mode
I would expect that code to give an error.
The comma basically evaluates the expression on the left, a=1 and then the expression on the right [b=2]["sort"] and returns the result of the expression on the right.
a=1 sets a to 1, creating a as a global if it's not in the current scope.
[b=2] sets b to 2, creating b as a global if it's not in the current scope, and also creates a one-element array with the value 2.
[b=2]["sort"] returns the array .sort method (it does not call the method).
So the result of the expression in parentheses is the array .sort method which is then executed by the final (), and the result would be assigned to test except that it doesn't work because by then it is not actually called on an array.
The final assignment is the equivalent of this: var test = ([2].sort)();.