Why can't I use .call() in a replace function? - javascript

Why is this valid:
function func(a,b,c) {
console.log(this, a,b,c);
return '';
}
'testing'.replace(/e/, func);
but this isn't:
function func(a,b,c) {
console.log(this, a,b,c);
return '';
}
'testing'.replace(/e/, func.call);
if func is a function reference, and call is a function reference shouldn't they both work?
Here's a fiddle of this

Because when you pass the call function, you take it out of the context of func, so inside call the this keyword will refer to window instead of func.
window is not a function, but call expects this to be a function, so it breaks.
For comparison.
var AnObject = {
call: function () { console.log("this.location is: ", this.location); },
location: "This string is the location property of AnObject"
};
AnObject.call();
setTimeout(AnObject.call, 500);

Because .call() itself is a method, but not one that is useful to replace().
In other words, while your intention is to pass func, you're actually passing a completely different function (call) that serves a purpose not useful as an argument to .replace().

Related

Bind named function to context

I have a named function like this
function handler(arg) {
this.arg = arg;
}
Is there a way to bind context to this function?
I'm looking for something like this
function handler(arg) {
this.arg = arg;
}.bind(this);
that will allow to call this function in any context, but it will always use bound context.
For example I have another function (in vendor code, that I can't edit):
addEventListener(callback) {
callback(currentArg);
}
where I will pass handler and it will be executed with bound context.
I'm not sure where and how you are going to use it but you can use a function expression instead of function deceleration:
var handler = function handler(arg) {
this.arg = arg;
}.bind(this)
Or just use an arrow function which will use a lexical context for this:
var handler = (arg) => {...}
From MDN
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
According to this definition you can do the following
function handler(arg) {
this.arg = arg;
}
var newFunc = handler.bind(newContext,arg);
If you just want to avoid context change, you can use arrow function:
handler = (arg) => {
this.arg = arg;
};

How to reference parent parameters of an anonymous function Javascript

I'm trying to figure out how I can reference an argument on a wrapper function stored in a variable that calls an anonymous function. Such as in the example below. The problem I run into is I'm used to accessing parameters via the arguments variable, but that sees only the parameter myFunc. I know it's supposed to be possible, but I can't figure out how.
var myFunc = function(arg1, arg2){//do stuff}
var myFuncWrapped = wrapper(myFunc);
myFuncWrapped('arg value1', 'arg value2');
function wrapper(func){
//how can I reference 'arg value1' from here since arguments == myFunc?
}
As the comment suggested, wrapper should be returning a function so you can capture the arguments via closure when myFuncWrapped gets called.
var myFunc = function(arg1, arg2) {
console.log(arg1); // (For testing only) Should be"arg value1"
};
var myFuncWrapped = wrapper(myFunc);
myFuncWrapped('arg value1', 'arg value2');
function wrapper(func) {
/* The anonymous function below is actually the one
* being called when you invoke "myFuncWrapped" so it has the arguments you need.
*/
return function() {
console.log(arguments[0]); // (For testing only) Should be"arg value1"
func.apply(this, arguments);
}
}

Get right object in callback

I have target object
function Foo() {
this.someVar = 'some var';
};
Foo.prototype.callback() {
console.log(this);
};
And object, that will call this callback
function Bar(callback) {
this.callback = callback;
};
Bar.prototype.onSomeAction = function() {
this.callback();
};
And initial code
foo = new Foo();
bar = new Bar();
bar.callback = foo.callback;
bar.onSomeAction();
Result: i have logged to console Bar()'s context instead of Foo().
How can i get context of Foo() in the Foo() callback?
PS: I tried closures
Foo.prototype.callback() {
var foo = this;
return function(foo) {
console.log(foo);
};
};
but it does nothing. I have not fully understanding of the closures :(
The reason your original code didn't work is that the value of this inside of a method call is the value of the object it's being called on. That means when you say:
bar.callback = foo.callback;
And then you call:
bar.callback();
The code defined here:
Foo.prototype.callback = function () {
console.log(this);
};
gets called with this being a reference to bar because bar is to the left of the . on the method call. So whenever you assign a function as an object property, calling it on that object will call it with the object as this.
You could also have written:
function callback() {
console.log(this);
}
bar.callback = callback;
bar.callback();
And you would find that this still references bar.
In fact, if you call the plain function callback(); as defined above, you'll find that this is a reference to the global object, usually window in web browsers. That's because all global variables and functions are properties of window, so callback(); is implicitly window.callback();
The fact that the value of this depends on what object is calling a function can be a problem when passing callbacks around, since sometimes you want this to reference the original object the function was a property of. The bind method was design to solve this problem, and Yuri Sulyma gave the right answer:
bar.callback = foo.callback.bind(foo);
However, the way you would do this using closures is to capture an instance of Foo within an anonymous function that calls the correct method on the correct object:
foo = new Foo();
bar = new Bar();
bar.callback = function () {
foo.callback();
};
bar.onSomeAction();
Which is essentially what bind does. In fact, we call write our own naive version of bind using a closure:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.call(obj);
};
};
call let's you call a function with the value of this explicitly defined. This allows you to "set the context" the function is called in so that it's the same as calling obj.fn() when you call bar.callback(). Since when we call foo.callback.bind(foo);, obj is foo and fn is foo.callback, the result is that calling bar.callback() becomes the same as calling foo.callback().
That's where Dalorzo's answer comes from. He uses call to explicitly set the context.
There's also another function for setting the context called apply that also takes an array representing the arguments for the function as its second argument. This allows us to write a more complete version of bind by taking advantage of the special arguments variable:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.apply(obj, arguments);
};
};
bar.callback = foo.callback.bind(foo);
You can polyfill Function.prototype.bind() if necessary: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
Try using these changes:
Use call to set context:
bar.onSomeAction.call(foo);
And I think your callback function needs to change to:
Foo.prototype.callback=function() {
console.log(this);
};

Where do function arguments exist when using .call()/.apply()?

i came across a situation where I need to call another function with .call() or .apply() like this:
function b() {
alert(arg);
}
Then
function a(arg) {
b.call();
}
a(123);
Function b is called, but doesnt' have access to arg. That's ok, I can pass scope.. yes?
function a(arg) {
b.call(this);
}
a(123);
Still no - I can't access arg from function b. How can I do it?
UPDATE:
I do not want to modify b function :-)
You still need to pass the arguments via call (individually) or apply (as an array):
function a(arg1, arg2, arg3) {
b.call(this, arg1, arg2, arg3);
// or
b.apply(this, arguments)
// or
b.apply(this, [arg1, arg2, arg3]);
}
Of course, nothing about your situation suggests actually using call or apply: Just invoke the function yourself.
function a(arg) {
b(arg);
}
It’s not possible to “pass scope” or something like that. The scope of a function is determined when the function is created, so it can only access arg if it exists where b is defined.
If arg was part of this, then you could do this using call, i.e. make the this in b the same this as it is in a (of course this will modify whatever this actually refers to, which can have side effects you might not want to happen).
function a (arg) {
this.arg = arg;
b.call(this);
}
function b () {
console.log(this.arg);
}
The other way would be to just pass the argument to b as an actual function argument. You can access all arguments of a function using arguments:
function b () {
console.log(arguments);
}
Try this one:
function a(arg) {
b.apply(this, [arg]);
// or
// b.call(this, arg);
}
function b() {
alert(arguments);
}
You failed to pass the arguments when you called b.
Function::call allows you to pass a fixed number of arguments:
function a(arg1,arg2) {
return b.call(this,arg1,arg2);
}
Function::apply allows you to pass any number of arguments, as an array:
function a(arg1,arg2) {
return b.apply(this,[arg1,arg2]);
}
// or
function a(arg1,arg2) {
return b.apply(this,arguments); // The magical 'arguments' variable
}
this is the context object, and is not the same thing as scope.
I'll guess the problem lies in b(). There's no 'arg' argument defined. Try:
function b(arg) { alert(arg); }
and
function a(arg) {
b.call(this,arg);
}
a(123);
now it runs
(Update: the call needs the arguments ( context functionarg1, functionarg2...) )
Assuming that, for some reasons you can't modify the body of b , you can try something like this:
function a(arg) {
eval("("+ b.toString() +")()");
}
DEMO

Add to a javascript function

I have a function I can't modify:
function addToMe() { doStuff(); }
Can I add to this function? Obviously this syntax is terribly wrong but it's the general idea...
function addToMe() { addToMe() + doOtherStuff(); }
You could store a reference to the original function, and then override it, with a function that calls back the original one, and adds the functionality you desire:
var originalFn = addToMe;
addToMe = function () {
originalFn(); // call the original function
// other stuff
};
You can do this because JavaScript functions are first-class objects.
Edit: If your function receives arguments, you should use apply to pass them to the original function:
addToMe = function () {
originalFn.apply(this, arguments); // preserve the arguments
// other stuff
};
You could also use an auto-executing function expression with an argument to store the reference of the original function, I think it is a little bit cleaner:
addToMe = (function (originalFn) {
return function () {
originalFn.apply(originalFn, arguments); // call the original function
// other stuff
};
})(addToMe); // pass the reference of the original function

Categories

Resources