I want to know if it is possible to somehow "bind" a javascript arrow function to an instance of the prototype to which it is scoped.
Basically, I want to get an instance variable from a prototype using an arrow function. I know this cannot be done by arrow function alone, but I am curious if it is possible to also bind this arrow function to the instance scope before assigning it. Something along the idea of:
String.prototype.myFunction = (() => {
console.log(this.toString());
}).naiveBindNotionICantDescribe(String.prototype);
which would be equivalent to:
String.prototype.myFunction = function() {
console.log(this.toString());
};
I am curious because I am trying to see if javascript arrow functions can completely replace javascript functions if you understand them well enough and are clever with them, or if there are things which are absolutely impossible to accomplish without keyword "function", even in clever ways.
Here is an example of what I mean:
/* anonymous self-contained demo scope. */
{
/**
* example #1: using function to reach into instance variable via prototype
* anonymous scope.
*/
{
String.prototype.get1 = function() {
return this.toString();
};
console.log('hello'.get1());
}
/**
* example 2: what I want to do but can't express in a way that works.
* This does not work.
* anonymous scope.
*/
{
String.prototype.get2 = () => {
return this.toString();
};
console.log('hello'.get2());
}
}
Is this possible to do, or are functions absolutely necessary to reach into instance variable and there is no way to circumvent this?
Apparent Solutions:
wrapper magicBind (Thanks Thomas)
code:
var magicBind = (callback) => function() {
return callback(this);
};
String.prototype.get = magicBind((self) => {
return self.toString();
});
console.log('hello'.get());
function converter from "fat arrow" to "function", (inspired by Thomas's answer)
code:
Function.prototype.magicBind2 = function() {
var self = this;
return function() {
return self(this);
}
};
String.prototype.get2 = ((self) => {
return self.toString();
}).magicBind2();
console.log('hello'.get2());
First solution without explicit use of 'function', which refers to "Function" constructor as (() => {}).constructor to avoid using the word "function" or "Function".
code:
var regex = /\{([.|\s|\S]+)\}/m;
String.prototype.get = (() => {}).constructor(
(() => {
return this;
}).toString().match(regex)[0]
);
console.log('hello, world'.get());
Both solutions so far allow burial of "function" keyword while allowing accessing local scope.
No. Lexically binding the this value is the point of arrow functions.
They are not a general replacement for function declarations and function expressions.
I was hoping to do some magic involving passing the instance as an argument to the arrow function and using this instance "self" in place of "this". Something along the lines of hastebin.com/baxadaxoli.coffee but without the "function". eg, pass the "self" during assignment without creating a function via some magicBind operation.
var magicBind = (callback) => function (...args) {
'use strict';
return callback(this, args);
}
var log = (self, args) => {
console.log('this: %o args: %o', self, args);
};
String.prototype.log = magicBind( log );
'hello'.log();
enough magic? ;)
Edit: but still, why do you insist on using fat arrows. Even if you have to jump through loops to achieve what you want. Just to scratch off a few characters from you .min.js-file
/*
log was more appropriate than get,
and it makes no sense to console.log() the result of a function that has no return-value; even less if the only purpose of that function is calling console.log() itself
*/
I know this cannot be done by arrow function alone, but I am curious
if it is possible to also bind this arrow function to the instance
scope before assigning it.
Exactly, it can be done alone, but you can make this be the desired value... by wrapping it inside a normal function:
String.prototype.get2 = function() {
return (() => this.toString())();
};
console.log('hello'.get2());
But at that point the arrow function becomes superfluous.
Related
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
Normally I'd assign an alternative "self" reference when referring to "this" within setInterval. Is it possible to accomplish something similar within the context of a prototype method? The following code errors.
function Foo() {}
Foo.prototype = {
bar: function () {
this.baz();
},
baz: function () {
this.draw();
requestAnimFrame(this.baz);
}
};
Unlike in a language like Python, a Javascript method forgets it is a method after you extract it and pass it somewhere else. You can either
Wrap the method call inside an anonymous function
This way, accessing the baz property and calling it happen at the same time, which is necessary for the this to be set correctly inside the method call.
You will need to save the this from the outer function in a helper variable, since the inner function will refer to a different this object.
var that = this;
setInterval(function(){
return that.baz();
}, 1000);
Wrap the method call inside a fat arrow function
In Javascript implementations that implement the arrow functions feature, it is possible to write the above solution in a more concise manner by using the fat arrow syntax:
setInterval( () => this.baz(), 1000 );
Fat arrow anonymous functions preserve the this from the surrounding function so there is no need to use the var that = this trick. To see if you can use this feature, consult a compatibility table like this one.
Use a binding function
A final alternative is to use a function such as Function.prototype.bind or an equivalent from your favorite Javascript library.
setInterval( this.baz.bind(this), 1000 );
//dojo toolkit example:
setInterval( dojo.hitch(this, 'baz'), 100);
i made a proxy class :)
function callback_proxy(obj, obj_method_name)
{
instance_id = callback_proxy.instance_id++;
callback_proxy.instances[instance_id] = obj;
return eval('fn = function() { callback_proxy.instances['+instance_id+'].'+obj_method_name+'(); }');
}
callback_proxy.instance_id = 0;
callback_proxy.instances = new Array();
function Timer(left_time)
{
this.left_time = left_time; //second
this.timer_id;
this.update = function()
{
this.left_time -= 1;
if( this.left_time<=0 )
{
alert('fin!');
clearInterval(this.timer_id);
return;
}
}
this.timer_id = setInterval(callback_proxy(this, 'update'), 1000);
}
new Timer(10);
I know this sounds like a opinion question, but I'm a Junior in JavaScript skills and would like to understand the technical plusses and minuses of each of the following ways of getting a this into a function (that has its own this, of course).
Let's say I write - and this is a real life example of mine -
Calculator.prototype.Initialize = function () {
// Fill in all regions in the RegionsChecked array
this.Data.forEach(function(region){
this.RegionsChecked.push(region.RegionName);
});
…
and I realize that
"Oops, the this in this.RegionsChecked is supposed to actually
refer to the Calculator function that is calling the Intialize
function."
I either solve this problem by doing
var that = this;
this.Data.forEach(function(region){
that.RegionsChecked.push(region.RegionName);
});
or
(function(calc){
this.Data.forEach(function(region){
calc.RegionsChecked.push(region.RegionName);
});
})(this);
and I'm wondering which would be considered better or if there is an even better way (and why).
Array.prototype.forEach also takes a second argument that specifies the this value (context) with which the callback should be invoked.
this.data.forEach(function (region) {
this.regionsChecked.push(region.regionName);
}, this);
A better alternative would be to use ES6 arrow functions, that are bound to the lexical this value:
this.data.forEach(region => {
this.regionsChecked.push(region.regionName);
});
If ES6 isn't available, and the method does not support specifying a this value for its callback, you could bind the function to a this value:
this.data.forEach(function (region) {
this.regionsChecked.push(region.regionName);
}.bind(this));
All the above solutions are working, it's more like personal preference. For example if you check out Airbnb javascript style guide:
https://github.com/airbnb/javascript#22.5
22.5 Don't save references to this. Use arrow functions or Function#bind.
// bad
function foo() {
const self = this;
return function () {
console.log(self);
};
}
// bad
function foo() {
const that = this;
return function () {
console.log(that);
};
}
// good
function foo() {
return () => {
console.log(this);
};
}
En example can be found in Twitter'a typeahead.js here:
function () {
// ...
return this.each(initialize);
function initialize() {
// ...
}
}
Questions:
What are the scopes and what function sees what?
What is the reason for using such a construct (usage scenarios and advantages)?
Javascript has function based scope, which means that every thing defined inside a function is available from the first line, since the definition is "hoisted" by the complier.
That goes for both variable and function definitions - variable values however, are not available until after assignment.
You can read all about javascript scoping and hoisting here
This means that the function initialize is available from the first line of the wrapping anonymous function.
There is no real reason, and no advantages, for doing it that way, unless you count the code structure as an advantage.
Personally I don't see any reason to do this. For me even it looks a little bit weird. Martin is right. You should be careful, because the defined variables are not accessible like functions. For example this doesn't work:
var getValue = function(func) {
return func();
}
var f = function() {
return getValue(now);
var now = function() {
return 10;
}
}
alert(f());
However, this works:
var getValue = function(func) {
return func();
}
var f = function() {
return getValue(now);
function now() {
return 10;
}
}
alert(f());
Is there any way to refer to the function object that you're currently executing in? If it's not a method of any object or not called with .call() or .apply(), the this pointer is likely just window, not the function object.
I often use a design pattern like this for global variables that I want scoped to a particular function as this keeps them out of the top level namespace:
function generateRandom() {
if (!generateRandom.prevNums) {
generateRandom.prevNums = {}; // generateRandom.prevNums is a global variable
}
var random;
do {
random = Math.floor((Math.random() * (99999999 - 10000000 + 1)) + 10000000);
} while (generateRandom.prevNums[random])
generateRandom.prevNums[random] = true;
return(random.toString());
}
But, I'd rather not have to spell out the function name every time I want to use a variable scoped to that object. If the name of the function ever changes, there are then a lot of places to change the name.
Is there any way to get the currently executing function object?
Well, you could use arguments.callee()...
https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments/callee
From MDN:
Description
callee is a property of the arguments object. It can be used to refer
to the currently executing function inside the function body of that
function. This is for example useful when you don't know the name of
this function, which is for example the case with anonymous functions.
Note: You should avoid using arguments.callee() and just give every
function (expression) a name.
BUT...
What you really want are Javascript Prototypes.
function RandomSomethingGenerator()
{
this.prevNums = {};
}
RandomSomethingGenerator.prototype.generate = function() {
var random;
do {
random = Math.floor((Math.random() * (99999999 - 10000000 + 1)) + 10000000);
} while (this.prevNums[random])
this.prevNums[random] = true;
return(random.toString());
};
Why do I say this?
1.) You're dirtying the global space with all those functions.
2.) Even if you like Jani's suggestion, and you want a "static" function like you have now, my suggestion would be the same, but with a twist: Create your global function, and wrap an instance of an object (built from a prototype) inside the closure and make the call to it (so, basically, make yourself a singleton).
As in this (adapted from Jani's answer):
var randomSomething = (function() {
var randomSomethingGenerator = new RandomSomethingGenerator();
return function() {
randomSomethingGenerator.generate();
};
})();
I don't think there's any way to do exactly what you ask, but you could use a closure for your function-local static variables instead.
You can easily achieve this using an IIFE:
var generateRandom = (function() {
//any function's static variables go here
var prevNums = {};
return function() {
//function code goes here
var random;
do {
random = Math....
}
prevNums[random] = true;
return random.toString();
};
})();
You want arguments.callee. From MDN - callee:
callee is a property of the arguments object. It can be used to refer to the currently executing function inside the function body of that function. This is for example useful when you don't know the name of this function, which is for example the case with anonymous functions.
For example:
> foo = function() { console.log(arguments.callee); };
> bar = function() { foo() };
> bar();
function () { console.log(arguments.callee) }
However, I think this is being deprecated. The above link says, "The 5th edition of ECMAScript forbids use of arguments.callee() in strict mode."
Something like this works for the global Function.prototype.
Function.prototype.aaa = 1
(function () {}).aaa // => 1
But is there a way to put inherited properties of functions without changing Function.prototype?
function MyFunction () { return function () {} }
MyFunction.prototype.bbb = 2
// Can I have (new MyFunction).bbb ?
Your only other choice is to create your own Function factory function, since you've ruled out modifying the mechanism used by the standard Function factory. And of course, that's unlikely to do what you want it to do, since everyone would have to use your factory. :-)
Modifying the Function.prototype isn't necessarily evil. Prototype's been doing it for years, mostly to good effect.
function Wrap() {
var f = new Function;
/* for (var k in Wrap.prototype) {
f[k] = Wrap.prototype[k];
}
return f; */
// return $.extend(f, Wrap.prototype);
return _.extend(f, Wrap.prototype);
}
Wrap.prototype.foo = 42;
(Wrap()).foo === 42; // true
Create a new function then extend that function with properties from your Wrap.prototype.
Either use jQuery, underscore or a simple for loop.