On understanding correlation of bind and inheritance - javascript

Today I was reading the MDN documentation on Function.prototype.bind(). Under the section Bound functions used as constructors there is an example that I cannot quite understand.
I ran the following piece of code both in Node.js (v.4.4.5) and Google Chrome ( v58.0.3029.81)
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + ',' + this.y;
};
var p = new Point(1, 2);
p.toString(); // '1,2'
var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 1/*x*/);
var axisPoint = new YAxisPoint(5);
console.log(axisPoint.toString()); // '1,5'
console.log(axisPoint instanceof Point); // true
console.log(axisPoint instanceof YAxisPoint); // true
console.log(new Point(17, 42) instanceof YAxisPoint); // true
I can clearly see why axisPoint is an instance of both Point and YAxisPoint. But how in the world can new Point(17,42) be an instance of YAxisPoint?

But how in the world can new Point(17,42) be an instance of YAxisPoint?
Because instanceof works special with bound functions (those created from .bind() calls). Usually it would check whether the object inherits from the constructors .prototype, but bound functions don't have a .prototype. Instead, when you use instanceof on a bound function, it checks whether the object is an instance of the target function (that bind() was called upon). So
… instanceof YAxisPoint
is exactly equivalent to
… instanceof Point
You can check this in the specs (ES5, ES6).

Related

Why can't specify the constructor's prototype inside that constructor? JavaScript

function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype = {
x : 0,
y : 0
};
};
console.log(Graph.prototype);
var g = new Graph();
console.log(g.x);
console.log(g.y);
Inside the constructor Graph, which is a toy exmaple, I try to assign an object to its prototype.
function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype = {
x : 0,
y : 0
};
}
When I look at Graph.prototype using:
console.log(Graph.prototype);
I find that Graph.prototype is still the default prototype. I didn't make any change to it.
Why can't I specify the constructor's prototype inside that constructor? Can someone tell me the real reason behind it?
Thanks a lot!
Background
I assume that you're calling console.log(Graph.prototype); before running the Graph constructor.
Remember that JavaScript's formal type-system for object values uses mutable prototypes as a way of implementing object-inheritance, and those prototype object references can also be swapped-out entirely and redefined at runtime even after object values using that prototype already exist, which makes it much harder to reason about a JavaScript program's type-safety (for this reason TypeScript still cannot represent every valid JavaScript program, as of early 2020).
For example, consider:
// 1. Declare `Foo` constructor.
function Foo() {
this.bar = 123;
}
// 2. Extend the prototype:
Foo.prototype.baz = 456;
// 3. Create a new instance of Foo:
const foo1 = new Foo();
console.log( "foo1.baz == %o", foo1.baz ); // "456"
// 4. Change the prototype:
delete Foo.prototype.baz;
Foo.prototype.qux = "abc";
// 5. Create a second new instance of Foo:
const foo2 = new Foo();
console.log( "foo2.qux == %o", foo2.qux ); // "abc"
// 6. Because Foo's prototype is changed, `foo1.baz` is no-longer valid:
console.log( "foo1.baz == %o", foo1.baz ); // "undefined"
So the formal-type of foo1 (i.e. the set of properties of foo1) is entirely up-in-the-air - which is why it's a good idea to never redefine a prototype in JavaScript before any objects using that prototype are created.
The Answer
With that background out of the way...
"Why can't I specify the constructor's prototype inside that constructor? Can someone tell me the real reason behind it?"
You technically can, it just won't work the way you want it to:
The Foo.prototype (or Graph.prototype in your case) would only be set when and only when the first new Foo() call is made.
The Foo.prototype object would be reupdated on every Constructor call.
This would be a bad thing: I'm unsure if JavaScript engines would treat the same lexical object-literal as the same instance of an object or would create a new object from the object-literal - either way, it makes your program much harder to reason about.
If you want to succinctly define the prototype in the same "place" in your project's codebase then just define it immediately after the Constructor function definition, this also means that Graph.prototype will be updated as-expected without needing to actually create any new Graph objects.
So this is what you're currently doing:
function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype = {
x: 0,
y: 0
};
}
console.log( Graph.prototype ); // "{}" or "{constructor: f}"
But if you update the prototype immediately after defining the constructor it will work as-intended:
function Graph() {
this.vertices = [];
this.edges = [];
}
Graph.prototype = {
x: 0,
y: 0
};
console.log( Graph.prototype ); // "{x: 0, y: 0}"
Regarding this.constructor, this.constructor.prototype, and Graph.prototype.
Inside a constructor function:
this refers to the newly created object-instance.
this.constructor refers to the constructor function (function TypeName()).
this.prototype is undefined as prototype is only defined on (constructor) functions.
TypeName.prototype:
By default this is an object with this definition:
{ constructor: f } // where `f` is `function TypeName`.
If TypeName.prototype is redefined anywhere (even inside the constructor) then it will be equal to that redefinition but only after the code that performs the redefinition actually executes (so not when the JavaScript code is merely loaded and parsed).
this.constructor.prototype also refers to TypeName.prototype (i.e. TypeName.prototype === this.constructor.prototype):
BTW, it breaks JSON:
Note that you probably don't want to do this anyway because object properties inherited from a prototype are not considered "own" properties and so are excluded from JSON.stringify output, e.g.:
var g = new Graph();
var json = JSON.stringify( g );
console.log( json ); // "{"vertices":[],"edges":[]}"
Fortunately there's a workaround you can use for serialization (but implementing a prototype-aware JSON.parse is an exercise for the reader):
function JSON_stringify_for_prototypes( obj ) {
const flat = Object.create( obj );
for( const key in flat ) { // `for( x in y )` includes non-"own" properties.
flat[key] = flat[key]; // This looks silly, but this actually causes each property to be marked as "own" in `flat`.
}
return JSON.stringify( flat );
}
You need to update Graph.prototype instead of replacing it with a new object, and you need to console.log(Graph.prototype) after calling the constructor rather than before:
function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype.x = 0;
Graph.prototype.y = 0;
};
var g = new Graph();
console.log(Graph.prototype);
console.log(g.x);
console.log(g.y);
But as others have said, because the prototype is shared by all instances of Graph, you probably don't actually want to do this. Without anymore context, it just seems like you want two more properties x and y for each instance:
function Graph() {
this.vertices = [];
this.edges = [];
this.x = 0;
this.y = 0;
};
var g = new Graph();
console.log(g);
console.log(g.x);
console.log(g.y);
It seems to me like you are really just trying to create a method that sets static properties. Here's how you would go about attempting to replicate this in JavaScript:
function Graph(){
this.vertices = []; this.edges = [];
this.staticXY = (x = 0, y = 0)=>{
const p = this.constructor.prototype;
p.x = x; p.y = y;
return this;
}
if(this.x === undefined && this.y === undefined)this.staticXY();
}
const graph1 = new Graph;
graph1.staticXY(2, 8); graph1.x = 3; graph1.y = 1;
console.log(graph1.x, graph1.y); // static remains anyways
const graph2 = new Graph;
console.log(graph2.x, graph2.y);

What is the purpose of setting prototype.constructor [duplicate]

This question already has answers here:
Why is it necessary to set the prototype constructor?
(14 answers)
Closed 3 years ago.
I haven't been able to find a clear explanation of this. This is a straightforward example that I found on MDN. The only thing I don't understand is why the constructor is set. Can someone explain why this is needed? Is it for inheritance and so that the correct prototype chain is reffered to?
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?', rect instanceof Rectangle);// true
console.log('Is rect an instance of Shape?', rect instanceof Shape);// true
rect.move(1, 1); // Outputs, 'Shape moved.'
Whenever you create a function, say function foo(){}, the JS engine also creates an anonymous object and connects the two in a similar fashion.
foo.prototype = {};
foo.prototype.constructor = foo;
The property names "constructor" and "prototype" are so only because of semantics. It is possible that the standard names were:
foo.ping = {};
foo.ping.pong = foo;
And "the purpose of setting prototype.constructor" is simple - to be able to use the constructor function of that class.
If you don't need to call the constructor function, you can omit the property entirely.
To know more about the topic I recommend reading
http://www.javascripttutorial.net/javascript-prototype/
and
http://www.javascripttutorial.net/javascript-prototypal-inheritance/

inherit is not Defined

I'm studying a javascript book, and i have this example
var p = {
// x and y are regular read-write data properties. x: 1.0,
y: 1.0,
// r is a read-write accessor property with getter and setter. // Don't forget to put a comma after accessor methods.
get r() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}, set r(newvalue) {
var oldvalue = Math.sqrt(this.x * this.x + this.y * this.y);
var ratio = newvalue / oldvalue;
this.x *= ratio;
this.y *= ratio;
},
// theta is a read-only accessor property with getter only.
get theta() {
return Math.atan2(this.y, this.x);
} };
var q = inherit(p); // Create a new object that inherits getters and setters
q.x = 0; q.y = 0; // Create q's own data properties
console.log(q.r); // And use the inherited accessor properties console.log(q.theta);
But I have this error Uncaught ReferenceError: inherit is not defined
Looking up this code on internet suggests you might be reading O'Reilly's 'JavaScript: the Definitive Guide'. If so, the code for inherit() is given in example 6-1:
See here: https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-6/creating-a-new-object-that
To add to Mchl's answer (I had the same problem, but couldn't follow the link in his answer), the following is the code for the inherit function as in Javascript The Definitive Guide's example 6.1:
// inherit() returns a newly created object that inherits properties from the
// prototype object p. It uses the ECMAScript 5 function Object.create() if
// it is defined, and otherwise falls back to an older technique.
function inherit(p) {
if (p == null) throw TypeError(); // p must be a non-null object
if (Object.create) // If Object.create() is defined...
return Object.create(p); // then just use it.
var t = typeof p; // Otherwise do some more type checking
if (t !== "object" && t !== "function") throw TypeError();
function f() {}; // Define a dummy constructor function.
f.prototype = p; // Set its prototype property to p.
return new f(); // Use f() to create an "heir" of p.
Straight to the point, you've missed the code on page 119 (see references at the end), on which inherit function is defined. It seems that you are treating 'inherit' function as a "native function" and it is not. So as has been said, you need to "construct" that function before inicialization - so you don't get Uncaught ReferenceError: inherit is not defined.
That error is telling you the clue: "inherit is not defined", so the first step is to look up where is this function defined. If it is not defined yet and you want to use it, you just need to define it as suggested bellow or with the specific instructions you need to play. If you're learning, it is very important to understand what erros are teeling you.
So, before calling 'inherit' function add the following code as suggested by the book:
// inherit() returns a newly created object that inherits properties from the
// prototype object p. It uses the ECMAScript 5 function Object.create() if
// it is defined, and otherwise falls back to an older technique.
function inherit(p) {
if (p == null) throw TypeError(); // p must be a non-null object
if (Object.create) // If Object.create() is defined...
return Object.create(p); // then just use it.
var t = typeof p; // Otherwise do some more type checking
if (t !== "object" && t !== "function") throw TypeError();
function f() {}; // Define a dummy constructor function.
f.prototype = p; // Set its prototype property to p.
return new f(); // Use f() to create an "heir" of p.
}
Example 6-1. Creating a new object that inherits from a prototype. JavaScript: The Definitive Guide, by David Flanagan (O'Reilly). Copyright 2011 David Flanagan, 978-0-596-80552-4. 6th Edition.

Clarifying javascript prototype nomenclature and mechanism

Recognizing that JavaScript doesn't have the concept of class per se, and that the "type" of all objects is "object", I'm trying to get my head around just what a "prototype" consists of and, in particular, how its "name" is associated with it. For example, in the following:
function Foo(){};
console.log(Foo.prototype); // => "Foo {}"
How does console.log know to output Foo before the braces and what is that name referring to?
(Note: I'm aware that in the above, I'm referring to the prototype property of functions and not the prototype per se (i.e. not the thing accessible by __proto__), but the same question applies to the actual prototype objects. I just used the prototype property to simplify my example.)
Update: Based on the comment thread, this question is really focused on what Chrome is doing and, in particular, rationalizing its behavior in the following:
function Foo(){};
Foo.prototype.constructor = function Bar(){};
f = new Foo();
console.log(f); // => Foo{} (remembering that f created by Foo, ignoring constructor)
console.log(Foo.prototype) // => Bar{} (reporting constructor value)
See https://gist.github.com/getify/5793213 for more discussion.
JavaScript has a very twisted form of prototypal inheritance. I like to call it the constructor pattern of prototypal inheritance. There is another pattern of prototypal inheritance as well - the prototypal pattern of prototypal inheritance. I'll explain the latter first.
In JavaScript objects inherit from objects. There's no need for classes. This is a good thing. It makes life easier. For example say we have a class for lines:
class Line {
int x1, y1, x2, y2;
public:
Line(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
int length() {
int dx = x2 - x1;
int dy = y2 - y1;
return sqrt(dx * dx + dy * dy);
}
}
Yes, this is C++. Now that we created a class we may now create objects:
Line line1(0, 0, 0, 100);
Line line2(0, 100, 100, 100);
Line line3(100, 100, 100, 0);
Line line4(100, 0, 0, 0);
These four lines form a square.
JavaScript doesn't have any classes. It has prototypal inheritance. If you wanted to do the same thing using the prototypal pattern you would do this:
var line = {
create: function (x1, y1, x2, y2) {
var line = Object.create(this);
line.x1 = x1;
line.y1 = y1;
line.x2 = x2;
line.y2 = y2;
return line;
},
length: function () {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
}
};
Then you create instances of the object line as follows:
var line1 = line.create(0, 0, 0, 100);
var line2 = line.create(0, 100, 100, 100);
var line3 = line.create(100, 100, 100, 0);
var line4 = line.create(100, 0, 0, 0);
That's all there is to it. No confusing constructor functions with prototype properties. The only function needed for inheritance is Object.create. This function takes an object (the prototype) and returns another object which inherits from the prototype.
Unfortunately, unlike Lua, JavaScript endorses the constructor pattern of prototypal inheritance which makes it more difficult to understand prototypal inheritance. The constructor pattern is the inverse of the prototypal pattern.
In the prototypal pattern objects are given the most importance. Hence it's easy to see that objects inherit from other objects.
In the constructor pattern functions are given the most importance. Hence people tend to think that constructors inherit from other constructors. This is wrong.
The above program would look like this when written using the constructor pattern:
function Line(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
Line.prototype.length = function () {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
};
You may now create instances of Line.prototype as follows:
var line1 = new Line(0, 0, 0, 100);
var line2 = new Line(0, 100, 100, 100);
var line3 = new Line(100, 100, 100, 0);
var line4 = new Line(100, 0, 0, 0);
Notice the similarity between the constructor pattern and the prototypal pattern?
In the prototypal pattern we simply create an object which has a create method. In the constructor pattern we create a function and JavaScript automatically creates a prototype object for us.
In the prototypal pattern we have two methods - create and length. In the constructor pattern too we have two methods - constructor and length.
The constructor pattern is the inverse of the prototypal pattern because when you create a function JavaScript automatically creates a prototype object for the function. The prototype object has a property called constructor which points back to the function itself:
As Eric said, the reason console.log knows to output Foo is because when you pass Foo.prototype to console.log:
It finds Foo.prototype.constructor which is Foo itself.
Every named function in JavaScript has a property called name.
Hence Foo.name is "Foo". So it finds the string "Foo" on Foo.prototype.constructor.name.
Edit: Alright, I understand that you have a problem with the redefining the prototype.constructor property in JavaScript. To understand the problem let's first understand how the new operator works.
First, I want you to take a good look at the diagram I showed you above.
In the above diagram we have a constructor function, a prototype object and an instance.
When we create an instance using the new keyword before a constructor JS creates a new object.
The internal [[proto]] property of this new object is set to point to whatever constructor.prototype points to at the time of object creation.
What does this imply? Consider the following program:
function Foo() {}
function Bar() {}
var foo = new Foo;
Foo.prototype = Bar.prototype;
var bar = new Foo;
alert(foo.constructor.name); // Foo
alert(bar.constructor.name); // Bar
See the output here: http://jsfiddle.net/z6b8w/
The instance foo inherits from Foo.prototype.
Hence foo.constructor.name displays "Foo".
Then we set Foo.prototype to Bar.prototype.
Hence bar inherits from Bar.prototype although it was created by new Foo.
Thus bar.constructor.name is "Bar".
In the JS fiddle you provided you created a function Foo and then set Foo.prototype.constructor to function Bar() {}:
function Foo() {}
Foo.prototype.constructor = function Bar() {};
var f = new Foo;
console.log(f.hasOwnProperty("constructor"));
console.log(f.constructor);
console.log(f);
Because you modified a property of Foo.prototype every instance of Foo.prototype will reflect this change. Hence f.constructor is function Bar() {}. Thus f.constructor.name is "Bar", not "Foo".
See it for yourself - f.constructor.name is "Bar".
Chrome is known to do weird things like that. What's important to understand is that Chrome is a debugging utility and console.log is primarily used for debugging purposes.
Hence when you create a new instance Chrome probably records the original constructor in an internal property which is accessed by console.log. Thus it displays Foo, not Bar.
This is not actual JavaScript behavior. According to the specification when you overwrite the prototype.constructor property there's no link between the instance and the original constructor.
Other JavaScript implementations (like the Opera console, node.js and RingoJS) do the right thing and display Bar. Hence Chrome's behavior is non-standard and browser-specific, so don't panic.
What's important to understand is that even though Chrome displays Foo instead of Bar the constructor property of the object is still function Bar() {} as with other implementations:
The constructor property (which refers to a function originally used as a generator of the corresponding objects) is used to give a name to a prototype object in the console log. Consider the following:
function Foo() {
this.x = 1;
}
console.log(Foo.prototype); // Foo {}
Foo.prototype.constructor = function Bar() {
this.y = 2
}
console.log(Foo.prototype); // Bar {}
var f = new Foo();
console.log(f.constructor); // function Bar() { this.y = 2}
console.log(f.x); // 1
console.log(f.y); // undefined
console.log(f); // Foo {x:1}
Here we've switched constructor to another function, giving a new name to prototype object. Note that the same function is returned when constructor property is queried directly from an object, created with Foo() function (as we go up the inheritance chain).
Still, it doesn't mean that another function (Bar()) was actually used to create the corresponding objects; it's still Foo(), and you can see it both by querying properties -- and f directly. Basically, objects remember the function that was used to create them, even if constructor property of prototype was "redirected".
function Foo(){};
Working down the chain:
console.log(Foo.prototype);
console.log(Foo.prototype.constructor);
console.log(Foo.prototype.constructor.name);
Took a bit of digging online but I found this article that really illustrates how prototypes and other key core javascript functionality works:
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
I particularly like the diagram on how the prototype chain looks like.

How do you do inheritance in JavaScript without sharing the same instance of the super class between all instances of the sub class?

I noticed that every tutorial on how to do JavaScript inheritance does this:
SubClass.prototype = new SuperClass();
But this will create a single instance of the super class and share it among all the instances of the sub class.
The problem is that I would like to pass arguments to the super class constructor which originate from arguments passed to the sub class.
In Java this would be done like this:
class SubClass extends SuperClass {
public SubClass(String s) {
super(s);
}
}
I tried doing something like this:
function SubClass(args) {
this.constructor.prototype = new SuperClass(args);
}
But this will not work. So is there a way to do this in JavaScript?
A common pattern is the following:
A temporary constructor is created, which inherits from the parent constructor's prototype. The child constructor's prototype is then set to an instance of the temporary constructor.
function inherits(Child, Parent) {
var Tmp = function() {};
Tmp.prototype = Parent.prototype;
Child.prototype = new Tmp();
Child.prototype.constructor = Child;
}
Inside the child constructor you then have to call the parent's constructor:
function Child(a, b, c) {
Parent.call(this, a, b);
}
inherits(Child, Parent);
// add prototype properties here
Inside this function call, this will refer to the new object which gets created when you call new Child(), hence, whatever initialization is performed inside Parent, it is applied to the new object we pass.
This is how I have always done it.
// Parent object
function Thing(options)
{
//do stuff
}
Thing.prototype.someMethod = function(){
// some stuff
console.log('hello');
}
// child object which inherits from the parent
function OtherThing(options)
{
Thing.call(this, options);
// do stuff for otherthing
}
OtherThing.prototype = new Thing();
OtherThing.prototype.someMethod = function(){
// call things original function
Thing.prototype.someMethod.call(this);
// now do anything different
console.log('other thing says hi');
}
var testObj = new OtherThing();
testObj.someMethod();
Live Demo
But this will create a single instance of the super class and share it among all the instances of the sub class.
Yes, that's how inheritance works in JavaScript.
So is there a way to do this in JavaScript?
Not without horribly subverting/twising/misusing the existing paradigm. I recommend taking a different approach to implementing whatever you're aiming for.
Another way would to get rid of the class-like (but not class-based) inheritance that involves the use of constructor.
Use Object.create along with Object.defineProperties. This is based on the native prototype-based inheritance system that JavaScript follows internally.
You can find more information on it on MDN and ECMAScript specs.
These methods however works only in ECMAScript 5 compliant browsers. This excludes IE8 and older. Fortunately, IE9 supports it and as does the rest of the major browsers. In long run, I think this would be the way to go.
With Object.create() it is now possible to simplify "temporary constructor" pattern. Here's an example right from the doc - Classical inheritance with Object.create():
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
//If you don't set Rectangle.prototype.constructor to Rectangle,
//it will take the prototype.constructor of Shape (parent).
//To avoid that, we set the prototype.constructor to Rectangle (child).
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?', rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?', rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'

Categories

Resources