So, I was writing some example code implementing another function for Constructor[Symbol.hasInstance] and I noticed my new implementation just won't get called.
The script below is what I expected to happen:
function Pirate(name) {
this.name = name;
}
const jackSparrow = {
isPirate: true
};
// Notice how `jackSparrow` is not yet considered an instance of the `Pirate` object
console.log(jackSparrow instanceof Pirate); // false
// Now let's assign another function for `Pirate[Symbol.hasInstance]`
Pirate[Symbol.hasInstance] = function (anObj) {
return anObj.isPirate;
};
// This will cause Pirate[Symbol.hasInstance] to be called with `jackSparrow`
console.log(jackSparrow instanceof Pirate); // true
I tried to add a console.log call to to my Pirate[Symbol.hasInstance] implementation, but it won't log anything to the console.
Does anyone have any idea of what is happening? Why is my implementation not getting called?
I'm running this on Node 6.9.1.
You can find the answer if you do
Object.getOwnPropertyDescriptor( Function.prototype, Symbol.hasInstance).writable
It returns false: you cannot write to the Symbol.hasInstance property of a function with the assignment = operator. The property never gets set and so it never gets called. (Failing silently feels like unhelpful behaviour to me, but there you go. A TypeError is thrown with a helpful message if you are in strict mode, one of the many reasons you should use it all the time.) You can only define the Symbol.hasInstance property on a function with Object.defineProperty.
Object.defineProperty(Pirate, Symbol.hasInstance, {
value: function(anObj) {
console.log('Is he a pirate?');
return anObj.isPirate;
}
});
Now jackSparrow instanceof Pirate first logs the question, then returns true.
#lonesomeday's answer explains the reason. Assignments don't define a property if the object already inherits that property as non-writable.
If you don't want to use explicit property definitions, consider using the class syntax:
class Pirate {
constructor(name) {
this.name = name;
}
static [Symbol.hasInstance](anObj) {
return anObj.isPirate;
}
}
const jackSparrow = {
isPirate: true
};
console.log(jackSparrow instanceof Pirate); // true
Related
I have this class
class Dark {
constructor(name) {
this.name = name;
}
destroy() {
console.log("method called")
console.log(this);
}
}
const DarkObject = new Dark('DarkObject');
const copyDarkObject = {destroy: DarkObject.destroy}
console.log(copyDarkObject.destroy())
//> method called
// > undefined
I stored the reference of the class method to the key of a other Object
const copyDarkObject = { destroy: DarkObject.destroy };
the created Object is not the owner of the method so the return is undefined thats clear to me but I can still invoke the method from the key with console.log(copyDarkObject.destroy())
how is that possible ?
the created Object is not the owner of the method so the return is undefined
No. The statement
console.log(copyDarkObject.destroy())
logs undefined because the destroy method doesn't return anything (since it doesn't use a return statement).
The statement
console.log(this)
in the destroy method will log your copyDarkObject, because destroy was called on copyDarkObject.
And that's why the log says:
method called
Object { destroy: destroy() } // the object destroy was invoked on
undefined // the return value of destoy
Class keyword in JavaScript is just syntactic sugar.
Under the hood it is still prototype based inheritance/system, which allows some extra flexibility.
What exactly happens in your code:
You see returned value undefined - this is correct, because you never return anything from destroy() and Js by default "returns" undefined from functions.
You create a new object giving it one property, which is a reference to darkObject.destroy - value of this pointer/reference in Js depends on how function was called. You can read up on MDN on how built-in function like bind() or apply or call work. In this case when you call destroy on copyOfDarkObject, this points to copyOfDarkObject
class Dark {
constructor(name) {
this.name = name;
}
destroy() {
console.log('I am calling destroy on', this);
return `I am ${this.name}`;
}
}
const darkObject = new Dark('DarkObject');
const copyOfDarkObject = {destroy: darkObject.destroy}
console.log('returned value', copyOfDarkObject.destroy());
console.log('returned value', darkObject.destroy());
console.log(copyOfDarkObject instanceof Dark);
console.log(darkObject instanceof Dark);
Some extra info:
While logging this you can see, that copyOfDarkObject has one property called destroy, but darkObject only one called name. It is so, because destroy method exists on darkObject prototype and you don't see it directly on the object.
I have a simple object and constructor in Traditional JavaScript as follows:
function Test() {
this.data={};
}
Test.prototype={
set value(something) {
},
get value() {
return data[property];
}
};
var test=new Test();
Inside the object is another object, initially with no own properties.
I would like to write a Setter for value which sets a property on the inner object (data).
test.value.whatever=23;
Is there any way I can do this?
I expect that the Setter function could then finish the job with something like this:
set value() {
// how do I get property & value?
data[property]=value;
},
Here you go
function Test() {
this.data = {};
}
Test.prototype={
set value(v) {
this.data.whatever = v;
},
get value() {
return this.data.whatever;
}
};
var test = new Test();
test.value = 'hi';
console.log(test.value) // 'hi'
console.log(test.data) // {whatever: 'hi'}
Remember to put the data property in the constructor. Otherwise, if you put it into the prototype, it will be a shared property among every instance.
OK, I have an answer: as suggested by #Bergi & #damianmr, I used (and had to learn about) a Proxy object.
Here is a working version:
function Test() {
this.dataStore={};
this.data=new Proxy(this,{
set(target,property,value) {
target.dataStore[property]=value;
},
get(target,property) {
return target.dataStore[property];
}
});
}
Test.prototype={
};
var test=new Test();
test.data.whatever=23;
test.data.etc=46;
alert(`${test.data.whatever} ${test.data.etc}`);
As you can see:
I have an object called dataStore, and a proxy called data
The Proxy is set in the constructor
This is a simple abstracted case, but it also works for the more complex project I’m working on.
The only shortcoming is that IE doesn’t support Proxy, and the polyfills I have seen don’t like new properties.
That just means I will need to supply a functional alternative for Legacy Browsers, and wait for them to die out …
JavaScript uses a Prototype system, which is fundamentally different than a Class system. This is my first serious encounter with the language. I had fooled around with it previously, but this is the first time I built a system with proper OO, inheritance, polymorphism, etc.
From what I read there seems to be a few common methods to do member function inheritance in Javascript. Assuming you have a parent foo as following
foo = function(){ this.one = 1; }
foo.prototype.func1 = function(){return this.one;}
The MDN Introduction to JavaScript Inheritance suggests the naive approach of invoking the parent's method in the context of the child, as shown below.
bar = function(){ foo.call(this); }
bar.prototype = Object.create(foo.prototype);
bar.prototype.func1 = function(){ return this.one + foo.prototype.func1();}
This has the advantage of being simple to understand, but can become cumbersome as pointed out in this Salsify Blog post. The blog post outlines an alternate method where a super property is defined in the child prototype, and the name of each member function is attached as a property to the method. This method, however, relies on the caller property of a method, which the article points out will soon be deprecated. Rather than duplicate the entire post, I believe a summary of the important points are these
Object.defineProperty(bar.prototype, "super", {
get: function get() {
...
// methodName is set as a property on each method in an omitted code segment
methodName = get.caller.methodName;
...
Object.getPrototypeOf(this.prototype)[methodName]
}
}
Which is to say that you find the method with the same name in your prototype's prototype. I was wondering if this can be done in a simpler manner, without having to attach the method name as a parameter and without the Function.caller.
foo.prototype.super = function(method) {
superMethod = Object.getPrototypeOf(this.constructor.prototype)[method];
return superMethod.call(this, Array.prototype.slice.call(arguments, 1));
}
bar.prototype.func1 = function(){ return this.one + super('func1'); }
I'm making a number of assumptions in the above, I'd like to verify some assumptions.
new bar().constructor.prototype === Object.getPrototypeOf(new bar())
If the above is always true, is one preferable over the other?
The Parent's member function will always live in the child's prototype's prototype (assuming that neither of the prototypes were mutated after object creation)
That Object.getPrototypeOf() is not the "language support for accessing super methods" that the blog refers to as being added in ES6
If Object.getPrototypeOf() isn't that language support, what is?
After seeing the error of using this, which does not change throughout the execution and always refers to the instance of the subclass, I've revisited and am thinking I need something like this
Grandfather = function(){};
Grandfather.prototype.function1 = function(){console.log("I am the Grandfather");};
Father = function(){Grandfather.apply(this);};
Father.prototype = Object.create(Grandfather.prototype);
Father.prototype.function1 = function f(){ f.super(); console.log("I am the Father");};
Father.prototype.function1.super = Grandfather.prototype.function1;
Child = function(){Father.apply(this);}
Child.prototype = Object.create(Father.prototype);
Child.prototype.function1 = function f(){ f.super(); console.log("I am the Child");};
Child.prototype.function1.super = Father.prototype.function1;
c = new Child();
c.function1();
// I am the Grandfather
// I am the Father
// I am the Child
And so the question becomes, how to set the super property on to each function in some automatic way?
One such way to do this is shown below, it has the benefit that functions added to the prototype after objects are instantiated still receive the benefit of being able to call superFunc, whereas an approach that sets a super property at class extension time would not set such a property if functions are added to the prototype later.
The downsides of this approach are that it only works in single threaded environment and that it requires functionality inherited from a common base class. It is not threadsafe since some state is held in a what is effectively a static variable of the function. This is fine for my purposes since browsers still have single threaded JavaScript. The requirement that all classes inherit from some base class containing this method isn't a huge blocker (especially if you do a "bad thing" and insert this into Object's prototype).
Grandfather.prototype.superFunc = function f(funcName){
currentPrototype = Object.getPrototypeOf(f.startingPrototype || Object.getPrototypeOf(this));
f.startingPrototype = currentPrototype;
return currentPrototype[funcName]();
}
Child.prototype.function2 = function(){this.superFunc('function2'); console.log("Still in the Child");};
Father.prototype.function2 = function(){this.superFunc('function2'); console.log("Still in the Father");};
GrandFather.prototype.function2 = function(){console.log("Still in the Grandfather");};
c = new Child();
c.function2();
// Still in the Grandfather
// Still in the Father
// Still in the Child
Question 1
new Bar().constructor.prototype should equal Object.getPrototypeOf(new Bar()), provided you haven't overrided Bar.prototype.constructor or Bar.prototype, or return a different object in the Bar constructor. Here's an example:
function Bar() {}
var foo = new Bar();
foo.constructor.prototype === Object.getPrototypeOf(foo); // true
function Bar2() {}
var foo2 = new Bar2();
Bar2.prototype = {};
foo2.constructor.prototype === Object.getPrototypeOf(foo2); // false
function Bar3() {}
var foo3 = new Bar3();
Bar3.prototype.constructor = function NotBar3() {};
foo3.constructor.prototype === Object.getPrototypeOf(foo3); // false
Question 2
If you're looking to get the actual prototype of an object, use Object.getPrototypeOf, as that's unaffected by any of the changes shown above.
Question 3
No, you will not be able to access Foo from new Bar(). In your example, new Bar() would not inherit from Foo.prototype and as a result, there's no way to access Foo unless you make it inherit from Foo.prototype or assign Foo to a property of new Bar() or Bar.prototype.
Question 4/5
No, that's not what they're referring to. ES6 will introduce a separate class contruct, where super takes on a special meaning (similar to how super works in other languages with classes). Here's an example of how classes work in ES6:
class Foo {
constructor() {
this.one = 1;
}
func1() {
return this.one;
}
}
class Bar extends Foo {
func1() {
return this.one + super();
}
}
When you use super in the way you do it'll break when inheritance is more than 2 levels.
Assuming you'd use it the following way:
//changed super to this.super since super is not shown to exist in global scope
bar.prototype.func1(){ return this.one + this.super('func1'); }
See the following example:
function GrandFather(){
this.i = 0;
};
GrandFather.prototype.test = function(){
console.log('test in GrandFather');
};
function Father(){
GrandFather.call(this);
};
Father.prototype = Object.create(GrandFather.prototype);
Father.prototype.constructor = Father;
Father.prototype.super = GrandFather.prototype;
Father.prototype.test = function(){
console.log('test in Father');
//prevent too many recursions
this.i++;
if(this.i>5){
return;
}
this.super.test.call(this);//because test in child was called
// with Child instance as invoking object this will be Child
// and this.super will be Father.prototype
};
function Child(){
Father.call(this);
}
Child.prototype = Object.create(Father.prototype);
Child.prototype.constructor = Child;
Child.prototype.super = Father.prototype;
Child.prototype.test = function(){
console.log('test in Child');
this.super.test.call(this);//because invoking object is Child
//this.super in Father is Child
};
var c = new Child();
c.test();
It's also common practice to start a constructor function with a capital so it's better to use Foo and Bar for constructor function names.
If you want to go through all the trouble of simulating super in JavaScript then the following way would be slightly more robust: http://ejohn.org/blog/simple-javascript-inheritance/
I have a DTO type that really is a map of key/value pairs. I would typically use an object literal for this for terseness, but this preculdes the resulting object having the [[prototype]].constructor set to a meaningful value.
e.g.
function MyDto() {
var o = {
myProperty: null
};
return o; //[[prototype]].constructor is meaningless
}
Is there a way to do something similar, but have the [[prototype]].constructor property set to the more meaningful MyDto (instead of Object)?
Not very much sure what you want to do. But this may help..
function MyDto() {
var o = {
myProperty: null
};
Object.setPrototypeOf(o,MyDto.prototype);
return o;
}
a = MyDto();
console.log(a);
To make obj instanceof Foo work, the prototype of obj has to point to the value of the prototype property of the function (Foo). A couple of ways have already been suggested, here is another one:
Call the function with new and return this (implicitly or explicitly). If you want to be able to call the function without new (not really clear from your question), check inside the function whether it was called with new or not:
function MyDto() {
if (!(this instanceof MyDto)) {
return new MyDto();
}
Object.assign(this, {myProperty: null});
}
Note: The constructor property has no meaning internally, only to the developers using your code.
I don't think I understand your question, but you might try this:
o.constructor = MyDto;
This will set o's constructor as MyDto, but will have no effect when doing o instanceof MyDto.
If this is what you want, my suggest is that you instantiate MyDto instead:
function MyDto() {
this.myProperty = null;
}
var o = new MyDto();
console.log(o instanceof MyDto); // true
console.log(o.constructor === MyDto); // true
EDIT: If you return within a function, then you will lost the reference to your new instance. In your case MyDto works as a factory to instances of Object that have an own property called myPropert.
EDIT 2: I still prefer the other way, but using Object.create also works:
function MyDto() {
return Object.create(MyDto.prototype, {
myProperty: {
writable: true,
configurable: true,
value: null
}
});
}
new MyDto() instanceof MyDto; // true
new MyDto().constructor === MyDto; // true
You're approaching this from the wrong direction. If you want the result of new Class() be a real instanceof Class, just extend the default instance object, instead of creating and returning a new one.
function Class () {
_.extend(this, {
property: 'value',
// ...
});
}
(The above code uses Lo-Dash function _.extend() to keep your code short & sweet. Similar implementations may be found in almost all utility libraries or bigger JavaScript frameworks).
Is there any difference when I explicitly return from a function vs. implicitly return?
Here's the code that puzzles me ATM:
function ReturnConstructor(arg){
// Private variable.
var privateVar = "can't touch this, return ctor.";
// This is what is being exposed to the outside as the return object
return {
print: function() {
console.log("ReturnConstructor: arg was: %s", arg);
}
};
}
function NormalConstructor(arg){
// Private variable.
var privateVar = "can't touch this, normal ctor";
// This is what is being exposed to the outside as "public"
this.print = function() {
console.log("NormalConstructor: arg was: %s", arg);
};
}
var ret = new ReturnConstructor("calling return");
var nor = new NormalConstructor("calling normal");
Both objects ('ret' & 'nor') seem the same to me, and I'm wondering if it's only personal preference of whomever wrote whichever article I've read so far, or if there's any hidden traps there.
When you use new, there's an implicit value. When you don't use new, there isn't.
When a function called with new returns an object, then that's the value of the new expression. When it returns something else, that return value is ignored and the object implicitly constructed is the value of the expression.
So, yes, there can be a difference.
Implicit return only happens for single statement arrow functions, except When arrow function is declared with {}, even if it’s a single statement, implicit return does not happen:
link