Bind named function to context - javascript

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;
};

Related

how to replace `bind(this)` in es6

I read some codes:
class XXXX {
init() {
MyOBJ.on('EVENTNAME', this.myfunction.bind(this)); // Line3, MyOBJ extends EventEmitter
}
}
Just curious how to use arrow function to replace Line3? Thanks
Function.prototype.bind 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.
What this specific example - this.myFunction.bind(this) - achieves, is to be able to pass a reference to a function (that happens to also be referenced by this.myFunction) while making sure that any calls to that function are done in the context of this.
In ES2015+ we can do this:
class XXXX {
init() {
MyOBJ.on('EVENTNAME', (event) => this.myfunction(event));
}
}
With ES2015 arrow functions this will inside the body be the declaration context of the arrow function. So in our case this.myFunction gets called in an arrow function whose context is the context of the call to init(), a.k.a. this within init.
The key difference is that now you actually create a call expression instead of just passing a reference to the function. This time the reference given to MyOBJ.on is the arrow function.
A strict ES5 equivalent to the snippet above would also use a function literal as callback given to MyOBJ.on:
class XXXX {
init() {
MyOBJ.on('EVENTNAME', function(event) {
this.myfunction(event));
}.bind(this));
}
}
Replace this
this.myfunction.bind(this)
to this
() => {}
Your event binding would look as follow:
class XXXX {
someMethod() {}
init() {
MyOBJ.on('EVENTNAME', () => {
this.someMethod(); // The originating context it's actually your class XXXX
});
}
}
Resource
Javascript ES6 — Arrow Functions and Lexical this
One of the most anticipated new features in the ES6 Javascript standard was the Arrow Function Expression. It promises a shorter syntax than it’s predecessor the Function Expression. In addition is how the new Arrow Function binds, or actually DOES NOT bind it’s own this. Arrow Functions lexically bind their context so this actually refers to the originating context.
Depending on how you add your function to your object, you can simply do:
MyOBJ.on('EVENTNAME', this.someMethod);
Using arrow functions like this will bind the methods to the instance of the class.
class XXXX {
constructor() {
this.init();
};
someMethod = () => {
console.log('someMethod() called');
};
init = () => {
MyOBJ.on('EVENTNAME', this.someMethod);
};
}
const x = new XXXX();
const MyOBJ = new EventEmitter();
MyOBJ.emit('EVENTNAME'); // someMethod() called
Note: using mocks to test for this.someMethod being called will likely fail if you use this method because the EventEmitter actually replaces the context of this inside someMethod when it's called. https://github.com/sinonjs/sinon/issues/1536 describes this issue.

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);
};

What is a best practice for ensuring "this" context in Javascript?

Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
Voilà -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.

How do I pass a function(delegate?) as a parameter to another function in Javascript and then use it

I want to pass a function to another function. I think functions being passed like that are call delegates? I am having a hard time finding a good explanation for this kind of thing online. Is this the right way to do this?
function getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
function naturalSort(a, b, func) {
//...
var $a = func(a);
var $b = func(b);
//...
}
//usage
naturalSort(x, y, getCellContentByColumnIndex);
Your code:
function getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
Is a syntax error. The following is a function declaration:
functon foo() {}
And this is a function expression:
var foo = function(){}
And this is a named function expression:
var foo = function bar(){}
There are a number of answers here on the differences, there is a detailed explanation in the article Named function expressions demystified which also covers many other aspects of function declarations and expressions.
The term "anonymous function" is jargon for a function expression that has no name and isn't assigned to anything, e.g.
someFn( function(){...} )
where someFn is called and passed a function that has no name. It may be assigned a name in someFn, or not. Ic could just be referenced as arguments[0].
Passing a function is not delegating, that is jargon for the practice of putting a listener on a parent element and catching bubbling events, it is preferred in cases where it can replace say a click listener on every cell in a table with a single listener on the table.
Anyhow, passing a function is just like passing any other object:
function foo(){
alert('foo');
}
function callIt(fn) {
fn();
}
callIt(foo); // 'foo'
In the above, foo is passed to callIt and assigned to the local variable fn, and is then called.
You pass functions around as variables like so:
var getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
function naturalSort(a, b, func) {
//...
var $a = func(a);
var $b = func(b);
//...
}
//usage
naturalSort(x, y, getCellContentByColumnIndex);
This is called using anonymous functions.
Anonymous functions..
var getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
will work..rest stuff of calling is already perfect in your code..:)
In JavaScript, functions are treated as first class citizens which mean you can toss them here and there like simple variables. The key is, use the FunctionName when you want to refer to function and use FunctionName() to invoke it.
this line: naturalSort(x, y, getCellContentByColumnIndex);
could have been written as
naturalSort(x, y, function (){
return $(row.children().get(index)).text();
});
In which case it would have been called passing Anonymous Function

Can I intercept a function called directly?

In this code I created a function called someFunction. Then I modified Function.prototype.apply and call methods. So instead of my function code is working I am running my interception code (which shows an alert). But neither "call" nor "apply" intercepts direct method call. Is it possiple to intercept this?
Function.prototype.call = function(){alert("call");};
Function.prototype.apply = function(){alert("apply");};
function someFunction(){}
window.onload = function(){
someFunction.call(this); //call alert is shown
someFunction.apply(this); //apply alert is shown
someFunction(); //how can I intercept this?
}
You can only override a known function by setting another function in its place (e.g., you can't intercept ALL function calls):
(function () {
// An anonymous function wrapper helps you keep oldSomeFunction private
var oldSomeFunction = someFunction;
someFunction = function () {
alert("intercepted!");
oldSomeFunction();
}
})();
Note that, if someFunction was already aliased/referenced by another script before it was changed by this code, those references would still point to the original function not be overridden by the replacement function.
Function.prototype.callWithIntercept = function () {
alert("intercept");
return this.apply(null, arguments);
};
var num = parseInt.callWithIntercept("100px", 10);
It is worth noting that in newer versions of JS, there are Proxy objects you can use:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
There is a chance you can intercept direct function call. This requires:
Either the function is created by Function.prototype.bind and you have to overwrite Function.prototype.bind before creating the function, or
The function is created from Function() (or new Function()) and you also have to overwrite Function function before creating the target function.
If neither of the above two can be met, the only way to intercept a direct call is to wrap the target function, which is the solution provided by AndyE https://stackoverflow.com/a/3406523/1316480
For a function that is created by function literal and is hidden in private scope, there is no way to intercept a direct call to it.
I have a blog post concludes all of these: http://nealxyc.wordpress.com/2013/11/25/intercepting-javascript-function/
You could iterate over the global scope and replace any objects of function type you find which aren't "yours".
Brilliant, love it :)
const originalApply = window.Function.prototype.apply;
window.Function.prototype.apply = function(){
console.log("INTERCEPTING APPLY", arguments);
return originalApply.call(this, ...arguments);
};
You can achieve this with a Proxy.
First define a handler with an apply trap that intercepts calls to the function. Then, using that handler, set the function to be a proxy onto itself. Example:
function add(a, b){
return a + b;
}
const handler = {
apply: function(target, thisArg, argumentsList) {
console.log('add was called with ' + argumentsList.join(' and '));
return target(...argumentsList);
}
};
add = new Proxy(add, handler);
var m = add(3, 5);
console.log('m = ', m);
var n = add(12, 8);
console.log('n = ', n);

Categories

Resources