Why do I get this error message Property 'shortDescr' of object #<Article> is not a function
function Article() {
this.id = null;
this.title = null;
this.url = null;
this.descr = null;
this.media = null;
};
Article.prototype.shortDescr = function () {
if ( this.descr.length > 100) {
return this.descr.substring(0,80) + "..";
} else {
return this.descr;
}
};
var ArticleFactory = {
numOfArgs : 5,
inputCheck : function(args) {
if (args.length != this.numOfArgs) {
throw new Error("Invalid number of arguments for class `Article`");
};
return true;
},
//Fill the properties with values from arguments
create : function() {
this.inputCheck(arguments);
var counter = 0;
var article = new Article();
for(propertie in article) {
article[propertie] = arguments[counter++];
}
return article;
}
};
var descr = "#hughes it actually can do both. i have an object i created with: var obj = and another object that is being passed into a callback from a server, the one passed through the callback prints with the little arrow so you can open it up, the statically created one just prints [object Object] with no arrow. ";
var article = ArticleFactory.create(1,"title","url",descr,{});
console.log(article.shortDescr());
Addendum
console.log(JSON.stringify(article, null, 4));
{
"id": 1,
"title": "title",
"url": "url",
"descr": "#hughes it actually can do both. i have an object i created with: var obj = and another object that is being passed into a callback from a server, the one passed through the callback prints with the little arrow so you can open it up, the statically created one just prints [object Object] with no arrow. ",
"media": {} }
Proof
#dystroy was right.
You're shadowing the function here :
for(propertie in article) {
article[propertie] = arguments[counter++];
}
More precisely, you iterate over the property names (including the ones of the prototype chain) and you set new values to your object. When you set a value with the name of a property of the prototype, you don't change the prototype but the value that will be found with article.shortDescr will be the one of the object, not the one of the prototype.
What you do is kind of blind (you don't even have any guarantee on the order of properties) so I would recommend to change your design on this point (how ? I can't say as I really don't get the purpose).
But if you want to keep it, you may skip the prototype properties by testing using hasOwnProperty.
Related
I am trying to create a property within a constructor function which is immutable except through a prototype function. I am trying to go off MDN documentation of this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties. But there does not seem to be a way to make a property completely immutable. Consider a simple example:
function Test(){
Object.defineProperties(this,{
elems : { value : [] }
})
}
Test.prototype.addElem = function(newElem){
if (this.elems.indexOf(newElem) == -1){
this.elems.push(newElem);
}
};
which works fine in most cases (not assignable):
>a = new Test()
Object { , 1 more… }
>a.elems
Array [ ]
>a.elems = 10
10
>a.elems
Array [ ]
Unfortunately, it is still mutable. Consider:
>a.elems.push(10)
1
>a.elems
Array [ 10 ]
I am sure they are other functions (array or object methods?) that will change the value of a non-writeable & non-settable property. Push was just the one I ran into. Is there a way to accomplish this? I know that one possible solution is :
function Test(){
var elems = [];
this.addElem = function(newElem){
if (elems.indexOf(newElem) == -1){
elems.push(newElem);
}
}
}
But I have read this is memory-inefficient especially when there are many instances of the "class". Also, what I am working on may have many methods like this, so I am even more worried about memory considerations.
Any ideas? I am not super knowledgeable about all the intricacies of JS prototyping.
In JavaScript, objects are extensible by default, but if you're able to take advantage of ES5, you should be able to use the Object.seal() or Object.freeze() methods to get immutable properties.
The MDN docs for Object.freeze() have an example that shows how to recursively freeze ("deepFreeze") all of the properties of an object, effectively making it completely immutable.
Here's a proof of concept that combines the code in the question with the code from the docs:
function Test() {
Object.defineProperties(this, {
elems : { value : [] }
})
}
Test.prototype.addElem = function(newElem) {
if (this.elems.indexOf(newElem) == -1) {
this.elems.push(newElem);
}
};
function deepFreeze(obj) {
// Retrieve the property names defined on obj
var propNames = Object.getOwnPropertyNames(obj);
// Freeze properties before freezing self
propNames.forEach(function(name) {
var prop = obj[name];
// Freeze prop if it is an object
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
// Freeze self (no-op if already frozen)
return Object.freeze(obj);
}
a = new Test();
a.elems.push(1);
console.log(a.elems); // [1]
deepFreeze(a);
a.elems.push(2);
console.log(a.elems); // Error
In FireBug, the a.elems.push() after the object is "deep frozen" returns a TypeError exception, indicating the property is not writable;
TypeError: can't define array index property past the end of an array
with non-writable length
The Safari inspector also returns a TypeError exception:
TypeError: Attempted to assign to readonly property.
You can largely accomplish this with the help of a closure. This is how you achieve privacy in JavaScript.
In a nutshell you create a variable inside of a function and have that function return an object that contains setters/getters.
In my example the foo function contains a _foo variable that can only be set by the methods in the object returned from function foo. You are effectively creating an API to the var held withing the function foo's scope.
var foo = function(config){
if (!config) {
config = {};
}
//enclosed variable
var _foo = {
bar: []
};
if (config.bar) {//All item to be initialized with data
_foo.bar = config.bar;
}
var fooAPI = {
addBarItem: function(val){
_foo.bar.push(val);
return _foo.bar.length - 1;//return idenx of item added
},
removeBarItem: function(index) {
return _foo.bar.slice(index, 1);//return the removed item
},
getBarItem: function(index) {
return _foo.bar[index];//return the removed item
},
emptyBarItems: function() {
return _foo.bar.slice(0, _foo.bar.length);//return all removed
},
getBarItems: function(){
//clone bar do not return reference to it in order to keep private
var newBar = [];
_foo.bar.forEach(function(item){
newBar.push(item);
});
return newBar;
}
};
return fooAPI;
};
var myFoo = new foo({bar: ['alpha', 'beta', 'gamma']});
console.log(myFoo.getBarItems());
I'm working with a data object lit, then trying to create a new object that changes properties in the data property just for that instance, here's some test code from jsbin
data = {
innerData : 1
}
-------------
'This works :-)'
construct = function(d){
this.data = Object.create(d);
};
construct.prototype.c = function(n){
this.data.innerData = n;
};
construct.prototype.d = function(){
console.log(this.data.innerData)
};
--------------
'This does not :-{'
construct = {
data : Object.create(data),
changeData : function(n){
this.data.innerData = n;
},
showData:function(){
console.log(this.data.innerData)
}
}
--------------
newInst = Object.create(construct);
newInst.changeData(5);
newInst.showData();
newInst2 = Object.create(construct);
newInst2showData();
when I run it using the constructor/prototype functions it works and the console outputs 5,2
when I run it using the object literal the console outputs 5,5 I guess when I create the first instance it changes the actual data object and not the data property of the instance of the construct object.
If someone could explain in depth why this happens that would be much help as I've not been working with OOJS for that long
UPDATE:
so I had a little go at merging what I found useful from the answers and I've come up with this....
data = {
innerData : 1
}
function construct(d){
return {
data : Object.create(d),
changeData : function(n){
this.data.innerData = n;
},
showData : function(){
console.log(this.data.innerData)
}
}
}
build = function(){
return(new construct(data));
}
newInst = build();
newInst.changeData(5);
newInst.showData();
newInst2 = build();
newInst2.showData();
Given what we know about the inheritance, what does the following actually do
this.data.innerData = n;
where this is the result of Object.create(construct)?
this does not have own property data, so look up in the inherited properties.
Okay we found a data === Object.getPrototypeOf(this).data, call it d
Next set innerData of d to n
So what the actual result has ended up is the innerData property has been set on the reference from the prototype, and not a new Object.
Why has this happened? Because if you have o = {} and try to do o.foo.bar, you get a TypeError: Cannot read property 'bar' of undefined, and therefore for it to not throw an error, it has to be accessing a defined Object.
var proto = {foo: {bar: 'fizz'}},
obj = Object.create(proto);
obj.foo.bar = 'buzz';
proto.foo.bar; // "buzz" (i.e. NOT "fizz" anymore)
// and
obj.foo === Object.getPrototypeOf(obj).foo; // true
In your second example, there is only ever one data object. That object lives inside construct and is available to all the subsequent objects on the prototype chain.
In your first example, you make a new data object every time a new instance is created. So each object gets its own copy of data.
Try this, for the first example:
console.log(newInst.data === newInst2.data); // should be false
and for the second example:
console.log(newInst.data === newInst2.data); // should be true
The second piece of code works just fine:
construct.changeData(2);
construct.showData(); // 2
The only difference is that in the above example construct is not a constructor, so there will be only a single instance of construct.data as opposed to the first approach; calling Object.create() on it will create a new object but will keep the same .data reference as the first.
This looks like an attempt to create a factory function instead of a constructor function. Since Object.create is now widespread, and shimmable for these purposes where not available, this has become perhaps the best default, although there are plenty of constructor functions still around.
Some of the other answers explain what went wrong with your attempt. Here's how you might do it so that it works as expected:
var factory = (function() {
var clone = function(obj) {return JSON.parse(JSON.stringify(obj));};
var data = {
innerData : 1
};
var proto = {
changeData : function(n){
this.data.innerData = n;
},
showData:function(){
console.log(this.data.innerData)
}
};
return function() {
var obj = Object.create(proto);
obj.data = clone(data);
return obj;
}
}());
But it looks as though your innerData might have been an experiment to try to get these things working. It it's not necessary, this would be cleaner:
var factory = (function() {
var proto = {
data: 1,
changeData : function(n){
this.data = n;
},
showData:function(){
console.log(this.data)
}
};
return function() {
return Object.create(proto);
}
}());
I am working on my first JS project that involves inheritance and the prototype chain, and I am confused about why the creation of one object with specific data is affecting the data already in place on my second object.
The goal is to have a set of basic defaults in the full_params object literal in the "parent" object, and have some more specific defaults in the default_params object literal in the "child" object.
The child object specificRequest takes an array argument for its constructor function, adds those to its default_params, and then call the setOptions function of its prototype to add those to the full_params.
The problem is that when I create one specificRequest object and initialize it, it works fine, but then when I create a second specificRequest object, the full_params is already the same as
that of the first.
This is probably something very simple from a misunderstanding of how prototype works...
/////// PARENT OBJECT
function baseRequest(custom_params) {
var key;
this.full_params = {
"SignatureVersion": "2",
"Timestamp": Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")
};
this.custom_params = custom_params;
}
baseRequest.prototype.setOptions = function(arg_options) {
var key;
if (typeof arg_options === "object") this.custom_params = arg_options;
// If an object of request options is passed, use that. Otherwise use whatever is already in the custom_params object.
for (key in this.custom_params) {
this.full_params[key] = this.custom_params[key];
}
}
///////// CHILD OBJECT
function specificRequest(mySKUList) {
var i;
this.mySKUList = mySKUList;
this.default_params = {
"Action": "myAction",
"Version": "2011-10-01"
};
for (i = 0; i < this.mySKUList.length; i++) {
var temp_sku = this.mySKUList[i];
var temp_sku_name = "SellerSKUList.SellerSKU." + (i + 1);
this.default_params[temp_sku_name] = temp_sku;
}
this.setOptions(this.default_params);
}
specificRequest.prototype = new baseRequest
///// Function to run
function testfoo() {
var skulist1 = ["AR6100", "AR6102", "WB1234"]
var skulist2 = ["XY9999"]
var req1 = new specificRequest(skulist1);
var req2 = new specificRequest(skulist2);
// Req1 has AR6100, AR6102, and WB1234 as parameters, as expected
// Req2 should only have XY9999, but instead has XY9999, AR6102, and WB1234
}
Well you have tied a concrete instance of the parent class to be the prototype of the child class with this line:
specificRequest.prototype = new baseRequest
Instead, don't instantiate the parent class at all:
specificRequest.prototype = Object.create( baseRequest.prototype );
Also, call super() equivalent when constructing a child instance:
function specificRequest(mySKUList) {
baseRequest.call( this );
...
}
And please start constructor names with UpperCase.
I have a bunch of functions (methods of a class actually) and I'm writing a method that will record the interactions with other methods in an array.
so for example :
foo = Base.extend ({
a : function (a1,a2){
....
},
b:function(b1,b2){
...
},
history : function(){ ... }
})
to simplify the history method, I'd like to read the name of the optional arguments and add them to the array, so for example if the method a is called, I want to record a1,a2 ...
so basically, is there any way to read the name of the optional arguments list of an array in javascript ?
here is the code :
var element = Base.extend({
constructor : function() {
if(arguments.length==1){
...
}else{
...
}
},
setLocation : function (top, left){
oldArgs = [this.top,this.left];
this.top = top;
this.left = left;
return(oldArgs);
},
setAspects : function (width, height){
oldArgs = [this.width,this.height]
this.width = width;
this.height = height;
return(oldArgs);
},
draw : function (page){
...
},
delet : function () {
...
},
$ : function(method,args){
var oldArgs = this[method].apply(this,args);
this.history(method,oldArgs);
Nx.pages[this.page].modified = true;
},
history : function (method,args){
Nx.history[Nx.history.length]=[this.id,method,args]
}
})
so in this class, if I want to call any method, I'll pas it through the $ method, and it will call the history method, so far what I've done is for example in the setLocation method it will return the old arguments and I will story them in my array Nx.history, but it's easier to factorise all of these "return" calls in the methods, and add a line to the $ method , that reads the name of the expected arguments of the method, and send it to the history method, so something like this :
$ : function(method,args){
this[method].apply(this,args);
**var oldArgs = this[method].arguments // get the list of argument names here
$.each(oldArgs, function(value) { Args[Args.length] = this.value //get the stored value in the class
})
this.history(method,Args); // and pass it to the history**
Nx.pages[this.page].modified = true;
}
I'm not 100% sure what you're asking for - a way to extract the formal parameter names of a function?
I have no idea if this would work or not, but could you parse the string representation of the function to extract the parameter names?
It would probably be a very lame solution, but you might be able to do something like:
function getArgNames(fn) {
var args = fn.toString().match(/function\b[^(]*\(([^)]*)\)/)[1];
return args.split(/\s*,\s*/);
}
2.0
The idea with this newer version is to define the properties that you want to record for the object beforehand. It's a level of duplication, but it's only a one time thing. Then, in the constructor, create property setters for each of these properties. The setter does some side work along with setting the property. It pushes the arguments name and value onto a stack, and assigns the properties. The $ method is supposed to call dispatch the call to the appropriate method. Once the call is complete, the stack will be populated with the parameters that were set in that function. Pop off each parameter from that stack, until the stack is empty. Then call history with the method name, and the parameters that we just popped off the stack. Please let me know if this doesn't make any sense, I might have to word it better.
See an example here.
Here's a code example written in MooTools which is slightly similar to your Base class.
var Device = new Class({
_properties: ['top', 'left', 'width', 'height'],
_parameterStack: [],
initialize: function() {
this._createPropertyAccessors();
},
_createPropertyAccessors: function() {
this._properties.each(function(property) {
Object.defineProperty(this, property, {
enumerable: true,
configurable: true,
set: function(value) {
var o = {};
o[property] = value;
// push the parameter onto the stack
this._parameterStack.push(o);
}.bind(this)
});
}.bind(this));
},
// method stays unchanged
setLocation: function(top, left) {
this.top = top;
this.left = left;
},
setAspects: function(width, height) {
this.width = width;
this.height = height;
},
// dispatches call to method
// pops off the entire stack
// passed method name, and emptied stack arguments to history
$: function(method, args) {
this[method].apply(this, args);
var argsStack = [];
while(this._parameterStack.length) {
argsStack.push(this._parameterStack.pop());
}
this.history(method, argsStack);
},
history: function(method, args) {
console.log("%s(%o) called", method, args);
}
});
1.0
The arguments passed to a JavaScript function are accessible through an array-like object named arguments which is available for all functions.
When calling history, pass the arguments object to it.
a: function(a1, a2) {
this.history.apply(this, arguments);
}
history will then be invoked as if it was called with two arguments with this being the base object - foo unless you call it with a different context.
I am not sure how Base plays into this. You would have to elaborate more as to the role of Base here.
Here' s a simple example:
var foo = {
a: function(a1, a2) {
this.history.apply(this, arguments);
},
history: function() {
console.log("history received " + arguments.length + " arguments.");
}
};
foo.a("hello", "world"); // history received 2 arguments
Also note that although a has two named parameters here, we can still pass it any number of arguments, and all of them will be passed to the history method in turn. We could call a as:
foo.a(1, 2, 3); // history received 3 arguments
Something like this should work. When you want to access the arguments that have been used you can loop through the foo.history array which contains the arguments list.
var foo = {
storeArgs: function (fn) {
return function() {
this.history += arguments
return fn.apply(null, arguments);
}
},
a: storeArgs(function(a1, a2) {
alert(a1+a2);
}),
history: []
};
I read your updated post. What do you mean by "names?" Variable names? For example, if someone called a(1337), would you want ["a1"] to be added to the array? And if a(1337, 132) was called ["a1", "a2"] would be added? I don't think there's any sane way to do that.
This is the best I can do. You will have to include a list of parameter names when defining your functions using the storeArgs function.
var foo = {
storeArgs: function (params, fn) {
return function() {
var arr = [];
for (var i = 0; i < arguments.length; i++) {
arr.push(params[i]);
}
this.history += arr;
return fn.apply(null, arguments);
}
}
a: storeArgs(["a1", "a2"], function(a1, a2) {
alert(a1+a2);
}),
history: []
};
Let me know if it works.
This works in IE, but I'm not sure about other browsers...
Function.prototype.params = function() {
var params = this.toString().split(/(\r\n)|(\n)/g)[0];
params = params.trim().replace(/^function.*?\(/, "");
params = params.match(/(.*?)\)/)[1].trim();
if (!params) {
return([]);
}
return(params.split(/, /g));
};
function Test(a, b) {
alert(Test.params());
}
Test();
I'm reading "Pro JavaScript Techniques" by John Resig, and I'm confused with an example. This is the 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() == "Bob" );
// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );
Now running that in the Firebug console (on Firefox 3) throws that user.getname() is not a function. I tried doing this:
var other = User
other()
window.getname() // --> This works!
And it worked!
Why?
Doing:
var me = this;
seems to work a bit better, but when executing "getname()" it returns '44' (the second property)...
Also I find it strange that it worked on the window object without modification...
And a third question, what's the difference between PEZ solution and the original? (He doesn't use an anonymous function.)
I think it's best not to use the new keyword at all when working in JavaScript.
This is because if you then instantiate the object without using the new keyword (ex: var user = User()) by mistake, *very bad things will happen...*reason being that in the function (if instantiated without the new keyword), the this will refer to the global object, ie the window...
So therefore, I suggest a better way on how to use class-like objects.
Consider the following example :
var user = function (props) {
var pObject = {};
for (p in props) {
(function (pc) {
pObject['set' + pc] = function (v) {
props[pc] = v;
return pObject;
}
pObject['get' + pc] = function () {
return props[pc];
}
})(p);
}
return pObject;
}
In the above example, I am creating a new object inside of the function, and then attaching getters and setters to this newly created object.
Finally, I am returning this newly created object. Note that the the this keyword is not used anywhere
Then, to 'instantiate' a user, I would do the following:
var john = user({name : 'Andreas', age : 21});
john.getname(); //returns 'Andreas'
john.setage(19).getage(); //returns 19
The best way to avoid falling into pitfalls is by not creating them in the first place...In the above example, I am avoiding the new keyword pitfall (as i said, not using the new keyword when it's supposed to be used will cause bad things to happen) by not using new at all.
Adapting Jason's answer, it works:
We need to make a closure for the values. Here's one way:
function bindAccessors(o, property, value) {
var _value = value;
o["get" + property] = function() {
return _value;
};
o["set" + property] = function(v) {
_value = v;
};
}
Then the User constructor looks like this:
function User( properties ) {
for (var i in properties ) {
bindAccessors(this, i, properties[i]);
}
}
You probably want something like this, which is more readable (closures are easy to learn once you get some practice):
function User( properties ) {
// Helper function to create closures based on passed-in arguments:
var bindGetterSetter = function(obj, p, properties)
{
obj["get" + p] = function() { return properties[p]; }
obj["set" + p] = function(val) { properties[p]=val; return this; }
};
for (var p in properties)
bindGetterSetter(this, p, properties);
}
I also added "return this;", so you can do:
u = new User({a: 1, b:77, c:48});
u.seta(3).setb(20).setc(400)
I started this post with the sole purpose of learning why that things happened, and I finally did. So in case there's someone else interested in the "whys", here they are:
Why does 'this' changes inside the anonymous function?
A new function, even if it is an anonymous, declared inside an object or another function, always changes the scope, in this case returning to the global scope (window).
Solution: all stated in the post, I think the clearer is executing the anonymous function with .call(this).
Why does getname() always return the age?
While the anonymous function gets executed right away, the getters/setters get executed for the first time when they are called. In that moment, the value of i will always be the last, because it has already iterated for all the properties... and it will always return properties[i] which is the last value, in this case the age.
Solution: save the i value in a variable like this
for ( i in properties ) { (function(){
var j = i
// From now on, use properties[j]
As written in the OP, this in the loop is not referring to the User object as it should be. If you capture that variable outside the loop, you can make it work:
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var me = this;
for ( i in properties ) { (function(){
// Create a new getter for the property
me[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
me[ "set" + i ] = function(val) {
properties[i] = val;
};
// etc
I just modified the code a bit like this.. This one should work.. This is same as setting me=this; But a closure is required to set the value of each property properly, else the last value will be assigned to all properties.
// Create a new user object that accepts an object of properties
var User = function( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var THIS = this;
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;
};
})(i); }
}
// 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() == "Bob" );
// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );
Maybe the variable i is "closured" with the last value in the iteration ("age")? Then all getters and setters will access properties["age"].
I found something that seems to be the answer; it’s all about context. Using the anonymous function inside the for loop, changes the context, making 'this' refer to the window object. Strange isn't it?
So:
function User(properties) {
for (var i in properties) {
// Here this == User Object
(function(){
// Inside this anonymous function, this == window object
this["get" + i] = function() {
return properties[i];
};
this["set" + i] = function(val) {
properties[i] = val;
};
})();
}
}
I don't know why that function changes the context of execution, and I'm not sure it should do that. Anyway, you can test it running the code there and trying window.getname(). It magically works! :S
The solution, as stated before, is changing the context. It can be done like J Cooper said, passing the variable 'me' and making the function a closure or you can do this:
(function(){
// Inside this anonymous function this == User
// because we called it with 'call'
this[ "get" + i ] = function() {
return properties[i];
};
this["set" + i] = function(val) {
properties[i] = val;
};
}).call(this);
Anyway, I'm still getting 44 when running 'getname'... What could it be?