I have an object, lets call it myObject, and a named function myFunction.
Can I create a method in myObject, lets call it myMethod, so that whenever I call myObject.myMethod I'll actually call myFunction.
I know i can create a new function for myMethod, and probably could make it be a wrappeI for myFunction, but is there a better way? or am I doing this all wrong
If I understand your question correctly, this is all you need to :
var myObject = {};
var myFunction = function() {/* code goes here*/};
myObject.myMethod = myFunction;
Because of how JavaScript works, though, this wouldn't become an object-specific function this way, so you couldn't track state with it. It would be similar to a static function in Java.
i think you are confusing your words. methods of an object are generally functions. like so-:
var myObject = {
x:0,
y:0,
getX: function () {
return this.x;
},
getY: function () {
return this.y;
}
};
you can add any number of methods/functions.
Related
Background
I want a function keeping track of its own state:
var myObject = {
myFunction: function () {
var myself = this.myFunction;
var firstTime = Boolean(!myself.lastRetry);
if (firstTime) {
myself.lastRetry = Date.now();
return true;
}
// some more code
}
}
The problem with the above code is that the value of this will depend on the site of the function call. I want the function to be able to refer to itself without using:
myObject.myFunction
.bind()
.apply()
.call()
Question
Is it possible to give a function this kind of self awareness independent of its call site and without any help from external references to it?
If you want to store that state on the function instance, give the function a name, and use that name within it:
var myObject = {
myFunction: function theFunctionName() {
// ^^^^^^^^^^^^^^^--------------------- name
var firstTime = Boolean(!theFunctionName.lastRetry);
// ^--------------------------- using it
if (firstTime) {
theFunctionName.lastRetry = Date.now();
// ^------------------------------------------------ using it
return true;
}
// some more code
}
};
You'd do that whenever you want to use a function recursively as well. When you give a name to a function that way (putting the name after function and before (), that name is in-scope within the function's own code. (It's not in-scope for the code containing the function if it's a function expression, but it is if it's a function declaration. Yours is an expression.)
That's a named function expression (where previously you had an anonymous function expression). You may hear warnings about NFEs, but the issues various JavaScript implementations had with them are essentially in the past. (IE8 still handles them incorrectly, though: More in this post on my blog.)
You might consider keeping that state somewhere private, though, via an IIFE:
var myObject = (function(){
var lastRetry = null;
return {
myFunction: function() {
var firstTime = Boolean(!lastRetry);
if (firstTime) {
lastRetry = Date.now();
return true;
}
// some more code
}
};
})();
Now, nothing outside that outer anonymous function can see lastRetry at all. (And you don't have to worry about IE8, if you're supporting stubborn XP users. :-) )
Side note: The unary ! operator always returns a boolean, so your
var firstTime = Boolean(!theFunctionName.lastRetry);
...is exactly equivalent to:
var firstTime = !theFunctionName.lastRetry;
...but with an extra unnecessary function call. (Not that it hurts anything.)
Of course you can, simply give your function an internal named representation and it can refer to itself from there. For example...
var obj = {
doThings:function doThingsInternal(arg1, arg2) {
console.log(arg1, arg2);
for (var arg in doThingsInternal.arguments) {
console.log(arg);
}
}
};
obj.doThings('John', 'Doe');
You could use a simple Closure, if you are not too bent on keeping state existence knowledge within the function. But I guess you don't want that. Another way to do this could be changing the function itself on the first call. Benefits, no/less state variables needed and no costly checks on subsequent calls! -
var myObject = {
myFunction: function () {
// Whatever you wanna do on the first call...
// ...
// And then...
this.myFunction = function(){
// Change the definition to whatever it should do
// in the subsequent calls.
}
// return the first call value.
}
};
You can extend this model to any states by changing the function definition per your state.
I've read the similar questions on SO and can't figure out what I'm doing wrong. I can't call the prototype method from within the constructor method.
I get: Uncaught TypeError: Object # has no method 'afterLoad'
var FiltersByDivision = function () {
$(document).on('afterLoad', this.afterLoad());
};
FiltersByDivision.prototype.afterLoad = function (){
console.log('afterLoad');
}
function OpenOrders() {
Widget.call(this);
FiltersByDivision.call(this);
this.widgetEndpoint = '/xxxxx';
}
OpenOrders.prototype = Object.create(Widget.prototype);
OpenOrders.prototype.constructor = OpenOrders;
There are a number of problems with this code:
You aren't inheriting from FiltersByDivision so thus an OpenOrders object does not have any FiltersByDivision methods. That's the reason why there is no afterLoad method.
$(document).on('afterLoad', this.afterLoad()); will execute this.afterLoad() immediately and pass it's return result as the event handler (which is not what you want). After you fix item 1, perhaps, you want $(document).on('afterLoad', this.afterLoad.bind(this));
There are many possible structures here. If FiltersByDivision is a separate object, then perhaps OpenOrders should just have one of those objects in its instance data like this (though if all it is doing is setting up an event handler, I'm not sure why it is a separate type of object):
var FiltersByDivision = function () {
$(document).on('afterLoad', this.afterLoad.bind(this));
};
FiltersByDivision.prototype.afterLoad = function (){
console.log('afterLoad');
}
function OpenOrders() {
Widget.call(this);
this.filter = new FiltersByDivision();
this.widgetEndpoint = '/xxxxx';
}
OpenOrders.prototype = Object.create(Widget.prototype);
OpenOrders.prototype.constructor = OpenOrders;
As jsfriend already pointed out is that afterLoad is not on ObjectOrders prototype. Doing a OtherConstructor.call does not inherit that constuctors prototype but initializes instance variables.
The value of this is the invoking object so the declaration of the function doesn't define its value but how you invoke it. You could use closures:
var FiltersByDivision = function ()
var me = this;
$(document).on('afterLoad', function(){
me.afterLoad();
});
};
More info on this, prototype and closures can be found here: https://stackoverflow.com/a/16063711/1641941
I am fairly new to javascript and jquery. I thought i had learned enough to do this, but apparently not. I think the code is fairly self explanatory.
The problem is that ArrowDir is apparently undefined. It looks defined to me, but I'm sure I'm missing something obvious.
function ArrowDir(){
var up = function(){
$("#arrow").attr('src','up_arrow.jpg');
};
var down = function(){
$("#arrow").attr('src','down_arrow.jpg');
};
}
$(function () {
if($("#toggle").onclick){
ArrowDir().down();
};
});
I've tried assigning the function as a variable, var ArrowDir = function(), but it doesn't seem to make a difference
You can't access the up and down values in the manner that you've written. You'd need to simply call down() from within the ArrowDir body, unless you've added those functions to the ArrowDir return value:
ArrowDir() {
var up = ...;
var down = ...;
return {
up: up,
down: down
};
}
Alternatively, if you're not using ArrowDir for anything other than encapsulating the up and down functions, you should just declare ArrowDir as an object, and call ArrowDir.up() and ArrowDir.down():
var ArrowDir = {
up: function () {
...
},
down: function () {
...
}
}
Assuming the missing quote after "#toggle is a typo, I'm not sure how you expect your code to work.
Here's how it runs, in prose:
Define a function ArrowDir.
When ready, attach a click handler
When clicked, call ArrowDir
In ArrowDir, define two local variables up and down, each with a function to do something.
There is no return statement, so return nothing
Call the down method of the "nothing" object. ERROR
See?
Try adding return {up:up,down:down}; to the end of your ArrowDir function.
You can create the object this way. In your function you are creating them insdied the scope of itself. SO it is not accessible outside.
function ArrowDir() {
}
ArrowDir.prototype.up = function () {
$("#arrow").attr('src', 'up_arrow.jpg');
};
ArrowDir.prototype.down = function () {
$("#arrow").attr('src', 'down_arrow.jpg');
}
and access it as
$(function () {
if($("#toggle").onclick){
var arrow = new ArrowDir(); // call the constructor to create a new instance
arrow.down(); // invoke the method
};
});
ArrowDir().down() here's your problem. Just because you define a variable in the ArrowDir function doesn't make it a method/property of it. You have to use Prototype Inheritance.
EDIT:
//Something like this:
ArrowDir.prototype.down = function() {
//Do stuff
}
Now you can call ArrowDir.down().
Doing this extends the properties (things it can do) of the object ArrowDir. You're adding a method to it.
The var up and down you have defined inside ArrowDir are not accessible outside the scope of that function.
The simplest way you could fix this, if this is a one-time kind of function, is just to put the up/down behavior in your jQuery function, like this:
$(function () {
if($("#toggle").onclick){
$("#arrow").attr('src','up_arrow.jpg');
} else {
$("#arrow").attr('src','down_arrow.jpg');
}
});
If you just want to namespace these functions for reusability you could make an object literal instead:
ArrowDir = {
up: function(){
$("#arrow").attr('src','up_arrow.jpg');
},
down: function(){
$("#arrow").attr('src','down_arrow.jpg');
}
};
Then you can call: ArrowDir.up() or ArrowDir.down() elsewhere.
For what it's worth, if your goal is just to namespace these functions for reusability, I would say the object literal syntax makes more sense to me.
Or, if you really want to do it as a function call, as Kolink has pointed out, the up/down functions need to be in the return value. You could write that like this...
function ArrowDir() {
var up = function(){
$("#arrow").attr('src','up_arrow.jpg');
}
var down = function(){
$("#arrow").attr('src','down_arrow.jpg');
}
return {up:up,down:down};
}
Or like this...
function ArrowDir() {
return {
up: function(){
$("#arrow").attr('src','up_arrow.jpg');
},
down: function(){
$("#arrow").attr('src','down_arrow.jpg');
}
};
}
Now calling ArrowDir().down() should work.
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.
Using KineticJS I figured out how to pass a function as the Gameboard function but all the this's inside the Gameboard function think's its now the object that got the function :(.
function Gameboard(){
//.... creates placeholders for stage layers and all objects
this.dice_layer=new Kinetic.Layer();
this.rolldice=function(){
alert(this.toString());
//..alter images
this.dice_layer.draw();//<~~ thinks this is circle once passed through setUpGameBoard says dice_layer is undefined. alert(this.toString()); shows this to be circle.
};
this.setUpGameBoard=function(){
// ...draws board pawns creates a circle object
var obj=this;//<~~ are there memory issues with this? Is there a better way?
circle.on("click",**obj**.rolldice.**bind**(obj);//** == ANSWER!!!!
};
};
The problem is this line:
this.doSomething=function(fnction){
You are declaring doSomething as a function with a single parameter, fnction but when you call it, you are passing two - a string and a function.
this.doSomething=function(str, fnction){
will behave as you expect it.
jsFiddle demo
Based on your "solution" to the second problem, it looks like you want to use ES5's bind. It allows to you specify the this for a particular function call since JavaScript really doesn't have "methods", you have to specify the object they operate on.
this.barfoo.doSomething(this.doBar.bind(this));
An example of malfunctioning code can be compared to the fix with bind.
Probably your simplification doesn't show up the real issue. I guess something like the following would be more similar to your problem:
function foo(){
this.doSomething = function(fnction){
fnction();
};
}
function bar(){
this.myField = "Buzz"
this.barfoo = new foo();
this.doBar = function(){
alert(this.myField);
};
this.barfoo.doSomething(this.doBar); // tried this
//this.barfoo.doSomething(this.doBar()); also tried this
//this.barfoo.doSomething(bar.doBar); also tried this
//this.barfoo.doSomething(bar.doBar()); also tried this
}
Where you can notice issues in accessing this-related properties.
If this is actually the problem, then you should be able to solve it by using either call or apply in foo's doSomething method:
function foo() {
this.doSomething = function (obj, fn) {
fn.call(obj);
};
}
And this is how you will use it in bar:
function bar() {
this.myField = "Buzz";
this.barfoo = new foo();
this.doBar = function () {
alert(this.myField);
};
this.barfoo.doSomething(this, this.doBar);
}
var myBar = new bar();
Check the jsFiddle.