I'm no doubt doing something dumb here, but the following code results in an
error "this.getString is not a function."
This occurs because when unrelatedInstance calls stringGetter, "this" in showCombinedStrings() has the value of Unrelated....which actually seems fair enough, but how could this be set up so that it would work?
function BaseStringGetter() {
this.getString = function () {
return 'this is from BaseStringGetter';
}
}
function DerivedStringGetter() {
this.showCombinedStrings = function () {
console.log( 'this is from DerivedStringGetter and... ' + this.getString() );
}
}
DerivedStringGetter.prototype = new BaseStringGetter();
var stringGetterInstance = new DerivedStringGetter();
function Unrelated() {};
var unrelatedInstance = new Unrelated();
unrelatedInstance.stringGetter = stringGetterInstance.showCombinedStrings;
unrelatedInstance.stringGetter();
One option is this:
unrelatedInstance.stringGetter =
stringGetterInstance.showCombinedStrings.bind(stringGetterInstance);
unrelatedInstance.stringGetter();
Here, you're using Function.prototype.bind() to make this inside of unrelatedInstance.stringGetter() always refer back to stringGetterInstance.
The problem is how you are calling unrelatedInstance.stringGetter();
Even though stringGetter refers to the showCombinedStrings function, this inside showCombinedStrings now refers to the unrelatedInstance instance which does not have the toString() property that is why the error.
Demo: Fiddle
Here the value of this is printed as Unrelated {stringGetter: function} which is not an DerivedStringGetter instance
One easy solution is to use .bind() to give a custom execution context to unrelatedInstance.stringGetter like
unrelatedInstance.stringGetter = stringGetterInstance.showCombinedStrings.bind(stringGetterInstance);
Demo: Fiddle
Now even if you call unrelatedInstance.stringGetter(), this inside showCombinedStrings will refer to the stringGetterInstance instance.
When you call a function on an object, this will refer to the object the function is invoked on even if the function was originally defined elsewhere.
When you call unrelatedInstance.stringGetter();, this inside the function will now refer to unrelatedInstance which doesn't have getString(). The MDN this reference page has more info.
You could do something like this to preserve the original context:
unrelatedInstance.stringGetter = function() {
return stringGetterInstance.showCombinedStrings();
}
Edit: I left out bind that the other answers have now mentioned since it doesn't exist in IE8 but you'd be fine using that if you have a shim or don't care about old browsers.
Related
Can I, in JavaScript, add a function to an already existing function or object that a function within that object then "suddenly" can "see" and call itself? Here is an example to demonstrate:
function CreateThing() {
function callAddedFunction() {
theFunction(); // this does not exist yet!
}
}
So theFunction() obviously does not exist in createThing(). Is there any way to add that outside so that when I then invoke callAddedFunction() it is able to resolve that? Something like:
let f = new CreateThing();
addFunctionAtRuntime(f, "theFunction", function() {
console.log("YAY!");
};
f.callAddedFunction();
I have tried to experiment with prototype, but I have been unable to do this. Note that the main reason for me wanting to do this is "fake" object inheritance without resorting to classes and inheritance as that requires the this keyword in front of every function call. I also want to avoid having to pass an object in that function as a parameter that can be called through in order to reach those other functions. I know that I can achieve this by having all those extra functions in global scope, but I have hoped to avoid that if possible.
EDIT: I have modified my example with the magic function I was looking for called addFunctionAtRuntime which from what I have understood is not possible. Some suggest I use eval and just make those functions available in the eval script, but so far I have been able to do this by creating a script tag dynamically and add my code as content including those functions I wanted callAddedFunction() in my example above to be able to see (without having to call through some object context).
I'm not sure this is exactly what you want but you can also use a generic higher-order function that returns the implementation you are looking for.
const supplimentor = (src, extraFunc) => ({
src: new src(),
extraFunc
})
//OR
function supplimentor1(src, extraFunc) {
this.extraFunc = extraFunc;
new src();
}
function CreateThing() {console.log('SOURCE')}
const extraFunc = () => console.log('EXTRA');
const newFunc = supplimentor(CreateThing, extraFunc)
newFunc.extraFunc()
const newFunc1 = new supplimentor1(CreateThing, extraFunc)
newFunc1.extraFunc()
Just in case the OP ...
... is not in need of something as complex as method modification as described / demonstrated at e.g.
"Can I extend default javascript function prototype to let some code been executed on every function call?"
"Intercepting function calls in javascript" ...
... why doesn't the OP just provide the very function object as parameter to the Thing constructor at the thing object's instantiation time?
After all it comes closest to (or is exactly) what the OP describes with ...
Can I, in JavaScript, add a function to an already existing function or object that a function within that object then "suddenly" can "see" and call itself?
function Thing(fct) {
this.callAddedFunction = () => fct();
}
const thing = new Thing(() => console.log("YAY!"));
thing.callAddedFunction();
I wonder if somebody could please help me understand something that seems odd with JS?
The below code works. The function inlineEditEvent.init() is called, and then t.copy() is called correctly (where var t = this;).
However, if I was to replace that with this.copy(), I get the error this.copy is not a function.
What's the difference here? Why does the below work, but not the way as described in the last paragraph? Thanks.
jQuery(function($){
$(document).ready(function(){inlineEditEvent.init();});
inlineEditEvent = {
init : function(){
var t = this;
/** Copy the row on click */
$('#the-list').on('click', '.row-actions a.single-copy', function(){
return t.copy();
});
}, // init
copy : function(){
// Do stuff here
}
} // inlineEditEvent
});
You're setting t as a context variable of this (of your init function). Once inside your click handler, this is now referring to the click handler, no longer the init function. Therefore, this.copy() is not a function.
this refers to this within the functions scope. That's why you need to set a self variable, so it's accessible within the scope of the function. Considering you're using jQuery, you could use $.proxy:
$.proxy(function(){
return this.copy();
},this)
t.copy(); appears in a different function to var t = this;. The value of this changes inside each function.
When you say var t= this; it refers to what this meant in that context. Later on when you are trying to refer to this, it is referring to a.single-copy instead since that is the new context it is in.
I have some JavaScript code that works as it should. However, I'm finding it a bit difficult to explain why it actually works. I hope, someone can make it clear to me.
I have an object that must respond to certain events, e.g. click events. Part of the object looks like this:
Maps.Marker = function (id, data, clickEvent) {
this.id = id;
this.data = data;
this.clicked = clickEvent;
};
The object is rendered in a Google map, so when the object is clicked in the map, I want to bubble the event to the clickEvent. Part of that code looks like this:
if (marker.clicked) { // click handler defined
google.maps.event.addListener(m, "click", function () {
marker.clicked();
});
}
Please note I've left out a lot of code here for brevity and know that it looks wrong as pasted here. The important thing is that the marker.clicked() function is invoked inside the Google Map event listener.
So, when my marker object is instantiated, it looks something like this:
var objClicked = function () {
if (this.data != null) {...}
...
}
var obj = new Maps.Marker("1", { "some object data" }, objClicked);
What I do not understand totally is how the this.data actually works in the objClicked function (I can access "some object data".
Can someone please explain it to me?
The reason lies in the way of the this keyword in javascript. When you assign a function to a property inside an object and you later call this function, marker.clicked(), the this inside the function is set to whatever is on the left side of the dot, which in this case is marker.
UPDATE
Here is a more thorough explanation: http://www.impressivewebs.com/javascript-this-different-contexts/
You invoke the function like this:
marker.clicked();
Because the reference to the function comes from that "clicked" property of the object that "marker" refers to, that object is used for the this value in the function. That's just how JavaScript works.
Note that if you did something weird like this:
var wrong = {};
wrong.clicked = marker.clicked;
wrong.clicked();
then your code would not work, because this would refer to that "wrong" object.
So, in general: if an object has a property, and the property value is a reference to a function, and you invoke that function via the reference, then this in the function will refer to that object. That binding happens on each individual function call; there's no permanent relationship between a function and an object. (You can get the effect of a permanent relationship with something like .bind().)
This question is best explained with some code, so here it is:
// a class
function a_class {
this.a_var = null;
this.a_function = a_class_a_function;
}
// a_class::a_function
function a_class_a_function() {
AFunctionThatTakesACallback(function() {
// How to access this.a_var?
});
}
// An instance
var instance = new a_class();
instance.a_function();
From within the callback in AFunctionThatTakesACallback(), how does one access this.a_var?
You'll need to expand the scope of this by creating a local variable that references it, like this:
function a_class_a_function() {
var self = this;
AFunctionThatTakesACallback(function() {
console.log(self.a_var);
});
}
The reason why you need to do this is because the this reference within the AFunctionThatTakesACallback function is not the same this as the current object, it will likely reference the global windowobject instead. (usually not what you want).
Oh, did I mention that this is called a closure?
You could try using the call method of function objects, which lets you specify a value for this:
myFunction.call(this, args...)
But I think that in this case it would probably be more straightforward to pass 'this' in as one of the parameters to the callback.
When you call instance.a_function(), you're really calling a_class_a_function with instance as this, so you can modify a_class_a_function like so:
function a_class_a_function() {
var self = this;
AFunctionThatTakesACallback(function() {
// do something with self.a_var
});
}
The problem here is that if you attempt to call a_class_a_function without calling it from an instance, then this will likely refer to the global object, window.
This is a very old problem, but I cannot seem to get my head around the other solutions presented here.
I have an object
function ObjA() {
var a = 1;
this.methodA = function() {
alert(a);
}
}
which is instantiated like
var myObjA = new ObjA();
Later on, I assign my methodA as a handler function in an external Javascript Framework, which invokes it using the apply(...) method.
When the external framework executes my methodA, this belongs to the framework function invoking my method.
Since I cannot change how my method is called, how do I regain access to the private variable a?
My research tells me, that closures might be what I'm looking for.
You already have a closure. When methodA is called the access to a will work fine.
Object properties are a different thing to scopes. You're using scopes to implement something that behaves a bit like ‘private members’ in other languages, but a is a local variable in the parent scope, and not a member of myObjA (private or otherwise). Having a function like methodA retain access to the variables in its parent scope is what a ‘closure’ means.
Which scopes you can access is fixed: you can always access variables in your parent scopes however you're called back, and you can't call a function with different scopes to those it had when it was defined.
Since a is not a property of this, it doesn't matter that this is not preserved when calling you back. If you do need to get the correct this then yes, you will need some more work, either using another closure over myObjA itself:
onclick= function() { myObjA.methodA(); };
or using Function#bind:
onclick= myObjA.methodA.bind(myObjA);
yes, you're right. Instead of a method reference
var myObjA = new ObjA();
libraryCallback = myObjA.methodA
pass a closure
libraryCallback = function() { myObjA.methodA() }
If you are using jQuery javascript framework, easiest way is to use proxy:
$('a').click($.proxy(myObjA, 'methodA'));
I'd do this:
function ObjA() {
this.a = 1;
this.methodA = function() {
alert(this.a);
}
}
function bindMethod(f, o) {
return function(){
return f.apply(o, arguments);
}
}
var myObjA = new ObjA();
myObjA.methodA = bindMethod(myObjA.methodA, myObjA);
...
Where bindMethod binds the methodA method to always be a method of myObjA while still passing on any arguments which function() {myObjA.methodA()} doesn't do.