While searching the web, I've encountered a post that shows why the following example of dynamically generated methods does not work as planned:
// Create a new user object that accepts an object of properties
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for ( var i in properties ) { (function(){
// Create a new getter for the property
this[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
this[ "set" + i ] = function(val) {
properties[i] = val;
};
})(); }
}
The reason for that is the anonymous function, which uses the "this" keyword is context of the "window", instead of "User".
1) Why does the this keyword in the anonymous function refers to "window instead of "User"?
2) Is there an accepted and common way to create "Dynamically Generated Methods"?
Thanks,
Joel
The reason that this refers to the window object, rather than User, is because this depends on the caller. In this case, the foreach contains an anonymous function that is immediately called. The caller will be considered to be the window object.
The reason it's not working is because the code is poorly written. It would be a simple thing to pass both context and the i-variable to be scoped:
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for ( var i in properties ) { (function(x){
// Create a new getter for the property
this[ "get" + x ] = function() {
return properties[x];
};
// Create a new setter for the property
this[ "set" + x ] = function(val) {
properties[x] = val;
};
}).call(this, i); }
}
I did try all your examples, but no one has worked perfectly.
This is the working code:
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var that = this;
for ( var i in properties ) { (function(){
// Create a new getter for the property
that[ "get" + i ] = function(i) {
return function (){
console.debug(i);
return properties[i];
}
}(i);
// Create a new setter for the property
that[ "set" + i ] = function(i) {
return function (val){
properties[i] = val;
}
}(i);
})(); }
}
var user = new User({
name: "Bob",
age: 44
});
console.log(user.getname(), user.getage()) //Bob, 44
user.setname("Antonio");
user.setage(33);
console.log(user.getname(), user.getage()) //Antonio, 33
More explanation to the following link
computerone.altervista.org
You need to set a proper reference of the "this" element. You are inside an anonymous scope.
As the first line of the function "User" you should declare a variable like
var _this = this;
Then, instead of calling this[ "get" + i], you have to call _this[ "get" + i]
Try:
// Create a new user object that accepts an object of properties
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var self = this;
for ( var i in properties ) { (function(){
// Create a new getter for the property
self[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
self[ "set" + i ] = function(val) {
properties[i] = val;
};
})(); }
}
The problem there is the missing new operator. If you instance your User without it, the this inside will be window.
This will not work:
var george = User(properties);
This will work:
var george = new User(properties);
This tutorial it's interesting to follow.
Related
I want to write a getter and setter functions for an object based on a Json files. I copied the below code of John Resig's book(Appress Pro JavaScript Techniques) but it doesn't work and these functions don't add to the object.Can you tell me why and what is the right code?
// Create a new user object that accepts an object of properties
function User(properties) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for (var i in properties) {
(function () {
// Create a new getter for the property
this["get" + i] = function () {
return properties[i];
};
// Create a new setter for the property
this["set" + i] = function (val) {
properties[i] = val;
};
})();
}
}
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
// Just note that the name property does not exist, as it's private
// within the properties object
//alert( user.name == null );
// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert(user.getname());
You've used a function to create a closure, but you've forgotten to pass i into it. You'll also need a different reference to this inside the function, as the context changes in it to window.
function User(properties) {
var i, me = this;
for (i in properties) (function (i) {
// Create a new getter for the property
me["get" + i] = function () { // using `me` because `this !== me`
return properties[i];
};
// Create a new setter for the property
me["set" + i] = function (val) {
properties[i] = val;
};
}(i)); // i passed into function closure
}
In your IIFE, this is actually window. You have to specify the context by using Function.prototype.call:
function User(properties) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for (var i in properties) {
(function(i) {
// Create a new getter for the property
this["get" + i] = function() {
return properties[i];
};
// Create a new setter for the property
this["set" + i] = function(val) {
properties[i] = val;
};
}).call(this, i);
}
}
Or keep a reference to it with another variable:
function User(properties) {
var that = this;
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for (var i in properties) {
(function(i) {
// Create a new getter for the property
that["get" + i] = function() {
return properties[i];
};
// Create a new setter for the property
that["set" + i] = function(val) {
properties[i] = val;
};
})(i);
}
}
If you were in strict mode, your code would throw an error instead of misleadingly succeeding:
TypeError: Cannot set property 'getname' of undefined
What is the best design pattern for achieving the following (which doesn't work)?
var obj = (function() {
// code defining private variables and methods
var _obj = {
property: value,
method1: function() {
// do stuff
},
method2: function() {
// use property
var prop = _obj.property; // obviously doesn't work
// call method1
obj.method1(); // "obj" not finished being defined yet!
}
};
// obviously now I could do...
var prop = _obj.property;
return _obj;
})();
// and I could now do...
obj.method1();
A variation which I think should work is
var obj = (function() {
var property = value,
method1 = function() {
// do stuff
},
method2 = function() {
// use property
var prop = property;
// call method1
method1();
},
_obj = {
property: property,
method1: method1,
method2: method2
};
return _obj;
})();
Similarly, how does it work for objects meant to be created with the new operator? Within the constructor function itself you can write this.method(). But what if you want to keep the constructor small, only defining those things which will likely be customized upon creation, and then defining the rest in the prototype? (This seems to be the common pattern.) Can the properties / methods within the prototype interact in any way?
var MyObj = function(name) {
this.name = name;
};
var obj = new MyObj('Bob');
MyObj.prototype = {
called_often: function() {
// lots more code than just the following
return document.getElementById('someID').value;
},
global_default: 'value', // can be changed, so need to pull value when run
does_stuff: function(value) {
var str = global_default + value, // can't access global_default on its own
input = MyObj.called_often(), // doesn't work; MyObj.prototype.called_often() DOES
name = this.name; // 'this' used in the prototype doesn't work
// even within a created object
return name + input + str;
}
};
I'm sure there's better ways to achieve my result whenever I run into this problem. This code isn't situation specific and just illustrates the general problem. So you won't be able to give me an alternative for those specific situations I run into. But maybe you can help my overall thinking.
Well, from your first example:
var _obj = {
property: value,
method1: function() {
// do stuff
},
method2: function() {
// use property
var prop = this.property;
// call method1
this.method1();
}
};
That's what the this value is for.
Now, what you cannot do is refer to a property of an "under construction" object from elsewhere in the object literal syntax. (It's hard to give an example because it's just not syntactically possible.) In cases where you want to do that, you do need one or more separate assignment statements.
Guess what? You are making simple stuff complex. Pointy's answer is good, but the prototype way is better for several reasons. That's why I am describing (rather, making corrections in) the last method. Check this fiddle.
var MyObj = function(name) {
this.name = name;
};
MyObj.prototype = {
called_often: function() {
// lots more code than just the following
return 'VALUE'; //document.getElementById('someID').value;
},
global_default: 'value', // can be changed, so need to pull value when run
does_stuff: function(value) {
var str = this.global_default + value, // can't access global_default on its own
input = this.called_often(), // doesn't work; MyObj.prototype.called_often() DOES
name = this.name; // 'this' used in the prototype doesn't work
// even within a created object
return name + input + str;
}
};
var obj = new MyObj('Bob');
Inside John Resig's book "Pro Javascript techniques" he describes a way of generating dynamic object methods with the below code:
// Create a new user object that accepts an object of properties
function User(properties) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for (var i in properties) {
(function() {
// Create a new getter for the property
this["get" + i] = function() {
return properties[i];
};
// Create a new setter for the property
this["set" + i] = function(val) {
properties[i] = val;
};
})();
}
}
The problem is when I try instantiating the above object, the dynamic methods are being attached to the window object instead of the object instantiated. It seems like "this" is referring to the window.
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
alert( user.getname() );
Running the above code throws this error "user.getname is not a function".
What is the correct way of generating the dynamic functions for each object instantiated?
Is this code from the book? I have the book, but I haven't read through it.
It's an error in the book. Check the errata: http://www.apress.com/9781590597279
Inside the anonymous function, this is the global window.
You could make it work by adding .call(this, i) after it.
function User(properties) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for (var i in properties) {
(function(i) {
// Create a new getter for the property
this["get" + i] = function() {
return properties[i];
};
// Create a new setter for the property
this["set" + i] = function(val) {
properties[i] = val;
};
}).call(this, i);
}
}
The this in the inner self-executing function is not the same as the this in the outer User function. As you noticed, it refers to the global window.
The problem is fixed if you slightly modify the code by adding a variable that refers to the outer this.
function User(properties) {
var self = this;
for (var i in properties) {
(function() {
self["get" + i] = function() { /* ... */ };
self["set" + i] = function() { /* ... */ };
})();
}
}
That said, I'm not sure why the anonymous self-executing function is even needed here, so you have the simpler option of just leaving it out entirely, like this:
function User(properties) {
for (var i in properties) {
this["get" + i] = function() { /* ... */ };
this["set" + i] = function() { /* ... */ };
}
}
Here is how to do it. You need to save the context into another variable. The other option is not to do this inner function that you are doing in the for loop.
// Create a new user object that accepts an object of properties
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var that = this;
for ( var i in properties ) { (function(){
// Create a new getter for the property
that[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
that[ "set" + i ] = function(val) {
properties[i] = val;
};
})(); }
}
Option 2:
// Create a new user object that accepts an object of properties
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for ( var i in properties ) {
// Create a new getter for the property
this[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
this[ "set" + i ] = function(val) {
properties[i] = val;
};
}
}
You can always force another this for any function call, using the apply method.
(function() {
// Create a new getter for the property
this["get" + i] = function() {
return properties[i];
};
// Create a new setter for the property
this["set" + i] = function(val) {
properties[i] = val;
};
}).apply(this);
You can also make a new function that has uses a certain this with the .bind method.
function getaloadofthis() {return this}
If you do getaloadofthis() it just returns window but if you do getaloadofthis.bind(3)() it returns 3.
So you could also have
const getaloadofthis3 = getaloadofthis.bind(3)
getaloadofthis3() // 3
This also works with anonymous functions
(function() {return this}).bind(3)() // 3
I like that in javascript, I can create a function, and then add further methods and attributes to that function
myInstance = function() {return 5}
myInstance.attr = 10
I would like to create a class to generate these objects. I assume I have to inherit from the Function base class.
In other words, I would like to:
var myInstance = new myFunctionClass()
var x = myInstance()
// x == 5
But I don't know how to create the myFunctionClass. I have tried the following, but it does not work:
var myFunctionClass = function() {Function.call(this, "return 5")}
myFunctionClass.prototype = new Function()
myInstance = new myFunctionClass()
myInstance()
// I would hope this would return 5, but instead I get
// TypeError: Property 'myInstance' of object #<Object> is not a function
I also tried the more complicated (and more proper?) inheritance method found here: How to "properly" create a custom object in JavaScript?, with no more luck. I have also tried using the util.inherits(myFunctionClass, Function) found in node.js. Still no luck
I have exhausted Google, and therefore feel that I must be missing something fundamental or obvious. Help would be greatly appreciated.
Your trying to inherit from Function. This is a right pain to do. I suggest you do the following instead
Live Example
var Proto = Object.create(Function.prototype);
Object.extend(Proto, {
constructor: function (d) {
console.log("construct, argument : ", d);
this.d = d;
// this is your constructor logic
},
call: function () {
console.log("call", this.d);
// this get's called when you invoke the "function" that is the instance
return "from call";
},
method: function () {
console.log("method");
// some method
return "return from method";
},
// some attr
attr: 42
});
You want to create a prototype object that forms the basis of your "class". It has your generic methods/attributes. It also has a constructor that gets invoked on object construction and a call method that gets invoked when you call the function
var functionFactory = function (proto) {
return function () {
var f = function () {
return f.call.apply(f, arguments);
};
Object.keys(proto).forEach(function (key) {
f[key] = proto[key];
});
f.constructor.apply(f, arguments);
return f;
}
}
A function factory takes a prototype object and returns a factory for it. The returned function when called will give you a new function object that "inherits" from your prototype object.
var protoFactory = functionFactory(proto);
var instance = protoFactory();
Here you create your factory and then create your instance.
However this isn't proper prototypical OO. we are just shallow copying properties of a prototype into a new object. So changes to the prototype will not reflect back to the original object.
If you want real prototypical OO then you need to use a hack.
var f = function () {
// your logic here
};
f.__proto__ = Proto;
Notice how we use the non-standard deprecated .__proto__ and we are mutating the value of [[Prototype]] at run-time which is considered evil.
JS does not allow a constructor to return a function, even though functions are objects. So you cant have an instantiation of a prototype that is itself executable. (Am I right in this? please correct if I'm not, it's an interesting question).
Though you could do a factory function:
var makeCoolFunc = function() {
var f = function() { return 5 };
f.a = 123;
f.b = 'hell yes!'
return f;
};
var func = makeCoolFunc();
var x = func();
You can extend Function and pass the wanted function body as String to the super constructor. The context of the function can be accessed with arguments.callee.
Example for an observable Attribute class:
export default class Attribute extends Function {
constructor(defaultValue){
super("value", "return arguments.callee.apply(arguments);");
this.value = defaultValue;
this.defaultValue = defaultValue;
this.changeListeners = [];
}
apply([value]){
if(value!==undefined){
if(value!==this.value){
var oldValue = this.value;
this.value=value;
this.changeListeners.every((changeListener)=>changeListener(oldValue, value));
}
}
return this.value;
}
clear(){
this.value=undefined;
}
reset(){
this.value=this.defaultValue;
}
addChangeListener(listener){
this.changeListeners.push(listener);
}
removeChangeListener(listener){
this.changeListeners.remove(listener);
}
clearChangeListeners(){
this.changeListeners = [];
}
}
Example usage:
import Attribute from './attribute.js';
var name= new Attribute();
name('foo'); //set value of name to 'foo'
name.addChangeListener((oldValue, newValue)=>{
alert('value changed from ' +oldValue+ ' to ' +newValue);
});
alert(name()); //show value of name: 'foo'
name('baa'); //set value of name to new value 'baa' and trigger change listener
I need to create simple reusable javascript object publishing several methods and parameterized constructor. After reading through several "OOP in JavaScript" guides I'm sitting here with an empty head. How on the Earth can I do this?
Here my last non-working code:
SomeClass = function(id) {
this._id = id;
}
(function() {
function intFun() {
return this._id;
}
SomeClass.prototype.extFun = function() {
return incFun();
}
})();
This is my usual approach:
MyClass = function(x, y, z) {
// This is the constructor. When you use it with "new MyClass(),"
// then "this" refers to the new object being constructed. So you can
// assign member variables to it.
this.x = x;
...
};
MyClass.prototype = {
doSomething: function() {
// Here we can use the member variable that
// we created in the constructor.
return this.x;
},
somethingElse: function(a) {
}
};
var myObj = new MyClass(1,2,3);
alert(myObj.doSomething()); // this will return the object's "x" member
alert(myObj.x); // this will do the same, by accessing the member directly
Normally the "this" keyword, when used in one of the object's methods, will refer to the object itself. When you use it in the constructor, it will refer to the new object that's being created. So in the above example, both alert statements will display "1".
An exception to this rule is when you pass one of your member functions somewhere else, and then call it. For example,
myDiv.onclick = myObj.doSomething;
In this case, JavaScript ignores the fact that "doSomething" belongs to "myObj". As a result, the "this" inside doSomething will point to another object, so the method won't work as expected. To get around this, you need to specify the object to which "this" should refer. You can do so with JavaScript's "call" function:
myDiv.onclick = function() {
myObj.doSomething.call(myObj);
}
It's weird, but you'll get used to it eventually. The bottom line is that, when passing around methods, you also need to pass around the object that they should be called on.
I usually don't worry too much about hiding the internals, although I do prefix them with underscores to mark them as not intended to be used outside the "class". Normally what I will do is:
var MyClass = function() {};
MyClass.prototype = {
_someVar : null,
_otherVar : null,
initialize: function( optionHash ) {
_someVar = optionsHash["varValue"];
_otherVar = optionsHash["otherValue"];
},
method: function( arg ) {
return _someVar + arg;
},
};
And use it as so...
var myClass = new MyClass( { varValue: -1, otherValue: 10 } );
var foo = myClass.method(6);
All vars are private:
SomeClass = function (id) {
var THIS = this; // unambiguous reference
THIS._id = id;
var intFun = function () { // private
return THIS._id;
}
this.extFun = function () { // public
return intFun();
}
}
Use THIS within private methods since this won't equal what you might expect.
From http://learn.jquery.com/code-organization/concepts/#the-module-pattern:
// The module pattern
var feature = (function() {
// private variables and functions
var privateThing = "secret";
var publicThing = "not secret";
var changePrivateThing = function() {
privateThing = "super secret";
};
var sayPrivateThing = function() {
console.log( privateThing );
changePrivateThing();
};
// public API
return {
publicThing: publicThing,
sayPrivateThing: sayPrivateThing
};
})();
feature.publicThing; // "not secret"
// logs "secret" and changes the value of privateThing
feature.sayPrivateThing();
So using returning an object that aliases its "methods" could be another way to do it.
I've read from http://www.amazon.com/Programming-Oracle-Press-Poornachandra-Sarang-ebook/dp/B0079GI6CW that it is always good practice to use getters and setters rather that accessing the variable directly from outside the object, so that would eliminate the need of returning variables by reference.
BTW you could just use this.variable to reference/declare a public variable and var variable to declare a private variable.
I know this is a late answer, but I hope it helps anyone who reads it in the future.