Look at the code bellow. I would except that the output would be 123-admin 0-null. But instead its 123-admin 0-admin. Can you explain me why the new keyword does not replace the entire user object?
function User()
{
}
User.prototype.id=0;
User.prototype.role={name:null}
var user = new User();
user.role.name = 'admin';
user.id = 123;
window.document.write(" "+user.id+"-"+user.role.name);
user = new User();
window.document.write(" "+user.id+"-"+user.role.name);
Also in JSFiddle:
https://jsfiddle.net/tfgx77au/6/
The role property is on the prototype, so that object is shared among all instances created with the constructor.
If you wanted a separate role object on each user, you'd have to initialize that in the constructor (or somewhere at least):
function User() {
this.role = { name: null };
}
Related
Here is my (simplified) constructor:
class User {
constructor() {
Object.assign(this, setup);
}
}
const setup = {
name: ''
}
export { User }
When I create a new object
import {
User
} from './User.js';
let user1 = new User();
user1 is what I expect.
If I declare user1.name = 'John' and then create another user let user2 = new User() then user2.name will be John, not an empty string.
Can someone explain why?
It appears setup is being changed and referenced instead of the new user object that was created.
It appears setup is being changed and referenced instead of the new user object that was created.
Yes. You need to make a copy of object before assigning it to instance of class
class User {
constructor() {
Object.assign(this, {...setup});
}
}
For anyone else running into similar issues, I finally figured it out. It boils down to shallow copying versus deep copying in Javascript. Both spread (...) and Object.assign() perform a shallow copy while JSON methods perform a deep copy.
If your setup is a nested object such as
const setup = {
name: '',
address: {
city: ''
}
}
the address object inside of setup will remain a referenced value, not a primitive value, when {...} or Object.assign() is used.
If you want to copy the whole object, you need to copy it as JSON.parse(JSON.stringify(setup)) so my constructor should be
class User {
constructor() {
Object.assign(this, JSON.parse(JSON.stringify(setup)));
}
}
since this performs a deep (nested) copy.
The following as you'd expect doesn't work:
let User = {
foo() {
User.prop = 1;
}
};
let User2 = User;
User = null;
User2.foo(); // Cannot set property of null
console.log(User2.prop);
This works, though:
class User {
static foo() {
User.prop = 1;
}
}
let User2 = User;
User = null;
User2.foo();
console.log(User2.prop); // 1
Since functions and classes are objects, and in both cases I set a property for it, why is it that the results differ? Where is it getting User reference?
Similar to named function expressions, classes are wrapped in an extra scope that contains an immutable binding with their name and value.
If we desugar the class syntax to ES5 faithfully, we'd get something like
let User = (() => {
const User = function() {};
// ^^^^^^^^^^
User.foo = function() {
User.prop = 1;
};
return User;
})();
let User2 = User;
User = null;
User2.foo();
console.log(User2.prop); // 1
This inner User declaration is the one that the foo method is closing over. Overwriting the outer variable doesn't matter to it.
Inside the declaration of a class, the name of that class is permanently bound. This is just one of the distinctions between old-school constructor-function classes and proper ES2015 classes. (Another important distinction that functions of a given name can be safely redeclared, but classes of the same name cannot.)
In second example you get 1 because are referring to a static variable even it is inside a class that is not instantiated before use it. Removing the static and instantiating correctly it return 1 anyway:
class User {
foo() {
this.prop = 1;
}
}
let User2 = new User();
User = null;
User2.foo();
console.log(">>" + User2.prop); // 1
Note User = null is irrelevant.
I came to a code that contains these lines
var data = function() {
function Metadata() { /*some initialization here*/ }
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
return Metadata;
}
I struggle to understand what is actually going on, and how to use the returning object. If I understand it correctly, data will now be an object that should be initialized like this
var d = new data()
But I don't understand the following lines and why Object.create() is used instead of the new keyword:
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
What do they do? Are they necessary? And what is the difference between Object.create and new ?
JavaScript is a prototype-based language.
It means that it don't use the class keyword as in other languages. Instead JavaScript use functions as classes.
In your example the data variable can be assimilated to a class:
var Data = function() { ... }
To create an instance of this class we use the new keyword assigning the result of type object to a variable.
var data = new Data()
Since ECMA Script 6 we can use the instantiation method Object.create() that create an uninitiated object with the specified prototype object and properties. It takes in argument the object which should be the prototype of the newly-created object. (It also copy the constructor)
So the following lines are a way to make Metadata extending the Backend object and keeps its own constructor:
// Metadata extends Backend
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
But this code is not exactly equivalent to Metadata.prototype = new Backend();. See this example:
//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();
/*
* Then the results are different (see code snippet at the end of this post)
*/
//result1 = 'undefined'
var data1 = new Metadata1();
var result1 = data1.publicProperty;
//result2 = 'SomeValue'
var data2 = new Metadata2();
var result2 = data2.publicProperty;
In fact both are very similar, the main difference is that new keyword actually runs constructor code, whereas Object.create will not execute code.
One other difference is that with Object.create you can create an object that doesn't inherit from anything (Object.create(null)).
Whereas if you do Metadata.prototype = null the newly created object will inherit from Object.prototype
Note: In some older browser (IE8 and below) you can use this equivalent code to Object.create:
Object.create = function(o){
function F(){}
F.prototype=o;
return new F();
}
Here is the working code snippet that shows the differences between the two approach
//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();
//result: 'undefined'
var data1 = new Metadata1();
$("#result1").text("result1: " + (typeof data1.publicProperty=='undefined' ? 'undefined' : data1.publicProperty));
//result: 'SomeValue'
var data2 = new Metadata2();
$("#result2").text("result2: " + (typeof data2.publicProperty=='undefined' ? 'undefined' : data2.publicProperty));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result1"></div>
<div id="result2"></div>
Those two lines are the prototypical way of having Metadata extend Backend.
Classes in JavaScript are defined as functions. If you define the prototype on a function, you can use the new keyword (or Object.create) to create an instance of the class. The functions/properties that are on the prototype will be on the new instance of the class you create.
In the above code, Metadata.prototype is being set to an instance of Backend, so Metadata.prototype will inherit call the methods/properties on Backend.prototype.
You can find more at Inheritance and the prototype chain.
In JavaScript there are no classes, and instead we have constructors that can be called with the new keyword to create new objects, so we will get the same behavior as classes and instanciation.
And these two lines are used to express inheritance, and to make Metadata extends Backend, in the line:
Metadata.prototype = Object.create(Backend.prototype);
We define the prototype of Metadata, object which should be the prototype of the newly-created object.
While in this line:
Metadata.prototype.constructor = Metadata;
We define the constructor of Metadata which will be used to create new instances of Metadata.
And inheritance can be tested like this:
var meta = new Metadata();
console.log('Is meta an instance of Metadata? ' + (meta instanceof Metadata)); // true
console.log('Is meta an instance of Backend? ' + (meta instanceof Backend)); // true
And you can find more about it with another example in the Object.create() documentaion here.
Object.create is an ES6 method, it creates an object with the given object as prototype.
Metadata.prototype = Object.create(Backend.prototype);
So the above line means Metadata inherits all properties and methods from Backend. It's somehow similar to the following line before ES6:
Metadata.prototype = new Backend();
However, Metadata also inherits constructor property from Backend:
var metaData = new Metadata();
metaData.constructor; // function Backend()
This look weird because metaData is actually constructed with Metadata, so we fix it like this:
Metadata.prototype.constructor = Metadata;
var metaData = new Metadata();
metaData.constructor; // function Metadata()
Notice that this doesn't mess with instanceof operator:
metaData instanceof Metadata // true
metaData instanceof Backend // true
function user(){
user.name ="Amine";
user.lastName ='Meziane';
document.write(user.name);
};
user();
When executing it writes "user" only and not the name "Amine"
user refers to the user function, which comes with an existing, unassignable name property initialized to the function name. The user.name ="Amine"; assignment is ignored.
As user2357112 pointed out, your Code tries to modify the name property of the function user. This property isn't modifieable. And so it doesn't change. The name property of the function user, contains the name of the function user, which is "user" :-). And this name is what your code prints out.
You can write:
function user(){
var user = {};
user.name = "Amine";
user.lastName = "Meziane";
document.write(user.name);
};
user();
Here user(.name) will not refer to the function user, but to the local variable (var) user, which is initialized with an object literal ({}).
Maybe you wanted to write an constructor function. Then you would add properties to this.
function User(name, lastname){
this.name = name;
this.lastName = lastname;
return this; //optional
};
var userAmine = new User("Amine", "Meziane"); // don't forget "new"
document.write(userAmine.name);
Maybe you can read JavaScript Patterns by Stoyan Stefanov for deeper understanding of JavaScript.
Because the name of the function is read-only. Refer to documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
window.onload = function() {
function user(){
var json = '{"name":"Amine","lastName":"Meziane"}',
obj = JSON.parse(json);
document.body.innerHTML=obj.name+' '+obj.lastName;
}
user();
};
//or
function user(){
var user = {};
user.name ="Amine";
user.lastName ='Meziane';
document.write(user.name);
};
user();
I have a rather academic question that doesn't particularly apply to anything I'm doing, I just really want to know the answer!
Say we have a simple object definition in the global namespace as such:
TestObject = function(){};
It has a method added to it's prototype that can be instantiated into a new object itself:
TestObject.prototype.AnotherObject = function() {};
Instantiate the first object:
var myObject = new TestObject();
Now my question is this:
How does
myObject.myProperty = new myObject.AnotherObject();
differ to
myObject.myProperty = new TestObject.prototype.AnotherObject();
Or are they exactly the same?
The difference I see is this: I could use the second method to instantiate objects within the TestObject context without knowing the name of the instantiated object itself, i.e.
TestObject.prototype.createAnObject = function() {
this.anotherProperty = new TestObject.prototype.AnotherObject();
}
And finally:
What are the implications of using a prototype method to instantiate an object of the same name? Why do this result in an infinite loop? (What actually happens inside..)
TestObject.prototype.AnotherObject = function () {
this.AnotherObject = new TestObject.prototype.AnotherObject();
};
myObject.AnotherObject();
Yet this does not...
TestObject.AnotherObject = function() {};
TestObject.prototype.createAnObject = function() {
this.AnotherObject = new TestObject.prototype.AnotherObject();
};
myObject.createAnObject();
...
I have a deep desire to understand the relationships between objects here! Thank you!
The reason I ask these questions is because I want to make something like so where there is a 1:1 relationship between objects:
ClientObject = function () {
this.objectname = "a client class";
}
ClientObject.prototype.loginUser = function(name) {
this.loggedin = true;
if (typeof this.User === 'undefined') {
this.User = new ClientObject.User(name);
}
}
ClientObject.User = function (name) {
this.username = name;
}
ClientObject.User.prototype.getProfile = function() {
return 'user profile';
}
var testClient = new ClientObject();
console.log('testClient.User = ' + (typeof testClient.User)); // should not exist
testClient.loginUser('Bob'); // should login 'bob'
console.log('testClient.User = ' + (typeof testClient.User)); // should exist now Bob is logged in
console.log(testClient.User.username); // should be bob
testClient.loginUser('Tom'); // should not do anything as User object already created
console.log(testClient.User.username); // bob still
console.log(testClient.User.getProfile()); // new functionality available
I am just not sure if I'm breaking any best practises or conventions here unwittingly.
myObject.myProperty = new myObject.AnotherObject();
differ to
myObject.myProperty = new TestObject.prototype.AnotherObject();
There's no difference at all. Remember, objects in JavaScript have a prototype chain. When you call new myObject.AnotherObject(); the engine will first check for a AnotherObject on myObject itself. Failing to find it, it will check on myObject's prototype, which it will find. The second version
myObject.myProperty = new TestObject.prototype.AnotherObject();
Just goes right to the place where AnotherObject is defined.
TestObject.prototype.AnotherObject = function () {
this.AnotherObject = new TestObject.prototype.AnotherObject();
}
myObject.AnotherObject();
Just walk through the code. When you say: myObject.AnotherObject();, AnotherObject will be called, with this set to myObject. The first line of that will attempt to create a new property on myObject (which is this) by setting it to the result of
new TestObject.prototype.AnotherObject();
which will then re-enter the very same AnotherObject function, but this time with this set to a new object whose prototype is set to TestObject.prototype.AnotherObject's prototype. And so on ad infinitum
Finally,
TestObject.prototype.createAnObject = function() {
this.AnotherObject = new TestObject.prototype.AnotherObject();
}
myObject.createAnObject();
Will not cause an infinite loop, so far as I can tell, and as far as I can test: FIDDLE
Basically, createAnObject will enter with this set to myObject. Inside of which a brand new property called AnotherObject will be created on myObject, which will be set to a new invocation of the AnotherObject function you previously set up.
Note that after this call is made, the AnotherObject function will still exist, but, it will be shadowed by the AnotherObject property you just created. So now you'll never ever be able to say
var f = new myObject.AnotherObject()
Because you now have a AnotherObject property sitting right on myObject, which will be found and returned before anything on the prototype is ever checked.
Well, I mean, you could always say delete myObject.AnotherObject and remove that property from the object, which would then open you up to the AnotherObject being found on the prototype, but really, you should avoid name conflicts like this to begin with.
Regarding your last bit of code
A) Why not make User its own function?
B) Why not set up this.User = new ...() right in the ClientObject constructor function? That way you wouldn't need the undefined check
C) ClientObject should be defined as
function ClientObject(){...`
the you have it now seems to be creating an implicit global.