Javascript inheritance - where the members creating - javascript

I am new in JS programming and trying to understand prototype-based inheritance. below is my test code and I have a question about line method 'parseParameters'.
As I know, when I am instantiating class Point and wring following:
var p = new Point ({x: 1, y: 1});
alert(p.x);
member 'x' firstly searched in the Point class, then in its prototype (Shape). Is it right?
And question itself: where will be created members 'x' and 'y' - in the Point class or in Shape (prototype)?
One remark: should I actually thinking of it? Maybe it is negligible question and there is no matter where the member created?
var Shape = function () {}
Shape.prototype = {
getParameter: function (params, name) {
return params !== null && params !== undefined
? params[name]
: 0;
},
parseParameters: function(params, names) {
if (params === null || params === undefined) {
return;
}
for(var i = 0; i < names.length; i++) {
this[names[i]] = params[names[i]];
}
}
}
var Point = function (params) {
this.parseParameters(params, ['x', 'y'])
}
Point.prototype = new Shape;

p.x
member 'x' firstly searched in the Point class, then in its prototype (Shape). Is it right?
Apart from that there are no "classes" in JavaScript, yes. The property name will first be searched on the p object itself, then in the object it did inherit from (there's an internal link).
Where will be created members 'x' and 'y' - in the Point class or in Shape (prototype)?
Depends on how you create them :-)
In a simple assignment, like p.x = 1; (which also happens in parseParameters' this[names[i]] = params[names[i]], as this === p) the property will be created on the p object.
should I actually thinking of it? Maybe there is no matter where the member created?
No, that really matters. If you would create them on the prototype object, all objects which inherit from that one would share the same values. While this is useful for the functions, it would be catastrophic for data properties like x and y which should differ from instance to instance (see also Why are my JavaScript object properties being overwritten by other instances? for an example).

Related

How were "classes" extended prior to class syntax and Object.create?

I have not been able to find an answer to this. Objects in JavaScript have an inheritance chain; the chain of any function is Function => Object, the chain of an instance of TypeError is TypeError => Error => Object, and the chain of TypeError is, oddly, Function => Function => Object.
I had looked up how to make a constructed object inherit properties from another function in addition to its direct constructor, expecting the resulting inheritance chain to be object => constructor => second function and for this to be how one constructor would extend another. The solution that I had found was to call <second function>.<call or apply>(this[, optional arguments...]) inside the body of the constructor, but object instanceof <second function> ended up returning false.
Further research revealed mostly answers that use class syntax or Object.create, but those are new and one "class" extending another in JavaScript has been around since the creation of the language, so there's some other way that's used to do this. This information is something that should be mentioned right alongside basic explanations of JavaScript constructors yet it is not. What is the primary method of extending a "class" (not actual class syntax) resulting in deeper inheritance chains?
Example result:
// Square is the subclass
// Rectangle is the superclass
var rectangle = new Rectangle(1, 1);
var square = new Square(1);
rectangle instanceof Rectangle; // true
rectangle instanceof Square; // false
square instanceof Rectangle; // true
square instanceof Square; // true
Square instanceof Rectangle; // true
False solution:
function F () {
this.value = 0;
}
function G () {
F.apply(this);
}
var f = new F();
var g = new G();
// g gets the same properties from F that f gets.
"value" in f; // true
"value" in g; // true
// But neither g nor G are instances of F.
g instanceof G; // true
g instanceof F; // false
G instanceof F; // false
one "class" extending another in JavaScript has been around since the creation of the language
No, it hasn't. JavaScript was never (and still is not) a class-based language. The only tools you had were .prototype and new.
How were "classes" extended prior to Object.create?
Using the same approach, basically. The key to setup the prototype chain is
Subclass.prototype = Object.create(Superclass.prototype);
and without Object.create, people just created that object using
Subclass.prototype = new Superclass;
See the answers from 2010 in How to inherit from a class in javascript? for examples.
Yes, this is a bad idea, but it proliferated. Better solutions that would not execute the superclass constructor were devised, and this is how Object.create came into existence, popularised by Douglas Crockford (see also What is happening in Crockford's object creation technique?).
So I thought that I'd include a couple of examples of "extensions" in javascript from my old project just in case that is what was being looked for.
Here's an example of "extending" all objects by modifying the valueOf function. First it copies the built-in .valueOf definition to a new function/property -> .originalvalueOf, then adds my custom .valueOf over the built-in one. I did this so that JS numbers would throw an error for things like NaN or division by zero. As you can see it is done by modifying the Object.prototype, and my new .valueOf actually calls the built-in version through .originalvalueOf.
/* **** Object extensions ****
Check for NaN and DivZeros
(adapted from: https://stackoverflow.com/a/20535480/109122)
*/
Object.prototype.originalValueOf = Object.prototype.valueOf;
Object.prototype.valueOf = function() {
if (typeof this == 'number') {
if (!isFinite(this)) {throw new Error('Number is NaN or not Finite! (RBY)');}
}
return this.originalValueOf();
}
// var a = 1 + 2; // -> works
// console.log(a); // -> 3
// var b = {};
// var c = b + 2; // -> will throw an Error
Here's an example of a constructor for my "class" called Pe2dSpatialState. Note that I've added all of the object's fields here:
// Constructor for Dynamic Spatial State
//( physical properties that change according to Newtonian Mechanics )
var Pe2dSpatialState = function(point2dPosition,
point2dVelocity,
point2dAcceleration,
interval,
rotationDegrees,
rotationDegreesPerSec) {
this.position = point2dPosition; // position of this state
this.velocity = point2dVelocity; // velocity of this state
this.acceleration = point2dAcceleration; // acceleration to be applied
this.interval = interval; // time to the next state
this.rotationD = (rotationDegrees ? rotationDegrees : 0);
// degrees rotated (because SVG uses degrees)
this.rotationDPerSec = (rotationDegreesPerSec ? rotationDegreesPerSec : 0);
// degrees per sec (because SVG uses degrees)
}
I added functions to Pe2dSpatialState objects through the prototype:
Pe2dSpatialState.prototype.clone = function() {
var tmp = new Pe2dSpatialState( this.position.clone(),
this.velocity.clone(),
(this.acceleration ? this.acceleration.clone() : undefined),
this.interval,
this.rotationD,
this.rotationDPerSec);
return tmp;
}
Then I added get/set type "properties" using Object.defineProperty:
Object.defineProperty(Pe2dSpatialState.prototype, "rotationR", {
get() {return this.rotationD * Math.PI * 2 / 360;},
set(v) {this.rotationD = v * 360 / (Math.PI * 2);}
});
Object.defineProperty(Pe2dSpatialState.prototype, "rotations", {
get() {return this.rotationD / 360;},
set(v) {this.rotationD = v * 360;}
});
Checking my listings, I always defined my "classes" in this order: Constructor function with fields, then adding functions/methods to the prototype and finally adding properties with Object.defineProperty. And only after that would I use it anywhere.
I cannot remember exactly why I did everything in these three different ways, except that I went through a lot of different attempts and iterations and this is what I finally landed on as working for me. (This is all probably much easier under ES6).
I also found a very sophisticated function that would list out the object-function-prototype trees for any object that was hugely helpful to me in figuring out what was really happening and what would work. I haven't included it because it's long, but if you want to see it then I will post it here.
(I cannot guarantee that this is the best way to do this nor even that there aren't any mistakes in this code. In fact, looking at it now, I suspect that some of my fields should have been properties instead...)

Why is obj.constructor.prototype not always equal to obj.__proto__?

function Product(name, price) {
this.name = name;
this.price = price;
}
const p1 = new Product('Pen', 20);
const p2 = Object.create(p1);
console.log(p1.constructor.prototype === p1.__proto__); // true
console.log(p2.constructor.prototype === p2.__proto__); // false
My understanding was these two are always equal (as in the first console.log statement).
But, while doing some tweaks I found this surprising result (second console.log statement).
Can someone please clear up my understanding about the relationship between prototype and __proto__. Thanks in advance!
This only works for instances created using new from constructors that follow the standard prototype pattern. These objects will inherit from the constructors .prototype object, which has a .constructor property pointing back to the constructor. This means when accessing the inherited .constructor, we can find the prototype object that we're inheriting on it.
However, it doesn't work for arbitrary objects that have their own .constructor property (e.g. {constructor: null}) or for objects that don't inherit directly from a constructor's prototype object, such as your p2.
To clarify what's happening in your code without using new:
const Product = Object.create(Function.prototype);
Product.prototype = Object.create(Object.prototype);
Product.prototype.constructor = Product;
const p1 = Object.create(Product.prototype);
p1.name = "Pen";
p1.price = 20;
console.assert(Product.prototype == Object.getPrototypeOf(p1));
console.assert(!p1.hasOwnProperty("constructor") && p1.constructor == Product);
console.assert(p1.constructor.prototype == Product.prototype);
console.assert(p1.constructor.prototype == Object.getPrototypeOf(p1));
const p2 = Object.create(p1);
console.assert(p1 == Object.getPrototypeOf(p2));
console.assert(!p2.hasOwnProperty("constructor") && p2.constructor == p1.constructor);

How to distinguish HTMLElement from its constructor.prototype?

I'm working on my LIPS project (Scheme-based Lisp in JavaScript) and I want to add a way to add string representation to any object. The code look like this:
NOTE: you can ignore scheme code. At the end there is simplified use case, this is only for context and why I need this.
(add-repr! HTMLElement (lambda (x)
(let ((tag (--> x.tagName (toLowerCase))))
(string-append "<" tag ">" x.innerHTML "</" tag ">"))))
it works fine when I evaluate:
(document.createElement "span")
;; or
(document.querySelector ".klas")
but it have problem while evaluating the code:
(let ((div (document.createElement "div")))
(. div 'constructor 'prototype))
and my interpreter thinks that this is instance of HTMLElement, the JavaScript code look like this:
var repr = new Map();
// values in map are lisp lambdas that are JavaScript functions
// keys are constructor functions
...
var fn;
if (repr.has(constructor)) {
fn = repr.get(constructor);
} else {
repr.forEach(function(value, key) {
if (obj instanceof key) {
fn = value;
}
});
}
if (fn) {
if (typeof fn === 'function') {
return fn(obj, quote);
} else {
throw new Error('toString: Invalid repr value');
}
}
I check if obj is instanceof given type (HTMLElement from add-repr!) and it return true for prototype.
And throw exception that x.tagName is not defined because it's not instance but prototype.
To simplify the code (I've added scheme code for context) this is the code:
document.createElement('div').constructor.prototype instanceof HTMLElement;
it return true because prototype was Object.create(HTMLElement). Is there a way to detect if the value is in fact a prototype of any value without have that original value.
var x = document.createElement('div').constructor.prototype;
// context is lost I can't access original value
// check if x is instanceof HTMLElement, but also it's constructed value and not prototype of any value.
and if you think that you can check if there is constructor value, this circular object:
document.createElement('div').constructor.prototype.constructor.prototype.constructor
to sum this question up I want to detect if value is any of but not both:
document.createElement('div')
document.createElement('div').constructor.prototype
My idea that just came to my mind while I was writing this was this:
var x = document.createElement('div').constructor.prototype;
if (x instanceof HTMLElement && x !== x.constructor.prototype) {
// real instance
}
is this correct approach? I was also looking at Object.getPrototypeOf but it just return HTMLElement object (the one I'm testing). I need this to work for any nested prototype chain, because it's programming construct and user may use anything.
For detecting whether something is a prototype object, irrespective of HTMLElement, I would suggest doing
hasOwnProperty(x, "constructor") &&
typeof x.constructor == "function" &&
x.constructor.prototype == x
The context of the expression the user is trying to evaluate doesn't matter, they might as well try printing (. HTMLElement 'prototype) directly.
Separately, I would suggest not tying the "representation" function for instances to its constructor through a Map. Your add-repr! should just create a .repr() method on the prototype of the class, using Symbol("lips-repr()") as the property key.

Can I have a function used to help assign and validate properties passed as an object?

I'm fairly new to JavaScript and I was wondering whether it was possible to do the following:
I have an object with 4 properties , defined as follows:
var Thing = new function(){
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
}
Is it possible to define a set method which takes an object as an argument , finds matching properties and sets them to the given values and throws an exception if the property is not a member of the object.
So if I had an instance:
var thing = new Thing();
Then
thing.set({width: 10});
Would set width to 10, but
thing.set({radius: 5});
Would throw an exception.
Is this possible? If so, how can I achieve this?
Yes, you would just need to add a set method on the Thing prototype:
Thing.prototype.set = function(obj){
for(var attr in obj){
// make sure attribute is not inherited from prototype chain
if(obj.hasOwnProperty(attr)){
// ensure that Thing has this attribute
if(this.hasOwnProperty(attr)){
this[attr] = obj[attr];
} else {
throw new Error("Property doesn't exist");
}
}
}
}
Also var blah = new function(){..} should just be var blah = function(){..}
Your first block is a bit weird, you shouldn't be using new there. Also, if you're going to use semi-colons, you should be consistent and place one after the closing brace of your function, since it's a function expression.
As for your actual question, that's indeed possible. You could extend a subclass which had this functionality...
Subclass.prototype.set = function(properties) {
Object.keys(properties).forEach(function(key) {
if ( ! Object.hasOwnProperty.call(this, key)) {
throw Error("The target object doesn't have the key '" + key + "' defined.");
}
this[key] = properties[key];
});
};
Then you could make Thing's prototype point to it. If Thing is the only thing you want to work with this, simply assign it to Thing.prototype.

Can we call this real prototypal inheritance?

I'm always flabbergasted by the way people try to force some form of classical inheritance into javascript. I have designed a method to inherit in some object b the prototype methods from object a, without adding prototype methods from object b to object a, and the possibility to use private variables from the object inherited from1. Now I'm curious: would you say this is really prototypal 'inheritance'? Is it a viable method? Are there pittfals to it?
Here's some example code:
Object.prototype.inheritsFrom = function(obj){
var prototo = this.prototype,
protofrom = obj.prototype;
for (var l in protofrom) {
if (protofrom.hasOwnProperty(l)){
prototo[l] = protofrom[l];
}
}
}
function Obj1(){
var const1 = 25;
if (!Obj1.prototype.getConst1){
Obj1.prototype.getConst1 = function(){
return const1;
}
}
}
function Obj2(){
var const2 = 50;
if (!Obj2.prototype.getConst2){
Obj2.prototype.getConst2 = function(){
return const2;
}
}
Obj2.inheritsFrom(Obj1);
}
var instanceA = new Obj1,
instanceB = new Obj2;
Now instanceA contains method getConst1, instanceB contains methods getConst1 and getConst2, as you can see in this jsfiddle.
1 By assigning the prototype methods in the constructor function, effectively using the closure created by that.
No, that's not prototypical inheritance. In true prototypical inheritance, changes to the prototype appear in the objects that rely on that prototype. In your example, they don't, because they're only copied.
I'm not saying it may not be another useful form of inheritance for some situations, but it's not prototypical. In some sense I'm not even sure it's inheritance, although I think one could argue it either way and it doesn't really matter regardless.
Here's an example of adding to the prototype:
function Parent() {
}
Parent.prototype.foo = function() {
display("foo!");
};
function Child() {
}
Child.prototype = new Parent();
var c = new Child();
display("[before] typeof c.foo === '" + typeof c.foo + "'");
// shows "[before] typeof c.foo === 'function'"
display("[before] typeof c.bar === '" + typeof c.bar + "'");
// shows "[before] typeof c.bar === 'undefined'"
display("Note that c.bar is currently undefined");
Parent.prototype.bar = function() {
display("bar!");
};
display("[after] typeof c.bar === '" + typeof c.bar + "'");
// shows "[after] typeof c.bar === 'function'"
display("Now c.bar is a function");
c.foo();
c.bar();
Live copy
Note that this is not an obscure case. After all, your own code relies on changes to Object.prototype being reflected in the other things (Function) that have already derived from it.
Off-topic: Strongly recommend never adding anything to Object.prototype. It will break a huge amount of code that assumes that using for..in on a {} would yield no properties. Until you can reliably mark additions as non-enumerable (ECMAScript5 now provides a way to do that, but most implementations don't have it yet), just stay away from Object.prototype. Just a recommendation. Additionally, in your case, it doesn't make sense, because the inheritsFrom only works for Function instances, so you'd want to add it to Function.prototype instead (which is a lot less dangerous).

Categories