I have two objects that are the same type and I want to copy the content of one of them to the other.
const Invoice1 = new InvoiceModel();
const Invoice2 = new InvoiceModel();
now in order to have something like : Invoice2 = Invoice1
After reading :
How do I correctly clone a JavaScript object?
I tried to use any of below commands but all of them say that invoice2 is not defined at runtime:
Invoice2 = { ...Invoice1 }; //OR
Invoice2 = Object.assign({}, Invoice1); //OR
Invoice2 = JSON.parse(JSON.stringify(Invoice1));
finally I used this function to copy the content of objects by reading this article (https://medium.com/#Farzad_YZ/3-ways-to-clone-objects-in-javascript-f752d148054d):
function CopyObject(src, target) {
for (let prop in src) {
if (src.hasOwnProperty(prop)) {
target[prop] = src[prop];
}
}
return target;
}
I wonder is there any cleaner way to do that except using above function?
I have read many post regarding this issue but all of them create a new object.
I recommend creating a method in the prototype in InvoiceModel that does this automatically for you.
class InvoiceModel {
constructor(num) {
this.num = num
}
duplicate() {
return Object.assign(Object.create(this), this)
}
}
const obj1 = new InvoiceModel(10)
console.log(obj1.num)
const obj1Copy = obj1.duplicate()
console.log(obj1Copy.num)
console.log(obj1Copy === obj1)
If the objects are just plain old data objects - with no methods or private state - you can just use a deep object clone method as specified here.
However, by the looks of things you are using classes with constructors, which implies you have methods and state. This is more tricky, because it suggests you may be relying on the constructor being re-run, e.g. to store private state in a closure, to allocate other resources, or rely on some kind of side effects. In that case you will need some kind of Invoice.prototype.clone method, that knows how to inject state into a new instance, and reruns the constructor function for the class - as per #andrew's answer.
I would avoid cloning objects with the syntax target = {...src}, which one commenter suggested. This will cause you trouble as soon as you have non-scalar reference members like sub-objects or arrays (as you will be copying pointers to the originals, not cloning their values). The same flaw applies to that CopyObject function you have picked up.
I have implemented a deep copier of objects, it does not override anything in the target option, but if you need that, you can achieve that as well:
var defaults = {
options: {
remove: true,
enable: false,
instance: {}
},
log: {
warn: true,
error: true
}
};
var config = {
options: {
remove: false,
instance: null
}
};
function applyDefaults(d, t) {
if ((typeof d !== "object") && (typeof d !== "array")) {
return d;
}
if (t === undefined) {
if (typeof d === "object") {
t = {};
} else if (typeof d === "array") {
t = [];
}
}
for (var key in d) {
if (t[key] === undefined) {
t[key] = d[key];
} else {
applyDefaults(d[key], t[key]);
}
}
return t;
}
applyDefaults(defaults, config);
console.log(config);
However, this will not copy "private" stuff, not defined as members of this.
Related
I want to create function that will return all properties of an object:
This is my initial code:
function dir(obj) {
if (obj === null || obj === Object.prototype) {
return [];
}
var names = Object.getOwnPropertyNames(obj);
return names.concat(dir(Object.getPrototypeOf(obj)));
}
and I have ES6 class where I want to hide some properties:
class Lexer {
constructor(input, { whitespace = false } = {}) {
this.__input__ = input.replace(/\r/g, '');
var internals = {};
[
'_i', '_whitespace', '_col', '_newline', '_line',
'_state', '_next', '_token', '_prev_char'
].forEach(name => {
Object.defineProperty(this, name, {
configurable: false,
enumerable: false,
get() {
return internals[name];
},
set(value) {
internals[name] = value;
}
});
});
this._whitespace = whitespace;
this._i = this._line = this._col = this._newline = 0;
this._state = this._next = this._token = null;
this._prev_char = '';
}
token() {
}
...
}
The problem I have is that Object.getOwnPropertyNames return enumerable properties and Object.key don't return ES6 class methods. So I if Use the first function I get hidden props and if I use second I only get __input__.
Is there a way to detect if something is hidden property and not ES6 class method? I don't want to detect if something is a function because I want something more generic there can be not enumerable properties that are functions.
I've also found that for ES5 constructor function there is constructor property that is also not enumerable and behave like ES6 method (in case of my Lexer class it work the same token method).
NOTE: this is not Y/X problem I want to create dir function for my Scheme interpreter that will work like similar function in Python, it should work with ES5 function constructor objects and ES6 class objects.
I've tried this:
function dir(obj, proto) {
if (obj === null || obj === Object.prototype) {
return [];
}
var names = Object.getOwnPropertyNames(obj);
if (!proto) {
names = names.filter(name => {
var d = Object.getOwnPropertyDescriptor(obj, name);
return d.configurable && d.enumerable;
});
}
return names.concat(dir(Object.getPrototypeOf(obj), true));
}
but I don't know if this is the best way, since the user may want to create property descriptor of prototype (i'm not sure if this is something that may happen). I want 100% working solution and not work around. Do you know any method to make it work?
EDIT:
Even that I have working solution for my case, I'm wondering if ES6 properties in fact are indistinguishable from not enunumerable properties.
Especially if you can detect if foo is different then bar:
class Test {
constructor() {
Object.defineProperty(this, 'foo', {
enumerable: false,
configurable: false,
value: function() { alert('foo') }
});
}
bar() {
alert('bar');
}
}
Is the only difference is that bar is on prototype object and there are no other way to detect the difference?
Is there a way to detect if something is hidden property and not ES6 class method? I don't want to detect if something is a function
There's no distinction. A class method is just a non-enumerable function-valued property of the prototype object.
If you absolutely had to distinguish them, you could .toString() the function and check whether it has method syntax, but that still won't catch methods that are copied around and miss ES5 methods. (See also How do you check the difference between an ECMAScript 6 class and function? and How can I differentiate between an arrow function, class and a normal function? for related topics)
Basically, socket.io uses nativeJSON to enconde and decode packets, and my problem is that I am obliged to use this version of prototype which changes JSON behaviour. When I should get in the server something like:
socket.on('event', function (a, b, c),
I get
socket.on('event', function ([a, b, c], undefined, undefined).
One solution is to comment this lines on json.js:
/* socket.io-client/lib/json.js
if (nativeJSON && nativeJSON.parse){
return exports.JSON = {
parse: nativeJSON.parse
, stringify: nativeJSON.stringify
};
}
*/
but this change affects performance seriously.
Is there a way to recover native JSON functionality?
Would it be possible to create a hidden iframe just to clone the JSON object to restore the old functionality?
One solution is to kill Prototype's toJSON() extension methods:
if(window.Prototype) {
delete Object.prototype.toJSON;
delete Array.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}
Then you should be able to use the browser's native JSON.parse/stringify methods without issue.
If you don't want to kill everything, and have a code that would be okay on most browsers, you could do it this way :
(function (undefined) { // This is just to limit _json_stringify to this scope and to redefine undefined in case it was
if (true ||typeof (Prototype) !== 'undefined') {
// First, ensure we can access the prototype of an object.
// See http://stackoverflow.com/questions/7662147/how-to-access-object-prototype-in-javascript
if(typeof (Object.getPrototypeOf) === 'undefined') {
if(({}).__proto__ === Object.prototype && ([]).__proto__ === Array.prototype) {
Object.getPrototypeOf = function getPrototypeOf (object) {
return object.__proto__;
};
} else {
Object.getPrototypeOf = function getPrototypeOf (object) {
// May break if the constructor has been changed or removed
return object.constructor ? object.constructor.prototype : undefined;
}
}
}
var _json_stringify = JSON.stringify; // We save the actual JSON.stringify
JSON.stringify = function stringify (obj) {
var obj_prototype = Object.getPrototypeOf(obj),
old_json = obj_prototype.toJSON, // We save the toJSON of the object
res = null;
if (old_json) { // If toJSON exists on the object
obj_prototype.toJSON = undefined;
}
res = _json_stringify.apply(this, arguments);
if (old_json)
obj_prototype.toJSON = old_json;
return res;
};
}
}.call(this));
This seems complex, but this is complex only to handle most use cases.
The main idea is overriding JSON.stringify to remove toJSON from the object passed as an argument, then call the old JSON.stringify, and finally restore it.
I'm having a bit of trouble with a small Javascript library that I am creating for practice that mimics jQuery and other libraries. As of now it does not work. I'm very new to Javascript and to scripting in general as I have only started teaching myself, so chances are I'm simply missing something that would be obvious to most. I have tried searching for a solution, but I have not been able to find one, so I have resorted to asking about it myself.
This is the library itself:
(function(window, undefined) {
var document = window.document
var $m = function(obj) {
if (typeof obj === "object") {
return obj
}
else {
return document.getElementById(obj)
}
class: function(name){
var ele = document.getElementsByTagName('*')
var objects = []
for (var q = 0 ; q < ele.length ; ++q){
if (ele[q].className === name){
objects.push(ele[q])
}
}
return objects
}
s: function(){
return this.style
}
window.$m = $m
})(window)
A brief edit: $m is intended to be an object with methods class and s.
And this is how it is referenced in a simple test page:
<h1 class="heading" onmouseover="$m(this).s.setAttribute('text-decoration', 'underline')" onmouseout="$m(this).s.setAttribute('text-decoration', 'none')">Testing.</h1>
Another edit: I have attempted to do this, and although it throws no errors it does not work correctly. I'm a bit stumped with what exactly is not being called.
Here is the new library:
(function(window, undefined) {
//set document to this local scope
var document = window.document
//create $m object
var $m = function(obj) {
//if it is anything but an element return itself
if (typeof obj === "object") {
return new init(obj);
}
//if it is an element return that element
else {
return new init(document.getElementById(obj));
}
}
function init(elem){
//set properties
this.elem = elem;
}
init.prototype = {
//return style
sty: function() {
return this.elem.style;
},
//return all objects with a certain class
cla: function() {
var allelem = document.getElementsByTagName('*')
var give = []
//gather all elements in the document and get those with matching class
for (var q = 0 ; q < allelem.length ; ++q){
if (allelem[q].className === this.elem.className){
give.push(allelem[q]);
}
}
//return found elements
return give;
}
}
//expose $m to global scope
window.$m = $m;
})(window)
and an attempt to fix how it is referenced:
<h1 class="heading" id="test" onmouseover="$m(this).sty.textDecoration='underline'" onmouseout="$m(this).sty.textDecoration='none'">Testing.</h1>
There's a couple of things wrong here.
First, as user1689607 says, your syntax is invalid; you want (I think) the following for the last part:
function class(name){
var ele = document.getElementsByTagName('*')
var objects = []
for (var q = 0 ; q < ele.length ; ++q){
if (ele[q].className === name){
objects.push(ele[q])
}
}
return objects
}
function s() {
return this.style
}
This still won't work, though, since class is a reserved word.
Furthermore, your code from further up isn't going to allow you to do what you're trying to do in your HTML. What you're looking for is to create a new object containing the parameter you pass to $ as a field, with the functions you've defined as members.
Try the following:
function MyObjConstructor(param) {
this.field = param;
}
MyObjConstructor.prototype =
{
s:function(){...}//As your S function, but replace "this" with "this.field"
clazz:function(){...}//As your class function, but replace "this" with "this.field"
}
and finally
function $m(obj) {
if (typeof obj === "object") {
return new MyObjConstructor(obj);
}
else {
return new MyObjConstructor(document.getElementById(obj));
}
}
This results in each object that is passed to your $ function being wrapped, and exposing the appropriate methods. I'm not sure how jQuery does it, but this is probably a good place to start.
class: function(name){ … }
s: function(){ … }
I don't know what you expect those statements to do. Usually, something like this appears in an object literal, but in here you just have labeled statements. Also, you are missing a closing brace somewhere.
From your usage, it seems you want them to be properties of the returned object. As you return a plain DOM element, you would need to extend them with those custom properties:
var el = document.getElementById(obj);
el["class"] = function(name){ … }; // notice that "class" is a reserved word,
// you should not use it as a (property) identifier
el.s = function(name){ … }; // notice this defines a /function/,
// which you still had to invoke - you can't just use ".s…"
return el;
This is generally considered bad practice, and you won't rejoice in it. Read the article for better patterns.
Btw, style declarations have no setAttribute method as DOM elements have. You can either directly assign to a property, or use the setProperty method.
Also, the s or sty property is a function. You will need to call it before you can access the style declaration object: $m(this).sty().textDecoration = …; "getStyle" might be a more appropriate name.
var Editor = {};
Editor.Basic = function(obj) {
this.config ={
value: obj
}
};
Editor.Basic.prototype = {
getValue: function() {
return this.config.value;
}
};
Editor.Advanced = function(obj) {
Editor.Basic.call(this, obj);
};
Editor.Advanced.prototype = {
config: {
notValue: !this.config.value
}
};
var extendByPrototype = function(obj1, obj2) {
for (var key in obj2.prototype) {
if (obj2.prototype.hasOwnProperty(key) && obj1.prototype[key] === undefined)
obj1.prototype[key] = obj2.prototype[key];
}
};
extendByPrototype(Editor.Advanced, Editor.Basic);
Is there anyway to get the Editor.Advanced.prototype to extend existing objects (recursively of course) instead of overriding them? (As seen in extendByPrototype)
I know I would check obj1.prototype[key] !== undefined, but I am unsure as what I need to do to extend existing keys in a generic way, without moving the config from Editor.Advanced.prototype to the constructor and using the push function.
The proper way of extending an Object in JavaScript is using Object.create(prototype). Objects created this way will have proper inheritance setup. To get the prototype of any object, you'll use Object.getPrototypeOf(object).
Object.create(prototype) is new in JavaScript 1.8.5. If you're looking for backward compatible, you'll have to use the non-standard way
function extend(child, supertype) {
child.prototype.__proto__ = supertype.prototype;
}
extend(Animal, Lifeform);
extend(Plant, Lifeform);
var anOnion = new Plant();
After that, you can get the prototype object by...
object.contructor.__proto__
More details on __proto__ here: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto
STORE = {
item : function() {
}
};
STORE.item.prototype.add = function() { alert('test 123'); };
STORE.item.add();
I have been trying to figure out what's wrong with this quite a while. Why doesn't this work? However, it works when I use the follow:
STORE.item.prototype.add();
The prototype object is meant to be used on constructor functions, basically functions that will be called using the new operator to create new object instances.
Functions in JavaScript are first-class objects, which means you can add members to them and treat them just like ordinary objects:
var STORE = {
item : function() {
}
};
STORE.item.add = function() { alert('test 123'); };
STORE.item.add();
A typical use of the prototype object as I said before, is when you instantiate an object by calling a constructor function with the new operator, for example:
function SomeObject() {} // a constructor function
SomeObject.prototype.someMethod = function () {};
var obj = new SomeObject();
All the instances of SomeObject will inherit the members from the SomeObject.prototype, because those members will be accessed through the prototype chain.
Every function in JavaScript has a prototype object because there is no way to know which functions are intended to be used as constructors.
After many years, when JavaScript (ES2015 arrives) we have finally Object.setPrototypeOf() method
const STORE = {
item: function() {}
};
Object.setPrototypeOf(STORE.item, {
add: function() {
alert('test 123');
}
})
STORE.item.add();
You can use JSON revivers to turn your JSON into class objects at parse time. The EcmaScript 5 draft has adopted the JSON2 reviver scheme described at http://JSON.org/js.html
var myObject = JSON.parse(myJSONtext, reviver);
The optional reviver parameter is a
function that will be called for every
key and value at every level of the
final result. Each value will be
replaced by the result of the reviver
function. This can be used to reform
generic objects into instances of
pseudoclasses, or to transform date
strings into Date objects.
myData = JSON.parse(text, function (key, value) {
var type;
if (value && typeof value === 'object') {
type = value.type;
if (typeof type === 'string' && typeof window[type] === 'function') {
return new (window[type])(value);
}
}
return value;
});
As of this writing this is possible by using the __proto__ property. Just in case anyone here is checking at present and probably in the future.
const dog = {
name: 'canine',
bark: function() {
console.log('woof woof!')
}
}
const pug = {}
pug.__proto__ = dog;
pug.bark();
However, the recommended way of adding prototype in this case is using the Object.create. So the above code will be translated to:
const pug = Object.create(dog)
pug.bark();
Or you can also use Object.setPrototypeOf as mentioned in one of the answers.
Hope that helps.
STORE = {
item : function() {
}
};
this command would create a STORE object. you could check by typeof STORE;. It should return 'object'. And if you type STORE.item; it returns 'function ..'.
Since it is an ordinary object, thus if you want to change item function, you could just access its properties/method with this command.
STORE.item = function() { alert('test 123'); };
Try STORE.item; it's still should return 'function ..'.
Try STORE.item(); then alert will be shown.