Following code fails:
array.map(String.prototype.toLowerCase)
Throws Uncaught TypeError: String.prototype.toLowerCase called on null or undefined. Indeed, this is not set, I got it.
But what's weird is that following code returns an array of empty strings without failing:
array.map((s) => String.prototype.toLowerCase(s))
Any idea why? Note that I know this is not the way of having an array of lowercased strings. I'm just wondering why these two approaches behave differently.
In other words, what is the difference between .map(String.prototype.toLowerCase) and .map((s) => String.prototype.toLowerCase(s))? I thought is was identical, but obviously, it behaves differently. Note that here, String.prototype.toLowerCase could be replaced by anything.
What is the difference between .map(String.prototype.toLowerCase) and
.map((s) => String.prototype.toLowerCase(s))
For the first case when you get the function outside of an object (String.prototype.toLowerCase) you lose your context, so you context is null and map tries to call on null or undefined. With the first solution you can't get the desired result, because you need to pass a context to the toLowerCase function, which must be each item of the array, but you don't get that each item of array.
For the second case you need to pass context which is the item to the String.prototype.toLowerCase via call function.
var array = ['ASD', 'BSD'];
var lowered = array.map(item => String.prototype.toLowerCase.call(item));
console.log(lowered);
The second approach array.map((s) => String.prototype.toLowerCase(s)) doesn't throw an error, because toLowerCase isn't taken out of context, i.e. the method still has String.prototype as its receiver.
String.prototype.toLowerCase(s)) returns an empty string, because the argument s is discarded. toLowerCase takes it value from its receiving object instead. The receiving object is String.protoype. To get the actual string the prototype must be converted to string. This happens with the String.prototype.toString method, which evaluates to "". Hence String.prototype.toLowerCase(s)) evaluates to "".
You can verify this behavior by changing the toString method:
String.prototype.toString = () => "FOO";
console.log(String.prototype.toLowerCase()); // "foo"
The diffrence between .map(String.prototype.toLowerCase) and .map((s) => String.prototype.toLowerCase(s)) is that .map((s) => String.prototype.toLowerCase(s)) takes an arrow function which is an anonymous function. According to definition of arrow function
"An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target."
An arrow function does not create its own this, the this value of the enclosing execution context is used.
using .map(String.prototype.toLowerCase) will not work as you are not passing any execution context to it but it's looking for one to execute.
For example in the below code
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| properly refers to the person object
}, 1000);
}
var p = new Person();
Please check this link
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
I think I got it finally:
first one has this set to undefined, equivalent to:
.map((s) => String.prototype.toLowerCase.call(undefined, s))
while second one has this set to String.prototype, equivalent to:
.map((s) => String.prototype.toLowerCase.call(String.prototype, s))
Now, the question is, why String.prototype.toLowerCase() is returning an empty string... But this deserves another question :)
Related
I have place a breakpoint into a callback where I'm supposed to run a search into an array of objects. The array.find statement returns the element I'm expecting, but if I try to assign this result to a variable I get 'undefined'
let resultCallback = function(data) {
var el = form.texts.find(text => {
text.options.name === this.options.name;
}, that);// breakpoint is here
//in console, form.texts.find(text => {text.options.name === this.options.name}, that); returns the element I"m looking for
// but el still is 'undefined'
};
I'm not sure I'm using any reserved variable names, but changing names around did not solve the issue. When looking into documentations, I see that I can assign the result of find to a variable, so I"m not sure what's going wrong here.
Could anybody help?
You have problem with the context. the context of this object you use belongs to resultCallback function, as it's not an arrow function. I don't know where this function is declared but try to change it to arrow function first. i.e.
let resultCallback = (data) => {
var el = form.texts.find(text =>
text.options.name === this.options.name, that
);
};
You're passing this object reference as second argument to find, which is unnecessary because when you call resultCallback function, you're not passing 'that' as parameter to the function. And also remove the curlies from arrow function so that it returns the result of comparison.
Your find function doesn't return anything, which by default means it returns undefined. Either lose the code block on lines 2-4, or use return statement before text.options.name === this.options.name;
When using arrow functions, return keyword is required if you define a block. Otherwise it will return the expression right after arrow.
var obj, method;
obj = {
go: function() { console.log(this); }
};
(method = obj.go)()
NOTE: Fyodor's first comment to his answer is what helped me the most. As the topic suggests, this was more about the parentheses than this.
In the last line, what I understand is that the parentheses will force the code inside to run first, so method takes the value of the go property, which is a function.
The () then calls that function, which logs window to the console because it wasn't called as a method.
If instead of (method = obj.go)(), you do method = obj.go(), it will first run the go function, and method will take the value returned by it. Since go returns nothing, it will be undefined. The value printed by go will be obj.
What I don't understand is, why if I do (obj.go)() the this printed is obj and not window?
Considering how the other code works, I expected this code to work kind of like this:
obj.go is evaluated first inside the parentheses and then the function is run as an IIFE (function() { console.log(this); })(). So since the function isn't called as a method of obj, the this defaults to window.
(method = obj.go)() evaluated in two steps.
method = obj.go is evaluated and method var become equal to function go of object obj. Functions in JavaScript can be called as methods or as functions, so in general it doen't metter how you;ve defined function go.
Then method() is called. As you didn't provided value for this (either by calling method function as method of some object or using bind or call it is called with this set to global object (in non strict mode) or to undefined (in strict mode)
When you call obj.go() this is set equal to obj (it is similar to use obj.go.call(obj) or method.call(obj)). If you call just method() this is equal to global object (similar to call obj.go.call(window))
EDIT
And to get obj as this with your example you can do such way
(method = obj.go.bind(obj))()
In this way you not only assign go function to variable method but create binding to specific this equal to obj
Good reading about function invocation and this in JavaScript
As per the Mozilla docs in order to execute a function using eval it must be wrapped inside ( ) i.e. if you don't use them then it treated as a string.
eval as a string defining function requires "(" and ")" as prefix and suffix
when I execute normal function it returns undefined as expected but not in the case of ES6 functions. My Question is ES6 functions are treated differently by javascript engines or only within eval function.
var fn = "function a(){}";
var es6fn = "()=>{}";
console.log(eval(fn)); // undefined
console.log(eval(es6fn)); // ()=>{}
console.log(typeof eval(es6fn)); // ()=>{} i.e. a function
Lets take a step back and see what is actually going on here. I think you are misunderstanding the point MDN is trying to make. The only function that is executed in your example is eval. The (...) the documentation is mentioning are not for executing the function inside the string but for changing how the function definition is evaluated.
A function call would function a(){}() but the docs talk about putting the function definition inside parenthesis: (function(){}).
There are basically thee major ways to define functions:
Function declaration
function foo() {}
Function expression
var foo = function() {}
Arrow function
var foo = () => {}
To understand the difference between a function declaration and a function expression, we have to understand the difference between statements and expressions (a declaration is basically like a statement).
A statement is something that has side effects and does not produce a value. if, for, switch, etc are all statements.
An expression is something that produces a value. E.g. 5 is a number literal that produces the value 5. 5 + 3 is an expression that computes the sum of the two literals, i.e. evaluating it will return the value 8.
A function declaration is like a statement. It doesn't produce a value itself, but as a side effect, a variable is defined whose value is a function (you can see in the specification that nothing happens when a function declaration is evaluated (they have already been processed at that point)).
A function expression is very similar, but instead of defining a variable, evaluating it simply results in the function object.
And that is the reason why
eval('function a() {}') // undefined, but a is defined as side effect
eval('(function a() {})') // a function object
produce different results. The first is interpreted as function declaration. A variable a will be created, but no value is created that eval could return. In the second case, the grouping operator ((...)) forces the function definition to be interpreted as a function expression, which means that a value is produced and return by eval.
Now regarding arrow functions: There is no ambiguity here. Arrow function definitions are always expressions, i.e. evaluating them always produces a value.
eval(`() => {}`) // a function object
To sum up
While there is a difference between arrow functions and function declarations/expressions, this difference is not the reason for the results you are seeing with eval. The difference here is because of evaling a statement/declaration and an expression.
In your example, a is declared as it's a named function. In the second case, you just write an expression which is a lambda function. See here and here.
If you want to get the same effect for fn, do
`console.log(eval("var a = function(){}"))`; //`function(){}`.
First of all, EcmaScript is the "official" name for JavaScript. Now that ES2015 is finalised, it effectively just becomes JavaScript v6 to most people. So, this difference does not come from the different engin.
The origin of the different behavior comes from the result of the string which is written in the eval function. the result of the first eval string is a function definition and there is not anything to return. On the other side, the second eval is evaluating a lambda function, so the result of the eval is that function. To clear this concept, you can rewrite the code like the following:
var fn = "function a(){ return 1;}";
var es6fn = "()=>{}";
console.log(eval(fn)); // undefined
console.log(eval(es6fn)); // ()=>{}
console.log(typeof eval(es6fn)); // ()=>{} i.e. a function
console.log(a()); // call the a() function
As, you can see, the a() is defined as a function, and you can use the function after the first eval. So, the first eval is run and all back to the return value for eval function.
The documentation says nothing on function execution. The functions won't be executed, unless they are executed explicitly like (() => {})().
The quote
eval as a string defining function requires "(" and ")" as prefix and suffix
refers to interpreting the string as function expression rather than function declaration.
// will throw SyntaxError
// because function declaration requires a name
typeof eval('function (){}');
typeof eval('function a(){}') === 'undefined';
typeof eval('(function a(){})') === 'function';
typeof eval('null, function a(){}') === 'function';
typeof eval('()=>{}') === 'function';
Arrow function is always an expression, it doesn't need auxiliary constructions like comma or parentheses to interpret is an expression, here is the difference.
According to this JavaScript reference:
The value null is a JavaScript literal representing null or an "empty"
value, i.e. no object value is present. It is one of JavaScript's
primitive values.
function getMax(arr){
return Math.max.apply(null, arr);
}
Wouldn't explicitly passing the keyword this be clearer, or at least more readable? Then again, at this point I may not understand why you would use null.
Why would you pass 'null' to 'apply' or 'call'?
When there is no value you wish to specify for the this pointer inside the function and the function you're calling is not expecting a particular this value in order to function properly.
Wouldn't explicitly passing the keyword this be clearer? Or at least
more human readable. Then again at this point I may not understand why
you would use null.
In your specific case, probably the best thing to pass is the Math object:
function getMax(arr){
return Math.max.apply(Math, arr);
}
While it turns out that it doesn't matter what you pass as the first argument for Math.max.apply(...) (only because of the implementation specifics of Math.max()), passing Math sets the this pointer to the exact same thing that it would be set to when calling it normally like Math.max(1,2,3) so that is the safest option since you are best simulating a normal call to Math.max().
Why would you pass 'null' to 'apply' or 'call'?
Here are some more details... When using .call() or .apply(), null can be passed when you have no specific value that you want to set the this pointer to and you know that the function you are calling is not expecting this to have any specific value (e.g. it does not use this in its implementation).
Note: Using null with .apply() or .call() is only usually done with functions that are methods for namespace reasons only, not for object-oriented reasons. In other words, the function max() is a method on the Math object only because of namespacing reasons, not because the Math object has instance data that the method .max() needs to access.
If you were doing it this way:
function foo() {
this.multiplier = 1;
}
foo.prototype.setMultiplier = function(val) {
this.multiplier = val;
}
foo.prototype.weightNumbers = function() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += (arguments[i] * this.multiplier);
}
return sum / arguments.length;
}
var x = new foo();
x.setMultiplier(3);
var numbers = [1, 2, 3]
console.log(x.weightNumbers.apply(x, numbers));
When the method you are calling .apply() on needs to access instance data, then you MUST pass the appropriate object as the first argument so that the method has the right this pointer to do its job as expected.
Calling apply with null as the first argument is like calling the function without providing any object for the this.
What does the apply method do?
The apply() method calls a function with a given this value and
arguments provided as an array (or an array-like object).
fun.apply(thisArg, [argsArray])
thisArg
The value of this provided for the call to fun. Note that this may not
be the actual value seen by the method: 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.
Further documentation can be found here.
One case where I have found this useful is when the function I'm calling is already bound to a particular context.
Because bound functions cannot be rebound, and they will always be called with the thisArg that was passed into bind, there is no use in passing a thisArg into call or apply. From source:
The bind() function creates a new bound function (BF).... When bound function is called, it calls internal method [[Call]] on [[BoundTargetFunction]], with following arguments Call(boundThis, args).
Here's an example:
class C {
constructor() {
this.a = 1;
}
}
function f(n, m) {
console.log(this.a + n + m);
}
let c = new C();
var boundF = f.bind(c, 2); // the context `c` is now bound to f
boundF.apply(null, [3]); // no reason to supply any context, since we know it's going to be `c`
I am bit late to answer this. I will try to give a long descriptive explanation here.
What is null in JavaScript?
The value null is a literal (not a property of the global object like undefined can be). It is one of JavaScript's primitive values.
In APIs, null is often retrieved in place where an object can be expected but no object is relevant.
fun.apply(thisArg, [argsArray])
thisArg: The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: 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.
argsArray: An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
If you are using 'strict mode', then it is advisable to pass the this or
Math as the parameter.
Apply is useful when you want to pass along the responsibility for doing something to a function that is determined at run time, and pass a variable number of arguments to that function. You may or may not have any appropriate "this" context when you're doing that.
For example I use a library I wrote to facilitate listening for and raising application events that uses apply.
I wanted to be able to be able to raise an event like this:
EventManager.raise('some:event-name', arg1, arg2, arg3, ..);
..and have all of the registered handlers for that event get called with that list of arguments (arg1, arg2, etc). So in the raise function, it goes through the handlers that are registered for that event name and calls them, passing all the passed in arguments except for the event name, like this:
var args = [];
Array.prototype.push.apply(args, arguments);
args.shift();
for (var l in listeners) {
var listener = listeners[l];
listener.callback.apply(listener.context, args);
}
When a registered handler (listener.callback) is called, apply is used to pass along a variable number of arguments. Here I have allowed the listener to supply a this context for its event handler when the listener is defined, but that context might not be defined or it might be null, and that's perfectly fine.
For a long time the raise function didn't even facilitate using any callback context. I eventually came across a need for it, so I put in support for it, but most of the time I don't really need or use it.
This question already has answers here:
Applying a Function to Null in Javascript
(5 answers)
Closed 7 years ago.
I am learning about call and apply in javaScript from a online TUT. This function allows more arguments to be passed, rather than having a fixed amount.
var calculate = function(){
var fn = Array.prototype.pop.apply(arguments);
return fn.apply(null, arguments);
};
What I am having difficulty wrapping my head around is this statement.
var fn = Array.prototype.pop.apply(arguments);
The presenter of the of the TUT, explained it as the following:
We are binding the apply method onto the arguments object. This is going to give us the Function Object and assign it to the fn variable. It will also remove the Function Object from the argumentsObject. Because the Array's pop method takes the final element in the array, it removes it from the Array and then assigns to what ever called the method. In this case the fn variable.
What confused me was the following:
We are binding the apply method onto the arguments object. This is
going to give us the Function Object
It will also remove the Function Object from the arguments
Object.
And when we write in the return statement:
return fn.apply(null, arguments);
Why are we including null?
Array.prototype.pop.apply(arguments);
When you have a function, there's automatically an arguments objects, which is an Array-like object of arguments. If you call this fake function:
someFunction('hello', 'world');
and someFunction looks like this:
function someFunction() {
console.log(arguments);
}
The console.log will output ['hello', 'world']. However, don't be confused... That is not an Array object! It is an "array-like" object. Therefore, you can't say arguments.pop()... because arguments doesn't have that method (it belongs to Array.prototype). However, normal Array objects do have access to Array.prototype (e.g. [1,2,3].pop() // => [1,2]).
When you say .apply(), the first argument is the context... It sets the this. So really, Array.prototype.pop.apply(arguments) is a clever way of mimicking arguments.pop(). But you can't do arguments.pop(), because it doesn't have a pop method.
In return fn.apply(null, arguments);, null is the first arguments because we don't need to set a new context for this example. arguments is the second arguments because it's being passed in to use with fn.
.apply() returns a function object, so it returns something like this:
function() { ... }
We can then later invoke that function.
By the way, .pop() mutates the original object (in this case, the array-like object arguments). So you're passing in arguments to fn, but it's missing the last item that was in it previously.
According to MDN:
Syntax
fun.apply(thisArg, [argsArray])
Parameters
thisArg:
The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: 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.
argsArray:
An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
The last argument passed to calculate is assumed to be a function. It is popped from the arguments list. (Using apply because arguments is not a real array.)
This popped function (fn) is called with the rest of the arguments list. (All other arguments passed to calculate). The arguments list no longer contains fn because pop() modifies the original object.
NULL is used because fn is called without a value for this. (See MDN)
If you call calculate for instance like
calculate(2, 3, function(a, b){ return a + b });
it will return 5.