I am trying to understand closures. In the code below, I create an instance of the constructor function Ninja and I call the instance kawazaki. I expected to be able to access the methods of Ninja. However, I am getting a TypeError: Object #<Ninja> has no method 'feints' instead.
The output I expected was 1.
Here is my code:
function Ninja() {
var feints = 0;
function getFeints() {
return feints;
}
function feints() {
feints++;
}
}
var kawazaki = new Ninja();
kawazaki.feints();
console.log(kawazaki.getFeints());
Try this instead:
var kawazaki = new Ninja;
kawazaki.feints();
alert(kawazaki.getFeints());
function Ninja() {
var feints = 0;
this.getFeints = function () {
return feints;
};
this.feints = function () {
feints++;
};
}
You need to assing public properties to this within the constructor function.
The scope of the functions getFeints and feints is limited to the function Nija. As you can not access to variables declared in a function, you can not access to those functions.
To be able to execute kawazaki.feints(), you have to "attach" the function to the Ninja function, as an object (which a function also is)
You will find in these resources several ways to achieve that, and also some deeper explanations:
How do JavaScript closures work?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
Closure is a very simple yet largely misunderstood topic.
function Ninja() {
var feints = 0;
function getFeints() {
return feints;
}
function feintsInc() {
feints++;
}
}
You have defined the closure alright but please note that a closure is not actually a method of the object. In order to get your desired output you need to call the closures just before closing the object.
feintsInc();
getFeints();
If,however, you wish to do it as
kawazaki.feintsInc();
you need to use this keywords in your function to get the functions assigned to the object.
Note,make sure your functions and variable names don't overlap.
Related
I just learned OOP and there is one little thing that I am not able to solve. The problem is a scope issue of some sort.
If I create a new object then how will I be able to give it access to my constructor function if the constructor function is inside another function? Right now I get undefined. Storing the function in a global variable wont do the job.
var example = new something x(parameter);
example.i();
var getFunction;
var onResize = function() {
getFunction = function something(parameter) {
this.i= (function parameter() {
// Does something
});
};
};
window.addEventListener('resize', onResize);
onResize();
For OOP javascript, the pattern should be like this.
//defining your 'class', class in quotes since everything is just functions, objects, primitives, and the special null values in JS
var Foo = function (options) {
// code in here will be ran when you call 'new', think of this as the constructor.
//private function
var doSomething = function () {
}
//public function
this.doSomethingElse = function () {
}
};
//actual instantiation of your object, the 'new' keyword runs the function and essentially returns 'this' within the function at the end
var foo = new Foo(
{
//options here
}
)
If I understand you, you want to know how to access variables inside another function. Your attempt is reasonable, but note that getFunction is not bound until after onResize is called. Here's a bit of a cleaner demo:
var x;
function y() {
// The value is not bound until y is called.
x = function(z) {
console.log(z);
}
}
y();
x('hello');
A common JavaScript pattern is to return an object that represents the API of a function. For example:
function Y() {
var x = function(z) {
console.log(z);
}
return {
x: x
};
}
var y = Y();
y.x('hello');
You should definitely read up on the basic concepts of JavaScript. Your code and terminology are both sloppy. I'd recommend Secrets of the JavaScript Ninja. It does a good job explaining scope and functions, two tricky topics in JavaScript.
When I run this code I get a TypeError because this.coverageList is undefined. I suspect this has to do with a closure issue I can't see. The method this.accumulateCoverage is passed to the forEach() method of an Array instance.
igv.Coverage = function (genomicInterval) {
this.accumulateCoverage = function (alignment) {
var i, j, blocksBBoxLength;
if (!this.coverageList) {
this.coverageList = new Array(genomicInterval.end - genomicInterval.start);
for (i = 0; i < this.coverageList.length; i++) {
this.coverageList[ i ] = 0;
}
}
};
genomicInterval.features.forEach(this.accumulateCoverage);
}
forEach should take a context as the second argument:
genomicInterval.features.forEach(this.accumulateCoverage, this);
But this may depend on whether you're using a polyfill. Support for forEach is relatively new, and may not be supported in all browsers.
The issue is that when you pass a function reference, it loses any association with the object it belongs to. It's just a function. It is only when you invoke the function using object.method() syntax that the function receives the implicit this argument (assigned to object). That's why functions that take as input and execute another function usually allow you to provide the object that should be passed as this as well.
An alternative pattern that might suit your purpose is to assign this to a local variable outside of your function, so that it is included in the function closure.
igv.Coverage = function (genomicInterval) {
var me = this;
function accumulateCoverage(alignment) {
var i, j, blocksBBoxLength;
if (!me.coverageList) {
//...
}
};
genomicInterval.features.forEach(accumulateCoverage);
}
This negates the need to pass around contexts like in the first solution.
I have an object in Javascript and want to process the data passed into the constructor using a function that could later be called externally with more data. Naturally I don't want to duplicate the code (once in the constructor, once in a function), so how should I best set this up?
I could use a nested function, but I'm told this is inefficient:
function MyOb(data) {
this.myData = {};
function addData(newData) {
//Add newData to myData
}
addData(data);
}
But if I use a prototype I get a "can't find variable addData" error on line 3:
function MyOb(data) {
this.myData = {};
addData(data);
}
MyOb.prototype.addData = function(newData) {
//Add newData to myData
}
So am I forced to either use a nested function or repeat myself, or is there a way of making this work using prototype?
Change
function MyOb(data) {
var myData;
addData(data);
}
to
function MyOb(data) {
this.myData = {}; // or another initialization
this.addData(data);
}
You need the explicit this in JavaScript objects.
Note also that using var myData makes it private : you won't be able to use this variable from functions defined outside the constructor, including the addData function. That's why you probably need this.myData instead.
Using the prototype you need to create a new object:
var abc = new MyOb(data);
then you can access the function using the this:
function MyOb(data) {
var _myData; // local variable in this scope (addData won't have access)
this.myData = {}; // public variable
this.addData(data);
}
If you don't use the newto build your object, then the thiswill be the window, and your code won't work
You should just make the nested function publically available as a property on your object:
function MyOb(data) {
var myData;
function addData(newData) {
//Add newData to myData
}
addData(data);
this.addData = addData;
}
Your current code with the prototype suffers from the problem that you're trying to get the function like a variable, while it's a (inherited) property of your instance (referenced by this). However, changing it to
this.addData(data);
would lead to the error can't find variable myData in the prototype function - it's a variable that is local to the constructor. You would have to make that a instance property as well to use the prototype. See also Javascript: Do I need to put this.var for every variable in an object?.
I am trying to understand the way that javascript passes functions around and am having a bit of a problem groking why a prototype function can NOT access a var defined in a function constructor while a function defined in the constructor can access the var. Here is code that works:
var model = function model() {
this.state = 1;
this.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
}
var othermodel = function othermodel(mdl) {
this.GetStateFn = mdl.GetState;
}
othermodel.prototype.WriteState = function() {
console.log(this.GetStateFn.call());
};
var m = new model();
var o = new othermodel(m)
o.WriteState();
This works and makes sense - the GetState() function can access this.state.
However, if I create GetState as follows:
model.prototype.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
The result will be an error that scope is not defined.
I would prefer to have this work with the prototype method as I do not want a copy of the function in ever model, but it would seem that prototype can't work because it can't access the specific instance of the model.
So, can someone provide me with a good explanation of a) what I need to do to get this to work with prototype (assuming I can) and b) if I can't get it to work with prototype, what is the reason so I can understand better the underpinnings of the issue.
Why not simply write the function this way
model.prototype.GetState = function() { return this.state; }
var othermodel = function othermodel(mdl) {
this.GetStateFn = mdl.GetState.bind(mdl);
}
othermodel.prototype.WriteState = function() {
console.log(this.GetStateFn.call());
};
The above code will work, as in most cases you will execute code like m.GetState(). That is an example of invoking a function as an object method. In that case, this is guaranteed to point to the object m. You seem to know how the prototype chains work, so I won't go there.
When assigning the function reference to the other model, we use the .bind to ensure that within GetState, this points to mdl. Reference for bind: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
Your original IIFE's were in effect your implementation of bind. The issue was the value of this was wrong. Currently, every time you need to assign models function to some other function, you will need to use bind at all those times. You have tagged your question as node.js, bind is available on the Function prototype in node.js and any ES5 compatible browser. If you need to run the above code on older browsers or environments that do not support bind, replace bind with your IIFE.
As for why your code isn't working,
model.prototype.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
Here, this doesn't refer to the eventual model object (m). this can refer to any one of 5 options in javascript. Refer: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this
Lets assume the above code is in an html file inside some script tag. Then this will refer to the window object. window doesn't have any property called state, hence the undefined. If you were to console.log(this.m, this.o) at the end of you script, you would see the respective m and o objects.
When defined like this:
var model = function model() {
this.state = 1;
this.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
}
the anonymous function is declared and immediately executed. this is passed as a parameter to that self-executing function. As a result - a new function is returned, but this function has scope parameter in its closure - so that it is accessible after the scope has exited. As a result - the function, when invoked, can still access state property of that "enclosed" this (the one that became scope and was closed upon).
If you define it like this:
model.prototype.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
the mechanism is the same, it's just this is not. It is now the context of the scope you execute the above code in. Assuming it's done in global scope - it would be window object.
If you don't want to use bind because of its support with older browsers, you could try this:
http://jsfiddle.net/j7h97/1/
var model = function (state) {
this.state = state || new Date().getTime();
};
model.prototype.GetState = function () {
return this.state;
};
model.prototype.WriteState = function () {
console.log("model WriteState: " + this.GetState());
};
var othermodel = function othermodel (mdl) {
this.GetStateFn = function () {
return mdl.GetState.call(mdl);
};
};
othermodel.prototype.WriteState = function () {
console.log("othermodel WriteState: " + this.GetStateFn());
};
var model1 = new model();
model1.WriteState();
var othermodel1 = new othermodel(model1);
othermodel1.WriteState();
var model2 = new model();
model2.WriteState();
var othermodel2 = new othermodel(model2);
othermodel2.WriteState();
Seems to do what you want without bind. I created the model.prototype.WriteState for testing purposes.
It depends on where it is called. If it's in global scope, this will not refer the the model. If it's running in a browser it will refer to the global window object instead.
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."