Initial Question
Yesterday i read about ECMAScript 5 Object.create() And I wanted to start building prototype Chains in my Code with this method instead of setting the prototype and its constructor, I like that you can directly set writable configurable etc..
I tried it like this
function printobject(msg, obj) {
if (msg) {
document.write("<b>" + msg + "</b><br>");
document.write("<hr><br>");
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (obj[prop].toString() !== "[object Object]") {
document.write(prop + " : " + obj[prop] + "<br>");
}
else {
document.write("<b>" + prop + " : " + obj[prop] + "</b><br>");
printobject("", obj[prop]);
}
}
}
if (msg) {
document.write("<br><hr><br>");
}
};
var base = {
extend: function () { //extend this Object
var args = Array.prototype.slice.call(arguments);
printobject("Arguments to Extend", args)
var that = Object.create(this, (args ? args.shift() : {}));
var arg = args.shift() || {};
printobject("Copy Properties to New Object", arg);
for (var prop in arg) {
that[prop] = arg[prop];
}
// Object.freeze(that);
return that;
},
create: function () { //Creates new instances of the Object
var that = Object.create(this, {
extend: {
value: null,
writable: false,
configurable: false
}, //sets extend and create to null so you cant create a new instance when used create ( use extend instead);
create: {
value: null,
writable: false,
configurable: false
}
});
that.init.apply(that, arguments); //call init function for the new created object;
return that;
},
init: function () {
printobject("No Initfunction supplied for New Object", this);
} // Empty init function for fallback
}
var Human = base.extend({
name: {
value: "test"
}
}, {
init: function (name) {
alert(name + " has been created");
this.name = name;
},
walk: function () {
alert(this.name + " walks");
}
});
var Human1 = Human.create("test2");
//alert("Human1 - Name:" + Human1.name);
Human1.walk();
Human.walk = function () {
alert("Walk has been overwritten")
}; //Object freezed
Human1.walk();
Human1.create = function () {
alert("Overwrite create");
}; //Doesnt exist in created Object
Human1.create(); ?
- Do the methods given in
Human
only exist once in the ram? andHuman1.walk()
points to it? - I wonder if this is the right Approach of doing it like this? I'm relatively new to JavaScript.
This is the code on jsfiddle.
Answer
First of all, thx a lot that made things def clearer =) But, 1: when I do it like this the instances inherit from their constructor's prototype (?)
Nothing = {};
function base() {
this.inherit = function(constructor) {
alert("inherit");
var obj = constructor;
obj.constructor = constructor;
obj.prototype = this;
return obj ;
}
;}
base.prototype = Nothing;
base.constructor = base;
var Top = new base();
var Human = Top.inherit(function(name) {
this.name = name;
});
var Paul = new Human("Paul");
alert(Paul.name);
alert(Paul instanceof Human); //true `
2: So the instanceof operator doesnt break in this Code , (that it works for functions only seems clear to me)
But written this way, Paul still inherits the inherit() method from Top's prototype and i would need to overwrite it But if i dont want the instance of Human to inherit the method, how do i do this ?
And i cant set property descriptors like wrtable except using Objkect.defineproperty (?)
So what are the main benefits from using Object.create() to inherit from Objects vs Setting the prototypes and construcotrs ? =)
3: Oh thx, yes thats def right thats not an extension of base object =) thx for the suggestion =)
Thx for all the effort =)
Answer
Ok so when i do
Nothing = {}
base.prototype = Nothing;
this doesnt prevent s.o to go up the prototype chain til Object.prototype ? if not , is there a way to do this ? =) Would ( Object.create(null); ) do this,
and i thought that i had to set
base.prototype.constructor = base;
because otherwise, the prototypes constructor of
var Top = new base();
would be Nothings' or does'nt base inherit a constructor from somewhere up the prototype chain if the prototype is set to Nothing ->
Top instanceof base // false
Update
I ended up doing it in a way like this now:
var base = {
// a tiny little selfmade prototypical inheritance system
// you are free to add function arguments for extending the created objects
// neither instanceof nor .constructor is featured, because "classes" are no functions
create: function(extension,desc) {
// instances inherit from the proto objects
var newInst = Object.create(this.proto, desc);
if(this.proto.childExtendable) //if Subclass allows its Children to be Extendible, do so
newInst.extend(extension);
if(newInst.init||this.proto.init) //4
newInst.init()
return newInst
},
inherit: function(props) {
// the "class" inherits static methods from the class
var sub = Object.create(this);
// and the proto objects inherits from the parent proto
sub.proto = Object.create(this.proto);
props.protect = this.protect;
if(props.childExtendable)
props.extend = this.extend;
this.extend.call(sub.proto, props);
return sub;
},
extend: function (props) {
for (var prop in props) {
var propmatch = prop.match(/(.*?)__(.{1,5}?)__(.*)/)||["",prop,"",""];
this[propmatch[1]+propmatch[3]] = props[prop];
if(propmatch[2])
this.protect(propmatch[1]+propmatch[3],propmatch[2]);
}
},
protect: function(prop,flags) { //with each call it toggles the given flags, so you can protect funcitons given to the inherit function ;; //This should be available to all childs, but adding it to the base.proto, it changes Object.prototyppe ( therefore not a good idea)
var d = Object.getOwnPropertyDescriptor(this, prop);
if (flags.match(/w/)){
Ti.API.info("Setting writable for propertie " + prop + " in Object " + this + " to " + !d.writable);
Object.defineProperty(this, prop, {writable:!d.writable});};
if (flags.match(/c/)){
Ti.API.info("Setting configurable for propertie " + prop + "in Object " + this);
Object.defineProperty(this, prop, {configurable:!d.configurable});};
if (flags.match(/e/)){
Ti.API.info("Setting enumerable for propertie " + prop + "in Object " + this);
Object.defineProperty(this, prop, {configurable:!d.enumerable});};
if (flags.match(/a/)){
Ti.API.info("Setting enumerable for propertie " + prop + "in Object " + this);
Object.preventExtensions(this);};
},
init: function() {},
proto: Object.prototype // or null, if you want
};
var Human = base.inherit({ //will be put in Human.proto
childExtendable:true,
init:function() {alert("Humans Init for all Instances")},
say:function() { alert("Hi, I'm "+this.name); }
});
Human.proto.name = "default"; // You could use an argument to the inherit function
// I just want to make clear what happens
Ti.API.info(Object.getPrototypeOf(Function) + "a");
var paul = Human.create({ //extends this object
name: "Paul",
test: function() {alert("test")},
init__wce__: function() {alert("Pauls Own Init")},
say__w__ : function() { alert("Hi, I'm" + this.name + "s Own Function")}
});
paul.name = "Paul"; // and again, the create function might do it for you
paul.say = function() {alert("Pauls say is overwritten")} // define init without __wce__ and it will be overwritten
paul.say(); // -> "Hi, I'm Paul"
Just if anyone cares
However, jsfiddle won't run this, Titanium does everythign as expected
maybe some strict mode (??)
Do the methods given in Human only exist once in the ram?
Yes.
and Human1.walk() points to it?
Yes. To be more correct, the prototype of Human1
, Human
, has a property "walk" pointing to it.
I wonder if this is the right Approach of doing it like this? I'm relatively new to JavaScript.
I'd say no, because it is overly complicated, and partly wrong.
- The prototype chain of Human instances includes
base
. Thats odd, and you need to overwrite the create and extend methods for every instance. The usual method is that "classes" contain a "prototype" property, from which their instances inherit. - Your pattern breaks the
instanceof
operator, although that might be a minor issue. - The extend method is confusing. It does not extend the object itself, but create a new object inheriting from it and setting properties and property descriptors on that. Better implementation of the same thing:
base.inherit = function(descs, props) {
// creates a new object inheriting from this
var that = Object.create(this, descs); // will even work when undefined
if (props)
for (var prop in props)
that[prop] = props[prop];
// Object.freeze(that);
return that;
};
To the extended question:
base.prototype = Nothing;
base.constructor = base;
is quite useless. First, the "prototype" property of any function is an (nearly) empty object by default, until you overwrite it. No need to set it to nothing
:-)
And the "constructor" property is usually a prototype property. It would be inherited by all instances, pointing to thei constructor function. You only need to set it explicitly when overwriting a function's "prototype" property - and you should not set the the "constructor" property on the function itself.
(continuing:) I though more about a solution like this:
var base = {
// a tiny little selfmade prototypical inheritance system
// you are free to add function arguments for extending the created objects
// neither instanceof nor .constructor is featured, because "classes" are no functions
create: function([desc]) {
// instances inherit from the proto objects
return Object.create(this.proto, [desc]);
},
inherit: function([props]) {
// the "class" inherits static methods from the class
var sub = Object.create(this);
// and the proto objects inherits from the parent proto
sub.proto = Object.create(this.proto);
[Object.extend(sub.proto, props);]
return sub;
},
proto: Object.prototype // or null, if you want
};
var Human = base.inherit();
Human.proto.name = "default"; // You could use an argument to the inherit function
// I just want to make clear what happens
Human.proto.say = function() { alert("Hi, I'm "+this.name); };
var paul = Human.create();
paul.name = "Paul"; // and again, the create function might do it for you
paul.say(); // -> "Hi, I'm Paul"
This way, paul
inherits from Human.proto
inherits from base.proto
which is Object.prototype
or null
. And Human
inherits from base
, i.e. you could easily build a "subclass" with Human.inherit()
.
Whether you want to use property descriptors or not is absolutely your choice. Everywhere you create something and extend it, you might use Object.defineProperties
(or the second argument to Object.create
) as well as Object.extend
(the usual for-in-copy-method).
what are the main benefits from using Object.create() to inherit from Objects vs Setting the prototypes and construcotrs?
It's a design choice. Object.create
won't call a [constructor] function on the built object. See Using "Object.create" instead of "new" or Understanding the difference between Object.create() and new SomeFunction() for further information.
base.prototype = {};
doesnt prevent s.o to go up the prototype chain til Object.prototype?
Yes. An empty object (as created by your literal) still has Object.prototype
in its chain. The only way to do this is Object.create(null)
(not shim-able with new
).
i thought that i had to set
base.prototype.constructor = base;
Not in this case. Having a function base(){...}
, setting its "prototype" property to {constructor: base}
changes absolutely nothing (except that "constructor" in enumerable now) - every function has such a default proto object including the "constructor".
So only when you need to overwrite the "prototype" property with a new object, as it happens when letting it inherit from another function's prototype, you might add this convenience property: MySubClass.prototype = Object.create(MyClass.prototype, {constructor:{value:MySubClass}});
otherwise...
Nothing would happen. The "constructor" property on prototype objects is needed for no language features (like instanceof
), and is seldom used. It's likely that nothing breaks.
来源:https://stackoverflow.com/questions/11240011/object-create-prototype-chains