Please look at the code below and explain: what am I doing wrong?
function doStuff(a, b){
return a + b + 1 ;
}
var myContext = {
c: 1,
d: 3
};
// myContext = this (result => 5)
doStuff.call(myContext,myContext.c,myContext.d)
// ... so why doesn't the below work? (result => NaN)
doStuff.call(myContext,this.c,this.d)
// To make the above work, i must replace "this" with "myContext" (result => 5)...
doStuff.call(myContext,myContext.c,myContext.d)
// ...which is no different to...
doStuff(myContext.c,myContext.d)
// ...so what was the point of call() method?
Am I being thick?
The main point of call is to set the value of this within the function. Since your doStuff doesn't use this within the function, using call with it is pointless.
Here's an example where it matters:
function doStuff(a, b) {
return this.sum(a, b);
}
var obj = {
sum: function(a, b) {
return a + b;
}
};
console.log(doStuff.call(obj, 3, 4)); // 7, because `obj` has a `sum` property
console.log(doStuff(3, 4)); // Fails with an error that `this.sum` is not a function
so why doesn't the below work? (result => NaN)
doStuff.call(myContext,this.c,this.d)
Because this.c is evaluated before the call to doStuff, using whatever the current this value is. How you're probably calling that code, this is (in loose mode) the global object (the window object, on browsers), which probably doesn't have a c or d property. (In strict mode, again making assumptions about how you're calling that, you'd get an exception, because this is undefined and you can't retrieve properties from undefined.)
.call and .apply are Function methods that 'manipulate' what this means inside a function.
doStuff.call(myContext, myContext.c, myContext.d):
here you've set myContext as context for doStuff function
and you may refer to it inside by using this,
and you've passed two arguments to it: myContext.c, myContext.d,
it works as you've intended...
doStuff.call(myContext, this.c, this.d):
again myContext is context for doStuff()
but you've passed .c and .d properties of what this points to
in context in which it appears (global object, window) in your case.
So doStuff's context is myContext, and parameters are 2 undefineds,
because this === window in context where you are calling the function,
and you are passing .c and .d properties of global into the function.
you are actually getting this: return undefined + undefined + 1; (NaN)
if you redefine 'doStuff' like this:
function doStuff () {
return this.a + this.b + 1;
// here it looks whatever this is set to
// by `.call()` or `.apply()` methods
}
and call it like this:
var sum = doStuff.call(myContext);
// notice that using `.call` here
// means that 'myContext' is manualy set
// as `this` value inside the function above
// and is refered to by dynamic variable 'this'
Related
In the JS manual I have the following example, that is working correctly:
let worker = {
someMethod() {
return 1;
},
slow(x) {
alert("Called with " + x);
return x * this.someMethod(); // (*)
}
};
function cachingDecorator(func) {
let cache = new Map();
return function(x) {
if (cache.has(x)) {
return cache.get(x);
}
let result = func.call(this, x); // теперь 'this' передаётся правильно
cache.set(x, result);
return result;
};
}
worker.slow = cachingDecorator(worker.slow); // теперь сделаем её кеширующей
alert( worker.slow(2) ); // работает
alert( worker.slow(2) ); // работает, не вызывая первоначальную функцию (кешируется)
The question is: how the "this" reference is transferred into cachingDecorator function, if the cachingDecorator is not declared inside of the object, and is called like worker.slow = cachingDecorator(worker.slow)? I talk about this row inside the cachingDecorator: let result = func.call(this, x).
this reference is transferred in the last 2 rows when the decorator is actually used as worker object is before the dot.
The this is not actually in cachingDecorator, but is rather in the anonymous function being returned. So the this value is not set until that function is invoked.
Because that function is assigned to worker.slow, and you're calling it from that object, the this value gets set to the worker object.
The important thing to remember is that this is kind of like a weird function parameter. It just has a keyword for a name and always gets set (except in arrow functions). It gets set based on how the function is invoked, instead of being set like regular parameters via arguments being passed.
Note that the function cachingDecorator returns a function declared like function(x) {. . .}. Any function like this will inherit context when it is invoked as a reference to an object member:
function magicFn() { return this }
const x = { magicFn };
const y = { someKey: 6, magicFn };
x.magicFn(); // === x, because it was invoked as x.magicFn
y.magicFn(); // === y
magicFn(); // === undefined, no `this` object member reference
So when worker.slow = cachingDecorator(worker.slow) is called, the resulting function invokes the original worker.slow using worker.slow.call(this, x); which proxies the incoming this value (in this case, worker).
I'm really puzzled with Javascript this time:
var x = Array.prototype.concat.call;
typeof x; // function
x(); // Uncaught TypeError: x is not a function
What on earth is going on here?
If it helps, I also noticed:
x([1,2],[3,4]) does not work either
toString also thinks it's a function:
Object.prototype.toString.call(x); // "[object Function]"
This also happens with Array.prototype.concat.apply.
When it is forced as an expression it also does not work:
(0, Array.prototype.concat.call)([1,2],[3,4]); // Same TypeError
Tested in Chrome and Node.
The error is misleading. x is a function, but it has lost the referenced function (concat), which throws an error
Running on firefox gives a more descriptive error
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Called_on_incompatible_type
What it's saying is that the call function has nothing its bound to.
In the same way that if you take an object like this:
const a = {
b: 2,
test() {
console.log('hi', this.b);
}
};
const c = a.test;
c();
You will get hi undefined as you've lost the relationship of the function to b.
You can fix this by either doing c.bind(a)() or c.call(a)
The call function behaves similarly. It is going to be the same for every function, and the pseudocode would look something like this:
class Function {
constructor(functionDefinition) {
this.functionDefinition = functionDefinition;
}
call(newThis, ...args) {
// take this.functionDefinition, and call it with `this` and `args`
}
}
Since you are extracting out the call function, it loses the function object it's associated with.
You can fix this by either binding concat to the function, or using call on call :-)
const a = []
const boundFn = a.concat.call.bind(a.concat)
console.log(boundFn([3], [1,2]));
// Or, you can use `call` to pass in the concat function
const callFn = a.concat.call;
console.log(callFn.call(a.concat, [4], [1,2]))
Can anyone please explain me the behavior of this code?
var arguments = 42;
var arr = () => arguments;
arr(); // 42
function foo() {
var f = (i) => arguments[0] + i; // foo's implicit arguments binding
return f(2);
}
foo(1); // 3
I know what implicit arguments binding is.
What I don't understand is how foo(1) is returning 3?
What does return f(2) does? As in which function it calls?
PS: I'm following this Mozilla docs.
Arrow functions do not bind arguments so when you use arguments[0] inside f you are accessing foo's arguments (ie 1). Since you have passed 2 as i you get 1 + 2
For example if you use an arrow function that tries to access arguments outside of a function call you should get a ReferenceError
const f = i => console.log(arguments[0])
try {
f()
console.assert(false, 'should not get here')
} catch (e) {
console.assert(e instanceof ReferenceError,
'should get reference error on trying to access arguments')
console.log(e.message)
}
To make this a little clearer for you I'll re-write it as ES5
function foo(){
var arg1 = arguments[0]; // argument is 1 in your example
var f = function(i) {
return arg1 + i; // i is 2 in your example
};
return f(2); // returns 3
}
foo(1); // sets argument to 1 and returns 3
In ES6 arrow functions do not have an arguments property like standard functions. You're accessing the foo function arguments array like object. As the first argument you passed in is 1, the arrow function accesses the foo function arguments object and retrieves the first value within the arrow function.
Thanks to this question I understand how a function may take two sets of parentheses and how a function may return another function which fires immediately after being returned.
What I do not understand is why this is good to know? How could this be applied to solve a problem?
The example of:
function add(x){
return function(y){
return x + y;
};
}
add(3)(4) === 7; // true
Works fine - sure. But why not just write it like this instead?
function add(a, b) {
return a + b;
}
add(3, 4) === 7; // true
Lets take the same code which you have mentioned.
function add(x) {
return function(y) {
return x + y;
};
}
var adder3 = add(3); //Forming adder3
var op1 = adder3(4) // 7
var op1 = adder3(5) // 9
// Now adder 10:
var adder10 = add(10); //Forming adder3
var op1 = adder10(4) // 14
var op1 = adder10(5) // 15;
Hope you understand!!
Revert me if you need more info on closure.
Your example called as closures
Closures’ Rules and Side Effects
Closures have access to the outer function’s variable even after the outer function returns:
One of the most important and ticklish features with closures is that the inner function still has access to the outer function’s variables even after the outer function has returned. Yep, you read that correctly. When functions in JavaScript execute, they use the same scope chain that was in effect when they were created. This means that even after the outer function has returned, the inner function still has access to the outer function’s variables. Therefore, you can call the inner function later in your program. This example demonstrates:
function celebrityName(firstName) {
var nameIntro = "This celebrity is ";
// this inner function has access to the outer function's variables, including the parameter
function lastName(theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}
var mjName = celebrityName("Michael"); // At this juncture, the celebrityName outer function has returned.
// The closure (lastName) is called here after the outer function has returned above
// Yet, the closure still has access to the outer function's variables and parameter
mjName("Jackson"); // This celebrity is Michael Jackson
Closures store references to the outer function’s variables; they do not store the actual value.
Closures get more interesting when the value of the outer function’s variable changes before the closure is called. And this powerful feature can be harnessed in creative ways, such as this private variables example first demonstrated by Douglas Crockford:
function celebrityID() {
var celebrityID = 999;
// We are returning an object with some inner functions
// All the inner functions have access to the outer function's variables
return {
getID: function() {
// This inner function will return the UPDATED celebrityID variable
// It will return the current value of celebrityID, even after the changeTheID function changes it
return celebrityID;
},
setID: function(theNewID) {
// This inner function will change the outer function's variable anytime
celebrityID = theNewID;
}
}
}
var mjID = celebrityID(); // At this juncture, the celebrityID outer function has returned.
mjID.getID(); // 999
mjID.setID(567); // Changes the outer function's variable
mjID.getID(); // 567: It returns the updated celebrityId variable
Reference site : http://javascriptissexy.com/understand-javascript-closures-with-ease/
Functions which return functions are useful when you want similar functions which depend on some parameters.
A real life example: [].sort can be called with a custom comparator function, but it can make sense to define a comparator function to allow more customizations:
function comparator(options) { // Function which returns a function
return function(a, b, tmp) {
if(options.reverse) tmp = a, a = b, b = tmp;
if(options.map) a = options.map(a), b = options.map(b);
if(options.func) return options.func(a, b);
return a < b ? -1 : (b < a ? 1 : 0);
}
}
Then you can use
[1,11,10,2].sort(comparator({map: String})); // [1, 10, 11, 2]
[1,11,10,2].sort(comparator({reverse: true})); // [11, 10, 2, 1]
[1,11,10,2].sort(comparator({func: Function.prototype})); // [1, 11, 10, 2]
if we require a function in certain state with certain value then we can use it inside another function and return that, so that the return function with certain state can be directly used in different scenario.
you can check out various example on closure.
http://javascriptissexy.com/understand-javascript-closures-with-ease/
If you know the first param is always going to be the same, then it will convenient to have it closure, instead of passing it again and again. For simple programs, it may not make sense. But, for programs which handles repetitive params more often, this technique definitely comes handy.
Other than closures you can also use it for pre-processing as a one time job, consider if you had to do something intensive, e.g. generate a million things;
function generateSessionSecrets(lock) {
var secrets = [], i = 1000000;
while (i-- > 0) {
secrets[i] = Math.random();
}
return function(key, i) {
if (key === lock) return secrets[i];
}
}
var chest = generateSessionSecrets('fizz');
chest('fizz', 0); // e.g. 0.2096199430525303
chest('fizz', 1); // e.g. 0.30329699837602675
// ...
chest('fizz', 0); // still 0.2096199430525303
(This is an example of concept, not an example of real security)
there isn't a point to using it immediately. you would use it to create a function to attach it to an event or use as a callback for an asynchronous function. an example might be such:
function factory(param){
return function(result) {
if (result==param) dosomething();
}
}
$('#domobject').click({
param = $('#domvalue').value;
asynch_function(factory(param));
});
Here I've attached a click event to presumably a button. When it is clicked it will retrieve the value of an input and create a function based on it and call an asynchronous function with the newly created function as it's callback. The asynchronous function might be an ajax request. When the asynchronous function completes the function that factory created, the callback, will be called. It will check the return value the asynchronous function passed to the callback against the param specified when the event was attached.
If we move the dom lookup to inside the callback function, then we wouldn't need factory or param, but then it would use the value that is in the input at the time when the asynch function has returned, rather than when the button was clicked which is later and the value might have changed.
Sometimes, you won't be able to obtain a value you need in the context of the callback for other reasons. Or it might just be that you want to abstract out a class of functions so you don't have to retype a slightly different version of it in all the places you use it.
Suppose I have JavaScript code like
myClass = function(){
function doSomething(){
alert(this); // this1
}
}
alert(this); //this2
What those two 'this' objects are refer for??
The this value in the global execution context, refers to the global object, e.g.:
this === window; // true
For Function Code, it really depends on how do you invoke the function, for example, the this value is implicitly set when:
Calling a function with no base object reference:
myFunc();
The this value will also refer to the global object.
Calling a function bound as a property of an object:
obj.method();
The this value will refer to obj.
Using the new operator:
new MyFunc();
The this value will refer to a newly created object that inherits from MyFunc.prototype.
Also, you can set explicitly that value when you invoke a function, using either the call or apply methods, for example:
function test(arg) {
alert(this + arg);
}
test.call("Hello", " world!"); // will alert "Hello World!"
The difference between call and apply is that with apply, you can pass correctly any number of arguments, using an Array or an arguments object, e.g.:
function sum() {
var result = 0;
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
var args = [1,2,3,4];
sum.apply(null, args); // 10
// equivalent to call
sum(1,2,3,4); // 10
If the first argument value of call or apply is null or undefined, the this value will refer to the global object.
(note that this will change in the future, with ECMAScript 5, where call and apply pass the thisArg value without modification)