I create an empty Object in ECMAScript without a prototype and define some properties like this:
var obj = Object.create(null)
obj.name = 'John'
obj.age = 27
Here, obj is not an instance of Object.
obj.name // John
obj.age // 27
obj instanceof Object // false
obj.prototype // undefined
obj.constructor // undefined
Trying to make obj an instance of something using two methods to extend this empty Object.
Using Object.__proto__ does not work.
function EmptyObject() {
this.__proto__ = Object.create(null)
this.foo = function() { // could use defineProperty
return this.bar // obviously does not exist here
}
return this
}
var obj = new EmptyObject()
obj instanceof EmptyObject // false!
Extending EmptyObject using ES6 class extends does not work either.
class SpecialObject extends EmptyObject { ... }
let obj = new SpecialObject()
obj instanceof SpecialObject // also false
I tried faking the instance by giving obj a prototype and constructor name property like an ordinary Object but this does not change the return value of instanceof.
Is it somehow possible to make this type of Object an instance of something? Maybe by wrapping it in a function differently or using ES6 class extends? I do not mind iterating over the Object keys or manually clearing them in the function if you think that is the best method of implementation.
The purpose is to create an Object that is not instance of Object (no prototype, etc) but can be instance of something. Is this possible at all? Maybe in a sneaky hacky way.
var obj = new EmptyObject()
obj instanceof Object // false
obj instanceof EmptyObject // true
Thanks!
Assigning this.__proto__ = Object.create(null) inside the constructor will completely disable you from extending this class. Don't do that. I suspect what you are actually look for is
function EmptyObject() {…}
EmptyObject.prototype = Object.create(null);
or in ES6
class EmptyObject extends null {
…
}
which you can instantiate, on which instanceof will work, and which you can extend using class syntax. Using new EmptyObject will create an object that inherits from the EmptyObject.prototype object, which in turn does inherit from nothing (i.e. null) instead of Object.prototype as usual.
Related
This question already has answers here:
__proto__ VS. prototype in JavaScript
(34 answers)
Closed 2 years ago.
When understanding about the 3 broad type of objects in Javascript i.e. Literal, Function objects and Objects from functions,
I had read somewhere that literal objects do not have prototype property like function objects (or constructors).
Would that be a true statement ?
Reason I am asking this is since we can do;
var person = {
fn: 'ABC'
}
person.__proto__ = someObj;
Not sure if this setting using "proto" is really allowed across all browsers, which is why the statement is not true ?
Also is the same statement true for "objects from functions" ? i.e. when we do "new SomeFunctionObject()"
I had read somewhere that literal objects do not have prototype property like function objects (or constructors).
This sounds like it's referring to the property named .prototype, which is true. A function and a class will automatically receive a .prototype property, which contains one property (constructor, pointing to the class/function itself) and inheriting from Object.prototype.
The statement in your question is true. Only callable class-like objects - classes and functions - automatically receive these sorts of properties. Other objects do not:
class Foo {
}
function Foo2() {
}
const obj = {};
console.log(
Foo.prototype,
Foo2.prototype,
obj.prototype
);
Regarding the code in your question, using __proto__ is permitted, but it's deprecated. As MDN says:
Warning: While Object.prototype.__proto__ is supported today in most browsers, its existence and exact behavior has only been standardized in the ECMAScript 2015 specification as a legacy feature to ensure compatibility for web browsers. For better support, it is recommended that Object.getPrototypeOf() be used instead.
Object.getPrototypeOf should be preferred nowdays.
i.e. when we do "new SomeFunctionObject()"
When you create an instance with new, the internal prototype of the new instance will (usually) be same object as the .prototype property of the constructor. That is, with the following code:
class Foo {
// anything
}
const f = new Foo();
the internal prototype of the f instance will be the same object as Foo.prototype.
class Foo {
// anything
}
const f = new Foo();
console.log(
Object.getPrototypeOf(f) === Foo.prototype,
f.__proto__ === Foo.prototype,
);
The only time where the internal prototype of an instance will not be the same as the constructor's .prototype would be when the constructor explicitly returns an object, which is somewhat unusual.
class Foo {
constructor() {
return {};
}
}
const f = new Foo();
console.log(
Object.getPrototypeOf(f) === Foo.prototype,
f.__proto__ === Foo.prototype,
);
functions have a prototype property, which inherits from Object.
function Person(last, first, middle){
// constructors allow private variables that can be shared across methods without the use of `this.` - API developers may accidentally access properties meant to be private in classes, so use constructors
const proto = Person.prototype;
if(last !== undefined){
this.last = last; this.first = first; this.middle = middle;
}
this.getFullName = (lastFirst = false)=>{
const m = this.middle ? ' '+this.middle : ''
if(lastFirst){
return this.last+', '+this.first+m;
}
return this.first+m+' '+this.last;
}
this.setFullName = (last, first, middle)=>{
proto.last = this.last = last; proto.first = this.first = first;
proto.middle = this.middle = middle;
return this;
}
}
console.log(typeof Person);
const bob = new Person('Smith', 'Bob', 'Gene');
console.log(bob.getFullName());
const james = new Person('Brown', 'James');
console.log(james.getFullName(true));
bob.setFullName('Static', 'Method', 'like');
const person = new Person;
console.log(person.getFullName()+' -- sort of');
console.log(typeof person);
Note that a new instance of a constructor returns a new Object literal.
You can set prototype using this:
Object.setPrototypeOf(obj, prototype)
You can also read docs here.
If you explicitly assign a value to an object's proto, of course now this object has prototype property. When you just assign a value to an object, currently it has no prototype.This property has some problems on compatibility across browsers.
An object built by new Con() certainly has prototype property natively.
If we assume this sentence is true: "prototype is the object that is used to build __proto__", how Object.create works? If you do:
let obj1 = {
name: "obj1",
}
const obj2 = Object.create(obj1);
How Object.create() should create obj2.__proto__ from ob1.prototype when ob1.prototype is undefined??
Maybe Object.create() uses another method of creating prototypical inheritance than constructor or factory functions??
Because, in Object.create() example above this is true:
console.log(obj2.__proto__ === obj1);
but if we do the same thing with constructor function, this will be true:
console.log(obj2.__proto__ === obj1.prototype);
Constructing the object with the function:
function obj1(name) {
this.name = name;
}
const obj2 = new obj1();
Am I missing something?
Your sentence "prototype is the object that is used to build __proto__" only applies to functions that get called with new. E.g.
let dog = new Animal();
equals:
let dog = Object.create(Animal.prototype); // <<<
Animal.call(dog);
Prototypal inheritance itself just means that objects contain an "internal" (__proto__) reference to it's prototype. With Object.create you create an object whose prototype is set to the object passed. Therefore
let inherited = Object.create(obj)
is rather equal to
let inherited = {};
inherited.__proto__ = obj;
In JavaScript one can make an object by using the incorporated constructor of Object:
var person = new Object();
person.firstName = 'John';
person.lastName = 'Doe';
Almost all people seem to prefer the way of object-creation as a literal:
var person2 = {
firstName: 'Peter',
lastName: 'Smith'
}
Now I'm wondering:
Why is the constructor-way ignored completely?
But especially:
Are there cases in which one should prefer one way over the other?
I mean the constructor-way must be in there for some reason ...
I mean the constructor-way must be in there for some reason ...
The Object constructor was originally the only way to create an object. The object initializer syntax ({}) was added in JavaScript 1.2, the updated JavaScript in Netscape Navigator 4.0 in 1997.
There are several reasons to prefer initializer syntax to the Object constructor:
It's shorter
It's more expressive, particularly when using property initializers
It can't be overridden/shadowed (whereas Object can be)1
In contrast, there is basically no reason to use Object to create objects except indirectly, when using a variable that might point to the Object function or might point to some other function, e.g.:
function constructAndInit(ctor, props) {
var obj = new ctor();
if (props) {
for (var key in props) {
obj[key] = props[key];
}
}
return obj;
}
// Maybe use use Object...
var foo = constructAndInit(Object);
// ...or maybe you use somnething else
var bar = constructAndInit(NiftyThingy);
We do, of course, routinely use the Object function without calling it to create objects:
Any time we want to get access to the root object prototype, Object.prototype
To use the functions that are properties of Object, like Object.create, Object.defineProperty, and such
1 "can't be overridden/shadowed (whereas Object can be)" Here's an example of shadowing Object:
function foo() {
var Object = {
foo: "bar"
};
// ...later...
var o = new Object(); // fails
}
foo();
Can someone please explain this behaviour to me.
First let's create a constructor and a new object using the constructor:
var MyConstructor = function() {};
var obj = new MyConstructor();
Everything is as expected:
console.log(obj.constructor === MyConstructor); //true
console.log(obj.__proto__ === MyConstructor.prototype); //true
Let's try again, but this time let's add a custom prototype to the constructor:
var MyConstructor2 = function() {};
var myProto = { fld: 'val' };
MyConstructor2.prototype = myProto;
var obj2 = new MyConstructor2();
Now things are not as I expect them to be:
console.log(obj2.constructor === MyConstructor2); //false?!?!
console.log(obj2.constructor === Object); //true, b-but i didnt use Object..
console.log(obj2.__proto__ === MyConstructor2.prototype); //true
Why is obj2.constructor referring to Object and not MyConstructor2?
--- edit1 ---
Just to clarify. If you create a new function:
var MyConstructor = function() {};
then Javascript implementation will also create a new object:
var temp = { constructor: MyConstructor };
and set it to:
MyConstructor.prototype = temp;
The thing to note here is that the temp object overwrites the constructor field from Object.prototype (and by default Object.prototype.constructor === Object).
So when I create a new object using the constructor:
var obj = new MyConstructor();
then the object inherits the constructor field which points to MyConstructor. In the second case there was no overwriting, so the second object inherited the constructor field directly from Object.prototype.
Each Function object has a prototype property whose "constructor" property is referencing the function. When you create a new prototype using the Object literal syntax, you are created a brand new object whose constructor is literally the Object function. You need to explicitly set the constructor property:
function MyConstructor2() {
}
MyConstructor2.prototype = {
constructor: MyConstructor2
};
I want to inherit new object instance using prototype.
Test case:
var MyObj = function() {}
MyObj.prototype.objName = {}
// I want this to be a different object for each instance of MyObj
var o1 = new MyObj (),
o2 = new MyObj ();
o1.objName['a'] = 1;
o2.objName['a'] = 2;
alert(o1.objName['a']) // 2
alert(o1.objName === o2.objName) // true
This means that objects in prototype are not inherited as its copies but instead as its reference.
I know that normally you can do it like this.
var MyObj = function() {
this.objName = {}
}
var o1 = new MyObj(),
o2 = new MyObj();
alert(o1.objName === o2.objName) // false
This works fine, but in my case this is not an option. I really need to define objName outside the MyObj function.
I managed to "solve" the problem with this
MyObj.prototype.objName = function() {
if ( this._objName === undefined ) {
this._objName = {};
}
return this._objName;
}
var o1 = new MyObj(),
o2 = new MyObj();
o1.objName()['a'] = 1;
o2.objName()['a'] = 2;
alert(o1.objName()['a']) // 1
But this is not very pretty and the performance of this code is much worse.
Is there any way to solve this more elegantly ?
This means that objects in prototype are not inherited as its copies but instead as its reference.
Nothing on the prototype is copied - the whole concept of prototypical inheritance is that properties reference the shared properties of the prototype object. So if you want a property to be individual for each instance, you have to explicitly assign it to the object and shadow the prototype property; just as you're doing it with the _objName property in your code.
But this is not very pretty and the performance of this code is much worse.
If you want it pretty, move it to the constructor (or make the constructor look for something like an init method to call if exists, then you can create that init method on the prototype.
To make performance a little better, you can change the getter function to
MyObj.prototype.getObj = function() {
var obj = {};
this.getObj = function(){ return obj; }; // overwrite itself
return obj;
};
though it still has the function call overhead. For even more elegance, you can use a getter property (not supported in old browsers) that removes itself on the first access:
Object.defineProperty(MyObj.prototype, "objName", {
get: function() {
var obj = {};
Object.defineProperty(this, "objName", {
value: obj,
writable: true //?
});
return obj;
},
enumerable: true,
configurable: true
});
Now you can omit the function call parenthesis.
This means that objects in prototype are not inherited as its copies but instead as its reference.
Just to be clear. First of all in JavaScript all objects are passed by reference, not by value. Only primitives are passed by value.
Second, you're not actually "copying" or "passing" anything. When you set a prototype, you're creating a prototype's chain. It means that in your case:
var MyObj = function() {};
MyObj.prototype.objName = {} ;
var o1 = new MyObj ();
var o2 = new MyObj ();
Both o1 and o2 doesn't have any property called objName, and you can simply test it with:
console.log(Object.keys(o1)); // []
When JS see a code like o1.objName, as first thing checks if the object has this property, and if it has, use it. If not, start to looking in the prototype's chain, starting by the prototype of o1, that is MyObj.prototype: it found the properties objName, and returns it. If it didn't find it, then JS will continue to check the prototype of MyObj.prototype, and so on. So, here the point: MyObj.prototype it's an object: and you shared that object between o1 and o2. That's why the instance of objName is the same. It's exactly the same logic of having:
function objName(obj) {
return "objName" in obj ? obj.objName : O.objName;
}
var O = { objName: [] };
var foo = {};
var bar = {};
objName(foo).push(0);
objName(bar).push(1);
So, you can't put in prototype any object that is not meant to be shared across the objects creates using that prototype. I would say that shared states like that is also a bad practice that should be avoided, that's why in general prototype shouldn't have such property.
It's still not clear to me why you can't modify the constructor, but the point is: when you create the instance of your object, you have to "setup" it. Usually, calling the constructor, but any function is fine. This is made also when you want to support inheritance, and calling the "super" constructor to initialize your object.