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.
Does Kyle Simpson's "OLOO (Objects Linking to Other Objects) Pattern" differ in any way from the the Prototype design pattern? Other than coining it by something that specifically indicates "linking" (the behavior of prototypes) and clarifying that there's no to "copying" happening here (a behavior of classes), what exactly does his pattern introduce?
Here's an example of Kyle's pattern from his book, "You Don't Know JS: this & Object Prototypes":
var Foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this.me;
}
};
var Bar = Object.create(Foo);
Bar.speak = function() {
alert("Hello, " + this.identify() + ".");
};
var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");
b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."
what exactly does his pattern introduce?
OLOO embraces the prototype chain as-is, without needing to layer on other (IMO confusing) semantics to get the linkage.
So, these two snippets have the EXACT same outcome, but get there differently.
Constructor Form:
function Foo() {}
Foo.prototype.y = 11;
function Bar() {}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.z = 31;
var x = new Bar();
x.y + x.z; // 42
OLOO Form:
var FooObj = { y: 11 };
var BarObj = Object.create(FooObj);
BarObj.z = 31;
var x = Object.create(BarObj);
x.y + x.z; // 42
In both snippets, an x object is [[Prototype]]-linked to an object (Bar.prototype or BarObj), which in turn is linked to third object (Foo.prototype or FooObj).
The relationships and delegation are identical between the snippets. The memory usage is identical between the snippets. The ability to create many "children" (aka, many objects like x1 through x1000, etc) is identical between the snippets. The performance of the delegation (x.y and x.z) is identical between the snippets. The object creation performance is slower with OLOO, but sanity checking that reveals that the slower performance is really not an issue.
What I argue OLOO offers is that it's much simpler to just express the objects and directly link them, than to indirectly link them through the constructor/new mechanisms. The latter pretends to be about classes but really is just a terrible syntax for expressing delegation (side note: so is ES6 class syntax!).
OLOO is just cutting out the middle-man.
Here's another comparison of class vs OLOO.
I read Kyle's book, and I found it really informative, particularly the detail about how this is bound.
Pros:
For me, there a couple of big pros of OLOO:
1. Simplicity
OLOO relies on Object.create() to create a new object which is [[prototype]]-linked to another object. You don't have to understand that functions have a prototype property or worry about any of the potential related pitfalls that come from its modification.
2. Cleaner syntax
This is arguable, but I feel the OLOO syntax is (in many cases) neater and more concise than the 'standard' javascript approach, particularly when it comes to polymorphism (super-style calls).
Cons:
I think there is one questionable bit of design (one that actually contributes to point 2 above), and that is to do with shadowing:
In behaviour delegation, we avoid if at all possible naming things the same at different levels of the [[Prototype]] chain.
The idea behind this is that objects have their own more specific functions which then internally delegate to functions lower down the chain. For example, you might have a resource object with a save() function on it which sends a JSON version of the object to the server, but you may also have a clientResource object which has a stripAndSave() function, which first removes properties that shouldn't be sent to the server.
The potential problem is: if someone else comes along and decides to make a specialResource object, not fully aware of the whole prototype chain, they might reasonably* decide to save a timestamp for the last save under a property called save, which shadows the base save() functionality on the resource object two links down the prototype chain:
var resource = {
save: function () {
console.log('Saving');
}
};
var clientResource = Object.create(resource);
clientResource.stripAndSave = function () {
// Do something else, then delegate
console.log('Stripping unwanted properties');
this.save();
};
var specialResource = Object.create( clientResource );
specialResource.timeStampedSave = function () {
// Set the timestamp of the last save
this.save = Date.now();
this.stripAndSave();
};
a = Object.create(clientResource);
b = Object.create(specialResource);
a.stripAndSave(); // "Stripping unwanted properties" & "Saving".
b.timeStampedSave(); // Error!
This is a particularly contrived example, but the point is that specifically not shadowing other properties can lead to some awkward situations and heavy use of a thesaurus!
Perhaps a better illustration of this would be an init method - particularly poignant as OOLO sidesteps constructor type functions. Since every related object will likely need such a function, it may be a tedious exercise to name them appropriately, and the uniqueness may make it difficult to remember which to use.
*Actually it's not particularly reasonable (lastSaved would be much better, but it's just an example.)
The discussion in "You Don't Know JS: this & Object Prototypes" and the presentation of the OLOO is thought-provoking and I have learned a ton going through the book. The merits of the OLOO pattern are well-described in the other answers; however, I have the following pet complaints with it (or am missing something that prevents me from applying it effectively):
1
When a "class" "inherits" another "class" in the classical pattern, the two function can be declared similar syntax ("function declaration" or "function statement"):
function Point(x,y) {
this.x = x;
this.y = y;
};
function Point3D(x,y,z) {
Point.call(this, x,y);
this.z = z;
};
Point3D.prototype = Object.create(Point.prototype);
In contrast, in the OLOO pattern, different syntactical forms used to define the base and the derived objects:
var Point = {
init : function(x,y) {
this.x = x;
this.y = y;
}
};
var Point3D = Object.create(Point);
Point3D.init = function(x,y,z) {
Point.init.call(this, x, y);
this.z = z;
};
As you can see in the example above the base object can be defined using object literal notation, whereas the same notation can't be used for the derived object. This asymmetry bugs me.
2
In the OLOO pattern, creating an object is two steps:
call Object.create
call some custom, non standard method to initialize the object (which you have to remember since it may vary from one object to the next):
var p2a = Object.create(Point);
p2a.init(1,1);
In contrast, in the Prototype pattern you use the standard operator new:
var p2a = new Point(1,1);
3
In the classical pattern I can create "static" utility functions that don't apply directly to an "instant" by assigning them directly to the "class" function (as opposed to its .prototype). E.g. like function square in the below code:
Point.square = function(x) {return x*x;};
Point.prototype.length = function() {
return Math.sqrt(Point.square(this.x)+Point.square(this.y));
};
In contrast, in the OLOO pattern any "static" functions are available (via the [[prototype]] chain) on the object instances too:
var Point = {
init : function(x,y) {
this.x = x;
this.y = y;
},
square: function(x) {return x*x;},
length: function() {return Math.sqrt(Point.square(this.x)+Point.square(this.y));}
};
"I figured to do it makes each obj dependent on the other"
As Kyle explains when two objects are [[Prototype]] linked, they aren't really
dependent on each other; instead they are individual object. You're linking one
object to the other with a [[Prototype]] linkage which you can change anytime you wish. If you take two [[Prototype]] linked objects created through OLOO style as being dependent on each other, you should also think the same about the ones created through constructor calls.
var foo= {},
bar= Object.create(foo),
baz= Object.create(bar);
console.log(Object.getPrototypeOf(foo)) //Object.prototype
console.log(Object.getPrototypeOf(bar)) //foo
console.log(Object.getPrototypeOf(baz)) //bar
Now think for a second do you think of foo bar and baz as being dependent on each-other?
Now let's do the same this constructor style code-
function Foo() {}
function Bar() {}
function Baz() {}
Bar.prototype= Object.create(Foo);
Baz.prototype= Object.create(Bar);
var foo= new Foo(),
bar= new Bar().
baz= new Baz();
console.log(Object.getPrototypeOf(foo)) //Foo.prototype
console.log(Object.getPrototypeOf(Foo.prototype)) //Object.prototype
console.log(Object.getPrototypeOf(bar)) //Bar.prototype
console.log(Object.getPrototypeOf(Bar.prototype)) //Foo.prototype
console.log(Object.getPrototypeOf(baz)) //Baz.prototype
console.log(Object.getPrototypeOf(Baz.prototype)) //Bar.prototype
The only difference b/w the latter and the former code is that in the latter one
foo, bar, baz bbjects are linked to each-other through arbitrary objects
of their constructor function (Foo.prototype, Bar.prototype, Baz.prototype) but in the former one (OLOO style) they are linked directly. Both ways you're just linking foo, bar, baz with each other, directly in the former one and indirectly in the latter one. But, in both the cases the objects are independent of each-other because it isn't really like an instance of any class which once instantiated, can't be made to inherit from some other class. You can always change which object an object should delegate too.
var anotherObj= {};
Object.setPrototypeOf(foo, anotherObj);
So they're all independent of each-other.
" I was hoping OLOO would solve the issue in which each object knows
nothing about the other."
Yes that's indeed possible-
Let's use Tech as an utility object-
var Tech= {
tag: "technology",
setName= function(name) {
this.name= name;
}
}
create as many objects as you wish linked to Tech-
var html= Object.create(Tech),
css= Object.create(Tech),
js= Object.create(Tech);
Some checking (avoiding console.log)-
html.isPrototypeOf(css); //false
html.isPrototypeOf(js); //false
css.isPrototypeOf(html); //false
css.isPrototypeOf(js); //false
js.isPrototypeOf(html); //false
js.isPrototypwOf(css); //false
Tech.isPrototypeOf(html); //true
Tech.isPrototypeOf(css); //true
Tech.isPrototypeOf(js); //true
Do you think html, css, js objects are connected to each-other? No, they aren't. Now let's see how we could've done that with constructor function-
function Tech() { }
Tech.prototype.tag= "technology";
Tech.prototype.setName= function(name) {
this.name= name;
}
create as many objects as you wish linked to Tech.proptotype-
var html= new Tech(),
css= new Tech(),
js= new Tech();
Some checking (avoiding console.log)-
html.isPrototypeOf(css); //false
html.isPrototypeOf(js); //false
css.isPrototypeOf(html); //false
css.isPrototypeOf(js); //false
js.isPrototypeOf(html); //false
js.isPrototypeOf(css); //false
Tech.prototype.isPrototypeOf(html); //true
Tech.prototype.isPrototypeOf(css); //true
Tech.prototype.isPrototypeOf(js); //true
How do you think these constructor-style Objects (html , css, js)
Objects differ from the OLOO-style code? In fact they serve the same purpose. In OLOO-style one objects delegate to Tech(delegation was set explicitly) while in constructor-style one objects delegate to Tech.prototype(delegation was set implicitly). Ultimately you end up linking the three objects, having no linkage with each-other, to one object, directly using OLOO-style, indirectly using constructor-style.
"As is, ObjB has to be created from ObjA.. Object.create(ObjB) etc"
No, ObjB here is not like an instance (in classical-based languages) of any class
ObjA. It sould be said like objB object is made delegate to ObjA object at it's creation
time". If you used constructor, you would have done the same 'coupling', although indirectly by making use of .prototypes.
#Marcus #bholben
Perhaps we can do something like this.
const Point = {
statics(m) { if (this !== Point) { throw Error(m); }},
create (x, y) {
this.statics();
var P = Object.create(Point);
P.init(x, y);
return P;
},
init(x=0, y=0) {
this.x = x;
this.y = y;
}
};
const Point3D = {
__proto__: Point,
statics(m) { if (this !== Point3D) { throw Error(m); }},
create (x, y, z) {
this.statics();
var P = Object.create(Point3D);
P.init(x, y, z);
return P;
},
init (x=0, y=0, z=0) {
super.init(x, y);
this.z = z;
}
};
Of course, creating a Point3D object that links to the prototype of a Point2D object is kind of silly, but that's beside the point (I wanted to be consistent with your example). Anyways, as far as the complaints go:
The asymmetry can be fixed with ES6's Object.setPrototypeOf or
the more frowned upon __proto__ = ... that I use. We can also use super on regular objects now too, as seen in Point3D.init(). Another way would be to do something like
const Point3D = Object.assign(Object.create(Point), {
...
}
though I don't particularly like the syntax.
We can always just wrap p = Object.create(Point) and then p.init() into a constructor. e.g. Point.create(x,y). Using the code above we can create a Point3D "instance" in the following manner.
var b = Point3D.create(1,2,3);
console.log(b); // { x:1, y:2, z:3 }
console.log(Point.isPrototypeOf(b)); // true
console.log(Point3D.isPrototypeOf(b)) // true
I just came up with this hack to emulate static methods in OLOO. I'm not sure if I like it or not. It requires calling a special property at the top of any "static" methods. For example, I've made the Point.create() method static.
var p = Point.create(1,2);
var q = p.create(4,1); // Error!
Alternatively, with ES6 Symbols you can safely extend Javascript base classes. So you could save yourself some code and define the special property on Object.prototype. For example,
const extendedJS = {};
( function(extension) {
const statics = Symbol('static');
Object.defineProperty(Object.prototype, statics, {
writable: true,
enumerable: false,
configurable: true,
value(obj, message) {
if (this !== obj)
throw Error(message);
}
});
Object.assign(extension, {statics});
})(extendedJS);
const Point = {
create (x, y) {
this[extendedJS.statics](Point);
...
#james emanon - So, you are referring to multiple inheritance (discussed on page 75 in the book "You Don't Know JS: this & Object Prototypes"). And that mechanism we can find in underscore's "extend" function for example. Names of object you stated in your example are a bit mixing apples, oranges and candies but I understand the point behind. From my experience this would be OOLO version:
var ObjA = {
setA: function(a) {
this.a = a;
},
outputA: function() {
console.log("Invoking outputA - A: ", this.a);
}
};
// 'ObjB' links/delegates to 'ObjA'
var ObjB = Object.create( ObjA );
ObjB.setB = function(b) {
this.b = b;
}
ObjB.setA_B = function(a, b) {
this.setA( a ); // This is obvious. 'setA' is not found in 'ObjB' so by prototype chain it's found in 'ObjA'
this.setB( b );
console.log("Invoking setA_B - A: ", this.a, " B: ", this.b);
};
// 'ObjC' links/delegates to 'ObjB'
var ObjC = Object.create( ObjB );
ObjC.setC = function(c) {
this.c = c;
};
ObjC.setA_C = function(a, c) {
this.setA( a ); // Invoking 'setA' that is clearly not in ObjC shows that prototype chaining goes through ObjB all the way to the ObjA
this.setC( c );
console.log("Invoking setA_C - A: ", this.a, " C: ", this.c);
};
ObjC.setA_B_C = function(a, b, c){
this.setA( a ); // Invoking 'setA' that is clearly not in ObjC nor ObjB shows that prototype chaining got all the way to the ObjA
this.setB( b );
this.setC( c );
console.log("Invoking setA_B_C - A: ", this.a, " B: ", this.b, " C: ", this.c);
};
ObjA.setA("A1");
ObjA.outputA(); // Invoking outputA - A: A1
ObjB.setA_B("A2", "B1"); // Invoking setA_B - A: A2 B: B1
ObjC.setA_C("A3", "C1"); // Invoking setA_C - A: A3 C: C1
ObjC.setA_B_C("A4", "B2", "C1"); // Invoking setA_B_C - A: A4 B: B2 C: C1
It is simple example but the point shown is that we are just chaining object together in rather flat structure/formation and still have possibility to use methods and properties from multiple object. We achieve same things as with class/"copying the properties" approach. Summed by Kyle (page 114, "this & Object Prototypes"):
In other words, the actual mechanism, the essence of what’s important
to the functionality we can leverage in JavaScript, is all about objects
being linked to other objects.
I understand that more natural way for you would be to state all the "parent" (careful :) ) objects in one place/function call rather modeling whole chain.
What it requires is shift in thinking and modeling problems in our applications according to that. I'm also getting used to it. Hope it helps and final verdict from the Kyle himself would be great. :)
#Marcus, just like you, I have been keen on OLOO and also dislike the asymmetry as described in your first point. I've been playing with an abstraction to bring the symmetry back. You could create a link() function that is used in place of Object.create(). When used, your code could look something like this...
var Point = {
init : function(x,y) {
this.x = x;
this.y = y;
}
};
var Point3D = link(Point, {
init: function(x,y,z) {
Point.init.call(this, x, y);
this.z = z;
}
});
Remember that Object.create() has a second parameter that can be passed in. Here is the link function that leverages the second parameter. It also allows a little bit of custom configuration...
function link(delegate, props, propsConfig) {
props = props || {};
propsConfig = propsConfig || {};
var obj = {};
Object.keys(props).forEach(function (key) {
obj[key] = {
value: props[key],
enumerable: propsConfig.isEnumerable || true,
writable: propsConfig.isWritable || true,
configurable: propsConfig.isConfigurable || true
};
});
return Object.create(delegate, obj);
}
Of course, I think #Kyle would not endorse shadowing the init() function in the Point3D object. ;-)
Is there a way to OLOO more than "two" objects.. all the examples I consist of the based example (see OP's example). Lets say we had the following objects, how can we create a "fourth" object that has the attributes of the 'other' three? ala...
var Button = {
init: function(name, cost) {
this.buttonName = name;
this.buttonCost = cost;
}
}
var Shoe = {
speed: 100
}
var Bike = {
range: '4 miles'
}
these objects are arbitrary, and could encompass all sorts of behaviors. But the gist is, we have 'n' number of objects, and our new object needs something from all three.
instead of the given examples ala:
var newObj = Object.create(oneSingularObject);
newObj.whatever..
BUT, our newObject = (Button, Bike, Shoe)......
What is the pattern to get this going in OLOO?
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).
I'm messing around with the prototype chain and noticed something I can't explain. I'm still learning all of this, so it's probably a mistake i've made. I'm trying to do some multi-inheritance, like so many others. I noticed the prototype object looks a lot like a hash/dictionary, I thought, why not use something like underscore.extend to merge multiple prototype objects together as one.
function A(){this.value="A";};
A.prototype.funcA = function (){console.log(this.value);}
function B(){this.value="B";};
B.prototype.funcB = function (){console.log(this.value);}
function C(){
// fix constructor
this.constructor = C;
// 'inherit' properties
A.call(this);
B.call(this);
};
// gobble up the prototype chains of A and B
C.prototype = new underscore.extend(A.prototype,B.prototype);
C.prototype.funcC = function (){console.log(this.value);}
var c = new C();
> c instanceof C
true
> c instanceof A
true
> c instanceof B
false
I'm really surprised to get a true at all here. Can anyone explain what's going on here?
UPDATE
I removed underscore's extend method from the code, as suggested, and this works a lot better. thanks!
function extend(destination, source) {
for (var property in source) {
if (source.hasOwnProperty(property)) {
destination[property] = source[property];
}
}
return destination;
};
function A(){this.value="A";};
A.prototype.funcA = function (){console.log(this.value);}
function B(){this.value="B";};
B.prototype.funcB = function (){console.log(this.value);}
function C(){
this.constructor = C;
A.call(this);
B.call(this);
};
var destination = {};
destination = extend(destination,A.prototype);
destination = extend(destination,B.prototype);
C.prototype = destination;
C.prototype.funcC = function (){console.log(this.value);}
var c = new C();
> c
{ constructor: [Function: C], value: 'B' }
> c instanceof A
false
> c instanceof B
false
> c instanceof C
true
There is no multiple inheritance in JavaScript, because one object can have only one prototype. To prove it is enough to see ECMAScript 5 Object.getPrototypeOf method which of course returns only one value. For older interpreters you could try __proto__ property (non-standard) of simply obj.constructor.prototype.
The example you've made give you a possibility to have all features from two different prototypes, however it brakes the prototype chain - this is why instanceof operator returns false for A and B. In fact prototypes of A or B are not prototypes of your object, but the mixin of them which you have made using the extend function. The function name is very misleading (however such name is used by some of frameworks and libraries) - because we don't extend any object (in object-oriented programming meaning) - we build a mixin of two object - which is completely different design pattern.
Bit out off topic - if you're experimenting with objects and prototypal inheritance - try to play with Object.create method (of ECMAScript 5). It's very useful in this case.
Javascript - The Definitive Guide (6ed) shows the following code:
Originally we have this function
/*
* Copy the enumerable properties of p to o, and return o.
* If o and p have a property by the same name, o's property is overwritten.
* This function does not handle getters and setters or copy attributes.
*/
function extend(o, p) {
for(prop in p) { // For all props in p.
o[prop] = p[prop]; // Add the property to o.
}
return o;
}
Then the author decided to rewrite it and extend the copy ability (able to copy accessor properties, for example):
/*
* Add a nonenumerable extend() method to Object.prototype.
* This method extends the object on which it is called by copying properties
* from the object passed as its argument. All property attributes are
* copied, not just the property value. All own properties (even non-
* enumerable ones) of the argument object are copied unless a property
* with the same name already exists in the target object.
*/
Object.defineProperty(Object.prototype,
"extend", // Define Object.prototype.extend
{
writable: true,
enumerable: false, // Make it nonenumerable
configurable: true,
value: function(o) { // Its value is this function
// Get all own props, even nonenumerable ones
var names = Object.getOwnPropertyNames(o);
// Loop through them
for(var i = 0; i < names.length; i++) {
// Skip props already in this object
if (names[i] in this) continue;
// Get property description from o
var desc = Object.getOwnPropertyDescriptor(o,names[i]);
// Use it to create property on this
Object.defineProperty(this, names[i], desc);
}
}
});
I don't understand why we are extending Object.prototype, and now how do we use this to copy all the attributes in Object y to Object x? How do I use Object.prototype.extend ?
I decided to test if I can do something quicker. I don't understand why the following custom code doesn't work.
function extend(o){
var p = new Object();
for( prop in o)
Object.defineProperty(p,prop,Object.getOwnPropertyDescriptor(o,prop));
return p;
}
// Let's perform a simple test
var o = {};
Object.defineProperty(o, "x", { value : 1,
writable: true,
enumerable: false,
configurable: true});
o.x; // => 1
Object.keys(o) // => []
var k = new Object(extend(o)); // Good, k.x => 1
// More test
Object.defineProperty(o, "x", { writable: false });
Object.defineProperty(o, "x", { value: 2 });
Object.defineProperty(o, "x", { get: function() { return 0; } });
o.x // => 0
// Perform the creation again
var k = new Object(extend(o)); // bad. k.x is 1, not 0
// so the getter property didn't copy to k !!!
Sorry I am pretty new to Javascript. Thanks for the help in advance. All these questions are related to the transformation / rewrite of the function "extend"
I edited the my test code. Sorry!
The purpose of defining this in the prototype is so that every Object can call it as a member function, like this:
yourobject.extend(anotherObject);
Most coders find that more elegant than having to pass both objects as parameters, like this:
extend(yourObject, anotherObject);
Modifying prototypes is a great way to add useful "methods" to objects.
side note: I would not recommend using the author's extend code. It doesn't properly check hasOwnProperty.
You seem to misunderstand the use of prototype. Prototyping is JavaScript's way of dealing with inheritance. The extend function is terminology loaned from languages like C++ and Java and is not native to JavaScript.
Here's a guide on prototype: http://mckoss.com/jscript/object.htm
I have several years experience writing JavaScript and I'd recommend not using prototyping at all. JavaScript has other ways of dealing with data modeling that I find more elegant. It's still useful to understand how prototyping works though because it deepens your understanding of Objects.