javascript initializing object attributes? - javascript

I'm trying to write a wrapper on an array and I've come up with code below:
myList = function () { };
myList.prototype.innerArray = [];
myList.prototype.add = function(pt) { this.innerArray.push (pt); return this; };
For every object myList i create, I hope to get an empty attribute innerArray. But I'm afraid i haven't really understood the concept of prototypes yet, because:
m = new myList().add(4).add(5);
m.innerArray.length;
returns 2, so far so good, but now I do:
j = new myList();
j.innerArray.length;
which returns also 2, and I would have expected 0 (a fresh new innerArray); I'm afraid i'm missing something fundamental.

You don't want to use prototype with innerArray. In your constructor simply do this.innerArray = [] and now you have an instance variable. By using prototype you are creating a class attribute (e.g. shared amongst class instances), not an instance variable (e.g. unique to an instance of the class).

All instances of myList share the exact same prototype object. It is not copied or cloned to each instance. For functions this is fine because when you call foo.add() then this is the instance, allowing you to fetch per instance values. foo.add and bar.add are the same function object but this is different in each call. But with values directly on the prototype when you change them they change for all instances, since they all point to the same objects.
Instead you want to make a new array in the constructor:
myList = function () {
this.innerArray = [];
};
myList.prototype.add = function(pt) {
this.innerArray.push (pt);
return this;
};

You should do
myList = function () { this.innerArray = []; };
as this:
myList.prototype.innerArray = [];
creates innerArray variable common to all instances.

Related

Passing "this" pointer from object constructor to setter/getter in JS

Fairly new to using a prototype design pattern so I had a quick question about the most efficient way of creating a setter/getter for multiple constructor variables.
Say I had a "trip" object :
function trip(){
this.numberOfDays = 0
this.price = 0
this.activites = []
this.bPacks = []
this.pPacks = []
}
Now the setters for the activities, bPacks, and pPacks all do primarily the same thing. They will add or remove to the array that they are associated with.
so instead of writing 6 separate setters for these properties such as :
trip.prototype.addActivity = function(itemToAdd){
var index = this.activites.push(itemToAdd)
}
trip.prototype.addBPack = function(itemToAdd){
var index = this.bPack.push(itemToAdd)
}
etc...
Is it possible to do something like this :
trip.prototype.addPacksOrActivity = function(item,typeOfItem){
this.typeOfItem.push(item);
}
where we target the specific property of the trip object we want to push to?
Any suggestions for more efficient ways of building setters/getters are welcome but I am trying to avoid transpiling from ES6 so ES5 type answers are preffered.
Yes, you can use a simple loop to do that:
["activities", "bPacks", "pPacks"].forEach(function(typeOfItem) {
var methodName = "add"+typeOfItem[0].toUpperCase()+typeOfItem.slice(1);
Trip.prototype[methodName] = function(item) {
this[typeOfItem].push(item);
};
});
Notice the use of bracket notation and the closure over typeOfItem.

Javascript "new" keyword making references to "class" properties

I have a javascript "class", that I instanciate several times.
My issue is that modifying properties from the instances does actually modify those properties of the "class", so further instances are not initialized in the way I'd like.
Here is a simplified snippet of what happens, the original code is using BackboneJS but this is enough to illustrate my case:
var foo = function() {
this.defaults.id = 1;
};
// This is my property
foo.prototype.defaults = {
id: 0,
};
console.log( foo.prototype.defaults ); // => {id: 0}
var bar = new foo();
console.log( foo.prototype.defaults ); // => {id: 1}
I thought that the "new" keyword would make a brand new object but it seems that it just makes a reference to the prototype properties.
The best option I came with is to clone all properties from inside the constructor...
Do anyone have a clean way to achieve a clean "Class Property" behavior ?
Many thanks in advance,
Romain.
Edit: It does not work from the SO snippet... just copy/paste it to your console if you want.
The prototype is shared among all objects of this type. A new copy is not made. The usual way to initialize properties that are to be unique to this instance is to initialize them directly in the constructor:
this.property = "foo";
This will override any property of the same name in the prototype and will be unique per instance.
In general, the prototype is not used for data properties because they are shared among all instances. If you want to initialize an object with a default set of data properties, the "best practice" for that is to assign a default set of properties into the object instance and then set any overrides. You can't just assign a default object because objects are assigned by pointer, not by copy so all objects would still be pointing at the same object (the same issue you have when using the prototype).
You could initialize your object with a set of defaults like this:
var foo = function() {
this.defaults = {};
// copy default properties
for (var prop in foo.defaults) {
this.defaults[prop] = foo.defaults[prop];
}
this.defaults.id = 1;
};
// defaults (not on the prototype)
foo.defaults = {
id: 0,
something: "whatever",
line: 22
};
Though, in practice, it is usually easier to just code in the defaults like this:
var foo = function() {
this.options = {
id: 1,
something: "whatever",
line: 22
};
};

When updating an array in a JavaScript object the prototype is updated. Why?

Kind of strange thing is happening with my code. What I observe is something like this:
var prototype = {
property : "",
array : []
}
var objectArray = [];
function myFunction {
objectArray[0] = Object.create(prototype);
objectArray[1] = Object.create(prototype);
objectArray[0].property = "Hello 1";
objectArray[0].array.push("Hello 1");
objectArray[1].property = "Hello 2";
objectArray[1].array.push("Hello 2");
// At this point when running the program I get:
// objectArray[0].property == "Hello 1"
// objectArray[1].property == "Hello 2";
// objectArray[1].array == objectArray[1].array == prototype.array
// == ["Hello 1", "Hello 2"]
}
What I want, and expected, was two separate arrays for the two objects. What am I missing here?
In JavaScript, objects are copied by reference, so both objectArray objects are simply references to the same object ( prototype ). You need to clone the object or create instances using the new keyword and a constructor to create separate objects.
Example on how to do it using the new keyword:
var prototype = function() {
this.property = "";
this.array = [];
};
objectArray[0] = new prototype();
objectArray[1] = new prototype();
You can also do:
var prototypeFactory = function() {
return {
property: "",
array: []
};
};
objectArray[0] = prototypeFactory();
objectArray[1] = prototypeFactory();
The prototype object exists the same as the [[Prototype]] for each object. They don't get a fresh copy when you use Object.create().
You'd need to use assignment, which never walks the prototype chain.
I wonder if you aren't asking yourself "why does it work for property, but not for array?". The fact is, it doesn't work for property either, JavaScript is fooling you.
When objects share the same prototype, one must consider that:
All properties of the prototype are shared by the objects that inherit from that prototype.
You cannot assign to properties of the prototype, only read them (unless it's an accessor property, but let's keep that aside).
So what's actually happening here:
objectArray[0].property = "Hello 1";
objectArray[1].property = "Hello 2";
is that a new own property called "property" is being created on each object, while prototype.property remains untouched. The array property is behaving differently because you're not assigning to it, you're accessing prototype.array and calling the push method on it.
Don't forget that Object.create() isn't yet standardized, and won't work in IE8 or FF3.6. A simple work-around to clone an object is to use the JSON object:
function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}

Ways to extend Array object in javascript

i try to extend Array object in javascript with some user friendly methods like Array.Add() instead Array.push() etc...
i implement 3 ways to do this.
unfortunetly the 3rd way is not working and i want to ask why? and how to do it work.
//------------- 1st way
Array.prototype.Add=function(element){
this.push(element);
};
var list1 = new Array();
list1.Add("Hello world");
alert(list1[0]);
//------------- 2nd way
function Array2 () {
//some other properties and methods
};
Array2.prototype = new Array;
Array2.prototype.Add = function(element){
this.push(element);
};
var list2 = new Array2;
list2.Add(123);
alert(list2[0]);
//------------- 3rd way
function Array3 () {
this.prototype = new Array;
this.Add = function(element){
this.push(element);
};
};
var list3 = new Array3;
list3.Add(456); //push is not a function
alert(list3[0]); // undefined
in 3rd way i want to extend the Array object internally Array3 class.
How to do this so not to get "push is not a function" and "undefined"?
Here i add a 4th way.
//------------- 4th way
function Array4 () {
//some other properties and methods
this.Add = function(element){
this.push(element);
};
};
Array4.prototype = new Array();
var list4 = new Array4();
list4.Add(789);
alert(list4[0]);
Here again i have to use prototype.
I hoped to avoid to use extra lines outside class constructor as Array4.prototype.
I wanted to have a compact defined class with all pieces in one place.
But i think i cant do it otherwise.
ES6
class SubArray extends Array {
last() {
return this[this.length - 1];
}
}
var sub = new SubArray(1, 2, 3);
sub // [1, 2, 3]
sub instanceof SubArray; // true
sub instanceof Array; // true
Using __proto__
(old answer, not recommended, may cause performance issues)
function SubArray() {
var arr = [ ];
arr.push.apply(arr, arguments);
arr.__proto__ = SubArray.prototype;
return arr;
}
SubArray.prototype = new Array;
Now you can add your methods to SubArray
SubArray.prototype.last = function() {
return this[this.length - 1];
};
Initialize like normal Arrays
var sub = new SubArray(1, 2, 3);
Behaves like normal Arrays
sub instanceof SubArray; // true
sub instanceof Array; // true
Method names should be lowercase. Prototype should not be modified in the constructor.
function Array3() { };
Array3.prototype = new Array;
Array3.prototype.add = Array3.prototype.push
in CoffeeScript
class Array3 extends Array
add: (item)->
#push(item)
If you don't like that syntax, and you HAVE to extend it from within the constructor,
Your only option is:
// define this once somewhere
// you can also change this to accept multiple arguments
function extend(x, y){
for(var key in y) {
if (y.hasOwnProperty(key)) {
x[key] = y[key];
}
}
return x;
}
function Array3() {
extend(this, Array.prototype);
extend(this, {
Add: function(item) {
return this.push(item)
}
});
};
You could also do this
ArrayExtenstions = {
Add: function() {
}
}
extend(ArrayExtenstions, Array.prototype);
function Array3() { }
Array3.prototype = ArrayExtenstions;
In olden days, 'prototype.js' used to have a Class.create method. You could wrap all this is a method like that
var Array3 = Class.create(Array, {
construct: function() {
},
Add: function() {
}
});
For more info on this and how to implement, look in the prototype.js source code
A while ago I read the book Javascript Ninja written by John Resig, the creator of jQuery.
He proposed a way to mimic array-like methods with a plain JS object. Basically, only length is required.
var obj = {
length: 0, //only length is required to mimic an Array
add: function(elem){
Array.prototype.push.call(this, elem);
},
filter: function(callback) {
return Array.prototype.filter.call(this, callback); //or provide your own implemetation
}
};
obj.add('a');
obj.add('b');
console.log(obj.length); //2
console.log(obj[0], obj[1]); //'a', 'b'
I don't mean it's good or bad. It's an original way of doing Array operations. The benefit is that you do not extend the Array prototype.
Keep in mind that obj is a plain object, it's not an Array. Therefore obj instanceof Array will return false. Think obj as a façade.
If that code is of interest to you, read the excerpt Listing 4.10 Simulating array-like methods.
In your third example you're just creating a new property named prototype for the object Array3. When you do new Array3 which should be new Array3(), you're instantiating that object into variable list3. Therefore, the Add method won't work because this, which is the object in question, doesn't have a valid method push. Hope you understand.
Edit: Check out Understanding JavaScript Context to learn more about this.
You can also use this way in ES6:
Object.assign(Array.prototype, {
unique() {
return this.filter((value, index, array) => {
return array.indexOf(value) === index;
});
}
});
Result:
let x = [0,1,2,3,2,3];
let y = x.unique();
console.log(y); // => [0,1,2,3]
Are you trying to do something more complicated then just add an alias for "push" called "Add"?
If not, it would probably be best to avoid doing this. The reason I suggest this is a bad idea is that because Array is a builtin javascript type, modifying it will cause all scripts Array type to have your new "Add" method. The potential for name clashes with another third party are high and could cause the third party script to lose its method in favour of your one.
My general rule is to make a helper function to work on the Array's if it doesnt exist somewhere already and only extend Array if its extremely necessary.
You CANNOT extend the Array Object in JavaScript.
Instead, what you can do is define an object that will contain a list of functions that perform on the Array, and inject these functions into that Array instance and return this new Array instance. What you shouldn't do is changing the Array.prototype to include your custom functions upon the list.
Example:
function MyArray() {
var tmp_array = Object.create(Array.prototype);
tmp_array = (Array.apply(tmp_array, arguments) || tmp_array);
//Now extend tmp_array
for( var meth in MyArray.prototype )
if(MyArray.prototype.hasOwnProperty(meth))
tmp_array[meth] = MyArray.prototype[meth];
return (tmp_array);
}
//Now define the prototype chain.
MyArray.prototype = {
customFunction: function() { return "blah blah"; },
customMetaData: "Blah Blah",
}
Just a sample code, you can modify it and use however you want. But the underlying concept I recommend you to follow remains the same.
var SubArray = function() {
var arrInst = new Array(...arguments); // spread arguments object
/* Object.getPrototypeOf(arrInst) === Array.prototype */
Object.setPrototypeOf(arrInst, SubArray.prototype); //redirectionA
return arrInst; // now instanceof SubArray
};
SubArray.prototype = {
// SubArray.prototype.constructor = SubArray;
constructor: SubArray,
// methods avilable for all instances of SubArray
add: function(element){return this.push(element);},
...
};
Object.setPrototypeOf(SubArray.prototype, Array.prototype); //redirectionB
var subArr = new SubArray(1, 2);
subArr.add(3); subArr[2]; // 3
The answer is a compact workaround which works as intended in all supporting browsers.

Object type determined at runtime - Javascript (ExtJS)

This may not be possible (or might be dead easy! :) ) so here it is...
I want to be able to create objects of a type that is dependant on a variable set, without the need for a big switch statement.
I think it is possible in PHP to do something like...
$objectType = "myNewClass";
$newObject = new $objectType();
where the $newObject variable will hold an instance of the Class "myNewClass".
Is this (or any similar technique) possible with Javascript?
Thanks
Stuart
If your constructor functions are defined in the global scope, you can access it trough the bracket notation (window[fnName]):
function ObjectType1(){ // example constructor function
this.type = 1;
}
var objectType = 'ObjectType1'; // string containing the constructor function name
var obj = new window[objectType](); // creating a new instance using the string
// variable to call the constructor
See: Member Operators
CMS's answer is good, but in EXT you're probably dealing with namespaces.
I create an object map that holds any dynamic classes:
// within a namespace:
var ns = {
Thinger: function(){}
};
// globals:
var Zinger = function(){}
// map:
var classes = {
zinger:Zinger,
thinger:ns.Thinger
};
var type = "thinger";
var myClass = new classes[type](props, type, etc);
Should be doable using eval():
var obj = eval("new " + objectType + "()");

Categories