I'm trying to solve this puzzle minded Javascript OOP problem.
So I have the following class :
var ClassA = function() {
this.initialize();
}
ClassA.prototype = {
methods : ['alpha','beta','gama'],
initialize : function() {
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
}
}
var a = new ClassA();
When I call every method I expect to print the name of it, right? But here is what i get :
a.alpha(); // returns gama ?!?
a.beta(); // returns gama ?!?
a.gama(); // returns gama
But when my class looks like this :
var ClassB = function() {
this.initialize();
}
ClassB.prototype = {
methods : ['alpha', 'beta', 'gama'],
initialize: function() {
for ( var i in this.methods ) {
this.addMethod(this.methods[i]);
}
},
addMethod: function(method) {
this[method] = function() {
console.log(method);
}
}
}
var b = new ClassB();
b.alpha(); // returns alpha
b.beta(); // returns beta
b.gama(); // returns gama
Why is this happening ?
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
Your problem lies here. When this loop ends, i is the last element. Each function uses the same i, so they are all the last element.
When you use addMethod you are making a closure to "capture" the correct value.
EDIT: When you call addMethod you are "copying" the value, instead of using the i value, which changes with each loop iteration.
In your first version:
initialize : function() {
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
}
The methods that you create within initialize all refer to the same i variable from initialize - and after initialize runs i has the value "gama", so regardless of which of the methods you call that's the value of i that they'll log to the console. JS doesn't store the current value of i at the time the method is created.
JS creates a "closure" for each function - variables declared in your initialize function (i.e., i) continue to be in scope for the nested function(s) even after initialize has finished.
The second version calls addMethod to add each method:
addMethod: function(method) {
this[method] = function() {
console.log(method);
}
}
...and so when they run they'll refer to their own "copy" of the method parameter because then there is a separate closure for each of the methods.
Edit: See also this question: How do JavaScript closures work? (several answers there explain this more clearly than I did).
You can fix your first example by adding an anonymous closure:
initialize : function() {
for ( var i in this.methods ) {
(function (i) { // anonymous closure
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}).call(this, i); // use .call() if you need "this" inside
}
}
Now it will work the same way as your second example. "Anonymous" means that the closure is made by function which doesn't have a name and is called instantly as it is "created".
Note sideways: use .call(this, ...) to preserve this inside the called function, or you can do var that = this, use that instead of this and call the function normally:
for ( var i in this.methods ) {
var that = this;
(function (i) { // anonymous closure
that[that.methods[i]] = function() {
console.log(that.methods[i]);
}
})(i); // Called normally so use "that" instead of "this"!
}
Well, first of all stop using for (property in object) loops on Arrays. It's all fun and games until somebody prototypes to the Array object which is both a perfectly reasonable and very useful/popular thing to do. This will result in custom methods getting added to your for x in array loops.
As for the problem, it's doing exactly what you told it to do in version 1. The problem is that by the time you get around to firing it, i is the last thing i was, 'gamma'. When you pass a reference into a function as an argument, the function holds on to the value's state as it was passed.
Related
I'm just studying javascript and I faced an issue related to scoping.
Here's the code
function User (properties) {
for (var i in properties) {
(function () {
this ['get' + i] = function () {
return properties [i];
};
}) ();
}
}
var me = new User ({
Id : 54,
Name : 'ohyou'
});
console.log (me.getName ());
console.log (me.getId ());
How I want it to work: it should create two functions getName and getId that belong to the me object.
How it works: it creates two functions just as I want, but they belong to the window
What I tried:
I tried removing the function on the line 3. It does what I want, but now it returns the name "ohyou" two times, instead of returning the id and the name
I tried using the functions from the window scope, the problem as in the previous case persists - it returns the name twice.
The question: what am I doing wrong and how do I make it work as I want?
P.S. Making this post using phone, sorry for possible typos or formatting issues. Also, please forgive me my english skills.
Try to understand why the following code works:
function User(properties) {
for (var i in properties) {
with ({ i: i, self: this, props: properties }) {
self["get" + i] = function () {
return props[i];
};
}
}
}
var me = new User({
Id: 54,
Name: "ohyou"
});
alert(me.getName());
alert(me.getId());
This is an example of one of the legitimate uses of the with statement.
I find the with statement to be more succinct than using an immediately invoked function expression (IIFE) as others are suggesting.
Edit: The with keyword is not bad if you use it correctly. There are some legitimate uses of with:
http://webreflection.blogspot.in/2009/12/with-worlds-most-misunderstood.html
The reason your code doesn't work is because every function has it's own value of this. Hence when you immediately invoke the function expression within the for loop, the value of this inside the function is no longer your new object. It is window. To solve that problem you could do something like:
(function (i, self, props) {
self["get" + i] = function () {
return props[i];
};
}(i, this, properties))
However using the with statement is clean and faster. It's faster because you're not calling a function.
function User (properties) {
var that = this;
for (var i in properties) {
(function (i) {
that ['get' + i] = function () {
return properties [i];
};
}) (i);
}
}
var me = new User ({
Id : 54,
Name : 'ohyou'
});
You can avoid all these closures and other things if you use Object.keys and then forEach, which already introduces a new scope. Then you'd have to pass the this value, which is the second parameter:
function User(properties) {
Object.keys(properties).forEach(function(k) {
this['get'+ k] = function() {
return properties[k]
}
},this)
}
One thing you must remember, any function that does not belong to any object, will always belong to window object.
For example if we modify your object and add a new method,
me.doSomeWork = function(){
this.myLuckyNumber = 10;
var that = this;
function doubleMyLuckyNumber(){
console.log(this); //window
that.myLuckyNumber = that.myLuckyNumber * 2;
}
doubleMyLuckyNumber();
};
me.doSomeWork();
console.log(me.myLuckyNumber) //outputs 20
So always save the reference to a var to use it in inner methods. You can use any of the way that others suggested but I prefer James emanon's approach.
My code is very simple. Ans to me it should work.
var preview = WinJS.Class.define(
function (el, options) {
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
But when click occurs this.form seems to be undefined of null. Why? I do not want to initialize objects in every method of the class.
New tests
I made additional test very small
var preview = WinJS.Class.define(
function (el, options) {
var test = 1;
this.test = 1;
this.test1();
},
{
test1: function () {
console.log(this.form, test);
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
This test fails on line this.test1();. What I think now that this class is called RegCtrl.preview() rather than new RegCtrl.preview().
How do I shek inside the function that this called as new but not a simple function?
The other answers aren't explaining what's going on, and as such are giving incorrect advice.
JavaScript has first-class function objects - you can pass them around as values. That's exactly what you're doing when you set up this callback:
this.preview.addEventListener('click', this.click, false);
You're taking the contents of the this.click property, which happens to be a function, and handing it to the addEventListener function to do whatever it wants with it.
I was very specific about terminology there - note I specifically said function, not method. JavaScript doesn't really have a method construct, it just has methods as properties on an object.
So where does the "this" member come from? It's determined at the caller - the object you use on the left side of the '.' is the one that becomes the value of this. For example,
function exampleFunc() { console.log("this.myName = " + this.myName); }
var a = { myName: "Chris", doSomething: exampleFunc };
var b = { myName: "Bob", doSomething: exampleFunc };
Note I've assigned the exact same function to the doSomething properties. What what happens:
a.doSomething(); // Outputs "this.myName = Chris"
b.doSomething(); // Outputs "this.myName = Bob"
The exact same function object, called through two different objects, has a different this pointer.
exampleFunc is a global function, let's call it:
exampleFunc() // Outputs "this.myName = undefined"
So where'd the undefined come from? In a global function, "this" is set to window (the global scope), which didn't have the myName property defined. Which also means that you could do this instead:
myName = "Global Name"; // note, no var - we want this global
exampleFunc(); // Outputs "this.myName = Global Name"
Ok, so what's going on with the original question? Basically, you've passed the function this.click to be the callback, but you haven't passed the "this" pointer that you want it called through. Actually, addEventListener doesn't have a way to pass the this pointer. As a result, when the function is invoked this is not pointing at your object. I don't remember off the top of my head what it's pointing at - it's either window or the element that was clicked on, check the DOM documentation to verify.
To get it to call the right function with the right context (context = the correct "this"), the traditional approach is to use a closure. Capture "this" in a variable, then pass in an anonymous function that calls your actual callback with the right this pointer. The code looks like this:
var preview = WinJS.Class.define(
function (el, options) {
// Capture your current this pointer in a global variable
// Using "that" as the name comes from JavaScript: The Good Parts book
var that = this;
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
// Note what gets passed instead of this.click:
this.preview.addEventListener('click',
function (e) {
// NOTE: Calling through "that": "this" isn't pointing to the right object anymore
// Calling through "that" resets "this" inside the call to click
that.click(e);
}, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
This is a common enough pattern that ECMAScript 5 has a utility function to build these wrappers for you - function.bind. Do this:
this.preview.addEventListener('click',
this.click.bind(this),
false);
The construct this.click.bind(this) will construct a new function that, when called, will set the "this" reference to whatever you passed (in this case "this"), and then invoke the function you called it on.
Yes, there are a lot of different values for "this" floating around. Keeping track of what "this" is pointing at is an important part of mastering JavaScript programming.
I think you may want to define a global JavaScript variable as :
var myForm = document.getElementById('perview-form');
or jest define var myForm; and initialize inside function (el, options) as:
myForm = d.getElementById('perview-form');
Now you can use this variable in your function as :
myForm.style('display', 'block');
EDIT: I believe you may define this variable as first line in your WinJS.Class.define to make it instance level variable as below:
var preview = WinJS.Class.define(
var myForm;
function (el, options) {
....
....
myForm = d.getElementById('perview-form');
...
},
{
click: function (e) {
myForm.style('display', 'block');
}
});
This is a really hard thing to research if you don't know what to look for. I added one line and changed another line. That should fix your issue.
In short, the keyword this gets reset every time you enter a new function, this the value of this inside your click function is not the same this of the outer scope. Preserve this this you want. The name of that seems fairly common.
Edited based on the link provided by the OP.
This code is UNTESTED. If using this doesn't work now, then I'd try this2
Sorry I can't test this, but I don't have the framework anywhere so I'm doing
educated guesswork.
var preview = WinJS.Class.define(
function (el, options) {
that = this; // No var should be needed since it is declared already
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
// This is the section for instance vars
{
click: function (e) {
that.form.style('display', 'block'); // AND THIS ONE
},
that: null // Added instance variable
},
// And these are static variables
{
that2: null
}
);
http://jsfiddle.net/ZLH7J/1/
What the jsFiddle and code below shows are two examples that essentially do the same thing. When trying to call first(); or this.first(); in either example, an undefined error is thrown. I can call the functions later through the instance, but not when trying to instantiate the object using init(){...}() like a constructor. I put init() at the bottom thinking it was an order of operations thing, but that is not the case. This does not work the way I thought it would work.
I am curious to understand how this is supposed to be done, and why this cannot be done.
//create and return an obj
var fishSticks = function(){
return {
first: function(){
document.getElementById('output').innerHTML="Success";
},
init: function(){
try{
first(); //err
this.first(); // also err
}catch(e){
document.getElementById('output').innerHTML=e.toString();
}
}()
}
}
//do function stuff and then return 'this'
var fishFillet = function(){
var first = function(){
document.getElementById('output2').innerHTML="Success";
}
var init = function(){
try{
first(); //err
this.first(); // also err
}catch(e){
document.getElementById('output2').innerHTML=e.toString();
}
}()
return this;
}
var test = new fishSticks();
var test2 = new fishFillet();
You need to understand two things:
1) JavaScript does not automatically insert this like Java does, so the first() call will only look through the lexical scope for a definition of first, it will nok look at the this object. Therefore the call to first() should work but this will be bound to something else than what you might expect inside first.
2) Local variables in a constructor do not become members of the constructed object.
In your second example, if you comment out the call in "init" to this.first() then you get the "Success" message.
The first version doesn't work because JavaScript simply does not allow for references to be made within an under-construction object to the object itself. There's just no way to do it.
The second one works (well the simple reference to "first" works) because "first" is declared as a local variable. Local variables are not properties of any object, and in particular they're not properties of the object allocated when the function is called with new. That's why this.first() doesn't work.
In the second one, you could make this.first() work by declaring things differently:
var fishFillet = function(){
this.first = function(){
document.getElementById('output2').innerHTML="Success";
}
var init = function(){
try{
this.first(); //will work
}catch(e){
document.getElementById('output2').innerHTML=e.toString();
}
}()
return this;
}
Also, for what it's worth, the weird anti-pattern of
var something = function() { ... }
is not as useful as
function something() { ... }
There's no reason to use the var declaration instead of the function declaration.
How about...
var fishFillet = function () {
var first = function () {
document.write( 'Success' );
};
var init = function () {
first();
};
init();
return {
first: first
};
};
And then:
var ff = fishFillet(); // calls init() which calls first()
ff.first(); // call first() manually
Live demo: http://jsfiddle.net/uaCnv/
So, first you define all your functions, next you manually invoke init, and last you return an object containing those functions which should be available through the resulting object (as methods).
Since you are using both as a constructor, format them as such:
function fishFillet(){
this.first = function(){
document.getElementById('output2').innerHTML="Success";
}
this.init = function(){
try{
this.first();
}catch(e){
document.getElementById('output2').innerHTML=e.toString();
}
}
}
var food = new fishFillet();
food.init();
The reason it wasn't working for you is b/c "first" is created as a local varaible, and is deleted after execultion. Init isn't being called until after the execution of the constructor has finished
i have JavaScript components, that has following architecture:
var MyComponent = function(params)
{
setup(params);
this.doSomething()
{
// doing something
};
function setup(params)
{
// Setup
// Interaction logic
var _this = this; // "this" points to DOMWindow, not to created object
$(".some-element").click(function(){
_this.doSomething(); // it craches here, because of above
});
}
};
When something, being controlled by interaction logic, happens, sometimes i must forward execution to "public" methods of component.
In this situation, i have a problem with "this" pointer.
Sample code demonstrates it:
var Item = function()
{
this.say = function()
{
alert("hello");
};
this.sayInternal = function()
{
_sayInternal();
};
function _sayInternal()
{
this.say();
};
};
To test it,
Create an object:
var o = new Item();
This works fine:
o.say(); // alerts "hello"
This crashes:
o.sayInternal();
I get an error:
TypeError: Result of expression 'this.say' [undefined] is not a function.
I think, such a behaviour takes place, because _sayInternal() function is declared (and not assigned to object, like "this.say = function()"). This way, it is shared across all created objects and acts like a static function in C++.
Is this true ?
No, sayInternal is not shared between created objects. But you are right, the created objects don't have access to sayInternal as it is not assigned to them. This function is only local to the constructor function.
this always refers to the context a function is invoked in. If you call it like func(), then this refers to the global object (which is window in browser). If you set the function as property of an object and call it with obj.func(), then this will refer to obj.
If you assign a "bound" function to a variable and call it:
var method = obj.func;
method();
then this will again refer to the global object. In JavaScript, functions are like any other value, they don't have a special relationship to the object they are assigned to.
You can explicitly set the context with call or apply:
var MyComponent = function(params)
{
setup.call(this, params); // <- using `call`
this.doSomething()
{
// doing something
};
function setup(params)
{
// Setup
// Interaction logic
var _this = this; // "this" to new created object
$(".some-element").click(function(){
_this.doSomething();
});
}
};
or in you other example:
var Item = function()
{
this.say = function()
{
alert("hello");
};
this.sayInternal = function()
{
_sayInternal.call(this);
};
function _sayInternal()
{
this.say();
};
};
That said, this approach to assign functions to objects is not good, because every instance will have its own this.sayInternal function. So for the Item code above, every creation of an instance involves creating three functions too, which is a waste of memory.
Making use of prototype inheritance would be a better way:
var Item = function() {
};
Item.prototype = (function() {
function _sayInternal() {
this.say();
};
return {
say: function() {
alert("hello");
},
sayInternal: function(){
_sayInternal.call(this);
}
}
}());
This way, _sayInternal is only created once and all instances inherit (refer to) the prototype, so say and sayInternal also exist only once. The "trick" with the immediate function makes _sayInternal only accessible by say and sayInternal.
Are the JavaScript code snippets given below some sort of function declaration? If not can someone please give an overview of what they are?
some_func = function(value) {
// some code here
}
and
show:function(value){
// some code here
}
There are six ways/contexts in which to create functions:
1) Standard declarative notation (most familiar to people with C background)
function foo() {}
All the rest are function expressions:
2) As a method of an object literal
var obj = {
foo: function() {}
};
3) As a method of an instantiated object (created each time new is exectued)
var Obj = function() {
this.foo = function() {};
};
4) As a method of a prototype (created only once, regardless of how many times new is executed)
var Obj = function() {};
Obj.prototype.foo = function() {};
5) As an anonymous function with a reference (same effect as #1) *
var foo = function() {};
6) As an immediately executed anonymous function (completely anonymous)
(function() {})();
* When I look at this statement, I consider the result. As such, I don't really consider these as anonymous, because a reference is immediately created to the function and is therefore no longer anonymous. But it's all the same to most people.
The first one is simply creating an anonymous function and assigning it to a variable some_func. So using some_func() will call the function.
The second one should be part of an object notation
var obj = {
show:function(value){
// some code here
}
};
So, obj.show() will call the function
In both cases, you are creating an anonymous function. But in the first case, you are simply assigning it to a variable. Whereas in the second case you are assigning it as a member of an object (possibly among many others).
First is local (or global) variable with assigned anonymous function.
var some_name = function(val) {};
some_name(42);
Second is property of some object (or function with label in front of it) with assigned anonymous function.
var obj = {
show: function(val) {},
// ...
};
obj.show(42);
Functions are first-class citizens in JavaScript, so you could assign them to variables and call those functions from variable.
You can even declare function with other name than variable which that function will be assigned to. It is handy when you want to define recursive methods, for example instead of this:
var obj = {
show: function(val) {
if (val > 0) { this.show(val-1); }
print(val);
}
};
you could write:
var obj = {
show: function f(val) {
if (val > 0) { f(val-1); }
print(val);
}
};
One way of doing it:
var some_func = function(value) {
// some code here
}
Another way:
function some_funct() {
}
Yet another way:
var some_object={};
some_object["some_func"] = function() {};
or:
var some_object={};
some_object.some_func = function() {};
In other words, they are many ways to declare a function in JS.
Your second example is not correct.
The first one is a function declaration assigned to a variable (at least it should be, despite the fact that it's missing the variable type declaration first), the second one is probably related to a object declaration.
They are called anonymous functions; you can read more about them here:
http://www.ejball.com/EdAtWork/2005/03/28/JavaScriptAnonymousFunctions.aspx
The first example creates a global variable (if a local variable of that name doesn't already exist) called some_func, and assigns a function to it, so that some_func() may be invoked.
The second example is a function declaration inside an object. it assigns a function as the value of the show property of an object:
var myObj = {
propString: "abc",
propFunction: function() { alert('test'); }
};
myObj.propFunction();
The first one...
some_func = function(value) {
// some code here
}
is declaring a variable and assigned an anonymous function to it, which is equivalent to...
function some_func (value) {
// some code here
}
The second one should look like this...
obj = {
show:function(value){
// some code here
}
}
// obj.show(value)
and equivalent to...
//pseudo code
class MyClass {
function show (value) {
// some code here
}
}
obj = new MyClass(); // obj.show(value)
Cheers