Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
371 views
in Technique[技术] by (71.8m points)

javascript - Object.create Prototype Chains

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? and Human1.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 (??)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...