使用 Object.create 创建原型链

4

初始问题

昨天我读到了有关ECMAScript 5 Object.create()的文章,我想用这种方法开始在我的代码中构建原型链,而不是设置原型和它的构造函数, 我喜欢你可以直接设置可写,可配置等等。

我尝试像这样:

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(); ?
  • Human类中的方法只存在内存中一次吗?Human1.walk()是否指向它?
  • 我想知道这种做法是否正确?我对JavaScript相对较新。

这是在jsfiddle上的代码。

答案

首先,非常感谢你使事情变得更加清晰了 =) 但是, 1:当我像这样做时,实例会从它们的构造函数的原型中继承(?)

 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: 所以 instanceof 运算符在这段代码中不会出错,(它只适用于函数似乎对我来说很清楚)。

但是按照这种方式编写,Paul仍然从Top的原型继承了inherit()方法,而我需要覆盖它。但是如果我不希望Human的实例继承该方法,我该怎么做呢?

我不能设置像writable之类的属性描述符,除非使用Objkect.defineproperty(?)

那么使用Object.create()从对象继承与设置原型和构造函数相比,主要有哪些好处呢?=)

3: 谢谢,是的,那肯定不是基本对象的扩展 =) 感谢您的建议 =)

感谢您的所有努力 =)

答案

好的,当我执行

Nothing = {}

base.prototype = Nothing;

时,这并没有防止s.o向上遍历原型链直到Object.prototype吗?如果不是,有没有办法做到这一点?=)使用(Object.create(null);)是否可以做到这一点?

而且我认为我必须设置

base.prototype.constructor = base;

否则,

var Top = new base();

的原型构造函数将是Nothing的,或者如果原型设置为Nothing,那么base从原型链上方继承构造函数吗?

Top instanceof base // false

更新

我最终以以下方式完成了它:

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"

只是如果有人关心
然而,jsfiddle无法运行此代码,在 Titanium 中一切都按预期执行 可能是某种严格模式(??)

1个回答

4

Human中的方法只在Ram中存在一次吗?

是的。

Human1.walk()指向它吗?

是的。更准确地说,Human1的原型Human有一个指向它的属性"walk"。

我想知道这样做是否正确?我对JavaScript还比较新。

我会说不是,因为它过于复杂,而且部分是错误的。

  • Human实例的原型链包括base。这很奇怪,并且你需要为每个实例覆盖create和extend方法。通常的方法是"类"包含一个"prototype"属性,从中继承它们的实例。
  • 你的模式破坏了instanceof运算符,尽管这可能是一个小问题。
  • extend方法令人困惑。它不扩展对象本身,而是创建一个从中继承并设置属性和属性描述符的新对象。同样的事情更好的实现方式:

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;
};

扩展问题如下:
base.prototype = Nothing​;
base.constructor = base;

这是毫无意义的。首先,任何函数的“prototype”属性默认情况下都是一个(几乎)空对象,直到您覆盖它。没有必要将其设置为nothing :-)

而“constructor”属性通常是原型属性。它会被所有实例继承,指向它们的构造函数。只有在覆盖函数的“prototype”属性时才需要显式设置它 - 并且不应该在函数本身上设置“constructor”属性。

(继续:)我更多地考虑了这样的解决方案:

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"

这样,paul继承自Human.proto,而Human.proto又继承自base.proto,它是Object.prototypenull。而Human继承自base,也就是说你可以使用Human.inherit()轻松地构建“子类”。
是否使用属性描述符完全是你的选择。在你创建和扩展任何东西的地方,你都可以使用Object.defineProperties(或Object.create的第二个参数),以及Object.extend(通常是for-in-copy-method)。

使用Object.create()从对象继承与设置原型和构造函数有什么主要好处?

这是一个设计选择。 Object.create不会在构建对象上调用[constructor]函数。 有关详细信息,请参见使用“Object.create”而不是“new”了解Object.create()和new SomeFunction()之间的区别

base.prototype = {};不能防止某些人向上访问prototype链直到Object.prototype吗?

是的。一个空对象(由您的字面值创建)仍然在其链中具有Object.prototype。唯一的方法是使用Object.create(null)(不能使用new进行shim)。

我以为我需要设置base.prototype.constructor = base;

在这种情况下不需要。拥有function base(){...},将其"prototype"属性设置为{constructor: base}绝对不会改变任何内容(除了现在的“constructor”是可枚举的)- 每个函数都有这样一个默认的proto对象,包括"constructor"。
因此,只有在需要用另一个函数的原型让它继承时要重写"prototype"属性时,您可能会添加此便利属性:MySubClass.prototype = Object.create(MyClass.prototype, {constructor:{value:MySubClass}});

否则......

什么也不会发生。在原型对象上的"constructor"属性对于没有语言特性(如instanceof)是必需的,并且很少使用。很可能什么也不会出错。

谢谢 =)已编辑答案, 是的,在原始代码中它不起作用=) 我也不会理解为什么它应该在除构造函数之外的任何地方起作用,这在我的想法中似乎不正确。 - Moritz Roessler
好的,我已经回答了第一部分。希望稍后能够回答你所有的问题。 - Bergi
非常感谢 =)我很感激,这确实帮助我更好地理解JavaScript =) 是的没错,那是一个错误,我本来想做base.prototype.constructor,这种情况经常发生,大多数情况下在运行之前我都没有意识到。 把构造函数设置为构造函数本身是没有任何意义的(?) - Moritz Roessler
Object.extend不是一个本地函数,但你会在每个常见的库中找到它的一个版本... - Bergi
哦,好的 =)但是我可以设置Object.prototype.extend = function() {...},像你提到的那样使用for in循环 =)//这是否适用于任何类型的对象,因为在JavaScript中任何东西都是一个“Object”(是吗?)? - Moritz Roessler
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接