is there a way to copy a global object (Array,String...) and then extend the prototype of the copy without affecting the original one? I've tried with this:
var copy=Array;
copy.prototype.test=2;
But if i check Array.prototype.test it's 2 because the Array object is passed by reference. I want to know if there's a way to make the "copy" variable behave like an array but that can be extended without affecting the original Array object.
Good question. I have a feeling you might have to write a wrapper class for this. What you're essentially doing with copy.prototype.test=2 is setting a class prototype which will (of course) be visible for all instances of that class.
I think the reason the example in http://dean.edwards.name/weblog/2006/11/hooray/ doesn't work is because it's an anonymous function. So, instead of the following:
// create the constructor
var Array2 = function() {
// initialise the array
};
// inherit from Array
Array2.prototype = new Array;
// add some sugar
Array2.prototype.each = function(iterator) {
// iterate
};
you would want something like this:
function Array2() {
}
Array2.prototype = new Array();
From my own testing, the length property is maintained in IE with this inheritance. Also, anything added to MyArray.prototype does not appear to be added to Array.prototype. Hope this helps.
Instead of extending the prototype, why don't you simply extend the copy variable. For example, adding a function
copy.newFunction = function(pParam1) {
alert(pParam1);
};
Related
So I have this class definition:
class Field {
constructor(canvas) {
const CANVAS = document.querySelector(canvas);
const CONTEXT = CANVAS.getContext("2d");
return Object.assign(CONTEXT, Field.prototype); // *
}
prototypeMethodName() {
return "something";
}
}
console.log(new Field("canvas"));
<canvas></canvas>
The main idea here is that when I invoke new Field() I get not the instance of Field, but the object consisting of two other objects: CONTEXT and Field.prototype. The CONTEXT is the instance of the CanvasRenderingContext2D, and, basically, I just want to augument it with other methods (in this example it's just one method prototypeMethodName()).
But in this case I get a bare CONTEXT object that doesn't contain any of "my" properties.
I also noticed that the assign method doesn't work correctly only when I'm trying to merge a prototype of something to the tatget object. So the thing here is not about the CONTEXT object, but is about the prototype.
Why doesn't my code work? What would I do to make it work then?
I just want to augument it with other methods
Don't do that. Use composition instead.
But I get a bare CONTEXT object that doesn't contain any of "my" properties.
I also noticed that the assign method doesn't work correctly only when I'm trying to merge a prototype of something to the target object.
Yes. The prototype objects of classes are not containing any enumerable methods, so Object.assign doesn't find them. Have a look at ES6 Iterate over class methods.
try this
merge(context, field) {
for(let prop in Field.prototype) {
context.prototype[prop] = Field.prototype[prop];
}
return context;
}
I've defined an enumerable property in the prototype object and would like it to appear when I convert a prototyped object to JSON.
My first idea was to set it in toJSON but because I don't really want to keep it in the object afterwards I'll have to more or less clone the whole object in the function and set the necessary property.
Redefining the property in the target object and just proxying with the context of the current object doesn't seem to be an option as well, since I can't really use apply or call when getting dynamic properties.
Working solutions I could come up with so far seem to require quite an amount of code and aren't flexible and concise enough, so I'm wondering if there are any best practices of solving this task.
Here is an example which could seem a bit synthetic but still, I believe, conveys the idea:
function ProjectFolder() {
this.files = [];
Object.defineProperty(this, 'size', {enumerable: true, get: function() {
return this.files.length;
}});
}
function GithubProjectFolder() {
this.files = ['.gitignore', 'README.md'];
}
GithubProjectFolder.prototype = new ProjectFolder();
var project1 = new ProjectFolder();
JSON.stringify(project1);
// output: {"files":[],"size":0}
// size is present
var project = new GithubProjectFolder();
JSON.stringify(project);
// output: {"files":[".gitignore","README.md"]}
// size is absent
I'll have to more or less clone the whole object in the function and set the necessary property.
Yes, and there's nothing wrong with that. That's how .toJSON is supposed to work:
ProjectFolder.prototype.toJSON = function toJSON() {
var obj = {};
for (var p in this) // all enumerable properties, including inherited ones
obj[p] = this[p];
return obj;
};
However, there are two other points I'd like to make:
The size of a folder doesn't really need to be stored separately in the JSON when it already is encoded in the length of the files array. This redundant data seems to be superfluous, and can confuse deserialisation. Unless something requires this property to be present, I'd recommend to simply omit it.
In ProjectFolders, the .size is an own property of each instance - in GithubProjectFolders it is not. This suggest that you're doing inheritance wrong. Better:
function GithubProjectFolder() {
ProjectFolder.call(this);
this.files.puhs('.gitignore', 'README.md');
}
GithubProjectFolder.prototype = Object.create(ProjectFolder.prototype);
If you'd fix that alone, the size will appear in the serialisation of your project.
Let's say there is a simple object literal which name will never change:
var car = {
wheels : 4,
construct : function() {
var that = this;
setTimeout(function() {
console.log(that.wheels);
console.log(car.wheels);
}, 500);
}
};
My question is: Which way is better? Referencing by the object's name or creating a new variable (which may take some time and memory and probbaly must be done in multiple functions)?
Within the object, you should always refer to the object via this (or a copy of it, e.g. that, if required) to prevent the following breakage:
var car = ...
// do stuff
car = undefined; // or anything else, perhaps by a code hacker in the JS console
// class is now broken
You should treat the variable name that happens to have been given to your object on the outside as unknown to you, and subject to change.
Someone else might call it something else, there might be multiple names, the name might suddenly point at some other object altogether. Such variables are for the benefit of the "owners" of references to the object, and not for the object itself.
I am left scratching my head here. I am pretty new with both JSON and Javascript so I am wondering how I would go about this.
Say I have an object:
MyObject.prototype = {
// different methods and properties
_randomMethod: function MyObject_randomMethod() {
MyObject.myArray = [];
},
};
How do I declare an array property for my object (like above: MyObject.myArray = [];) and have it available throughout the object so I can access it in other methods.
Maybe this has already been covered and I am just not using the right terminology but if someone could help me out, I'd appreciate it since I can't figure it out myself.
Just so its clear, I want to declare this array property dynamically within a method like the example above and then be able to use it in other methods within this same object with the 'this' reference or something similar.
Use this to refer to the current instance:
MyObject.prototype = {
// different methods and properties
_randomMethod: function MyObject_randomMethod() {
this.myArray = [];
},
};
http://jsfiddle.net/5jSe3/
I have a function within an object constructor that is altering all objects created by that constructor. I'm not sure why. Could someone please take a look at my code and tell me what I'm missing?
A quick description of what is going on:
Warning! It might be easier to just read through the code than try to make sense of my description
I have created two new Arrays. The first one called foos, which will be an array of foo objects each one containing an array of bar objects. The second is called bars which is an array of all bar objects that are available to be added to the foos.foo.bars arrays.
When a new foo object is created using the foo object constructor it is given two arguments(aBars,bBars). aBars is an array of all bar objects to be included in the foo object. bBars is an array of all included bar objects that are considered 'special' in some way. Within the constructor there is a function that runs through every object in the bars array and if it's name value matches that of a string in the aBars argument then it is added to foo.bars array. If it's name value matches a string in the bBars argument it then has it's property bBar set to true, otherwise it's set to false.
The issue I'm having is that on the second foo object constructor when a bar object has bBar set to true or false it also changes that value in that object in all other foo.bars objects.
I realize that this is probably hard to follow. Sorry about that, it's the end of the day.
Found my own answer!
I just realized what the issue is. foos[0].bars[4] and foos[1].bars[3] are not separate objects, they are simply two different variables pointing to the same object. So when one is changed the change shows up on both. Wow, I can't believe I just spent so much time working on this when the answer was a basic fact about how javascript works that I learned back when I first started.
Ok, the new question:
How can I change this code to create duplicates of the objects instead of just pointing at the originals? This is not something I've ever had to do before.
Thanks
jsfiddle
JS:
var foos = new Array();
var bars = new Array();
function foo(aBars,bBars) {
var $this = this;
this.aBars = aBars;
this.bars = new Array();
bars.forEach(function(e,i) {
if ($this.aBars.lastIndexOf(e.barName) > -1) {
$this.bars.push(e);
if (bBars.lastIndexOf(e.barName) > -1) {
$this.bars[$this.bars.length-1].bBar = true;
} else {
$this.bars[$this.bars.length-1].bBar = false;
}
}
});
}
function bar(name) {
this.barName = name;
}
bars.push(new bar('l'));
bars.push(new bar('m'));
bars.push(new bar('n'));
bars.push(new bar('o'));
bars.push(new bar('p'));
foos.push(new foo(['l','m','n','o','p'],['n','p']));
foos.push(new foo(['l','n','o'],['n','o']));
console.log(foos);
The only way to achieve that would be to replace this line
$this.bars.push(e);
in your 'foo'-constructor with this one:
$this.bars.push(new bar(e.barName));
Cloning objects in javascript is only possible by copying their properties.