学习JavaScript"原型"应用

342 阅读3分钟

我们已经了解了原型的基本机制,[[Prototype]]就是存在于对象中的一个内部链接,它会引用其他 对象。

对象关联

之前我们说过Object.create(..),现在我们来用他创建一个对象的关联:

var foo = {
    sayName: function(){
        console.log("tom");
    }
};

var bar = Object.create(foo);

bar.sayName(); // "tom"

我们并不需要通过“类”的方式来实现关联,只需要通过这种委托的方式就能关联两个对象的关系,而且Object.create(..)不包含任何“副作用”,是确定两个对象关联的最佳方式。本例中foo对象关联到bar对象上,这样bar对象就能使用foo中的方法和属性。

看起来对象之间的关联关系是处理“缺失”属性或者方法时的一种备用选项。由于存在[[Prototype]] 机制,这段代码可以正常工作。但是如果这样写只是为了让bar在无法处理属性或者方法时可以使用备用的foo,那么代码就会变得难以理解和维护。

为了符合我们设计软件的思维方式和可以维护理解的角度,我们稍微修改一下代码:

var foo = {
    sayName: function(){
        console.log("tom");
    }
};

var bar = Object.create(foo);

bar.doneName = function() {
  this.sayName(); //在bar内部委托  
};

bar.doneName(); // "tom"

这样我们就是直接使用bar对象上的方法,从内部完成了委托,让我们的API设计更加清晰。

行为委托

现在我们试着把思路从类和继承的设计模式转换到行为委托的设计模式。

var foo = {
    setName: function(id) {
        this.id = id;
    },
    getName: function() {
        console.log(this.id);
    }
}

var bar = Object.create(foo);

bar.setFoo = function(id) {
    this.setName(id);
}

bar.speak = function () {
    this.getName();
}

这段代码中,foobar它们都是对象,bar通过Object.create(..)使[[prototype]]委托到foo对象上。

行为委托意味着某些对象(bar)在找不到属性或方法时,会把这个请求委托给另个对象(foo)。

利用委托来写编写一个控件对象:

var Widget = {
    init: function(width,height){
        this.width = width || 50;
        this.height = height || 50;
        this.$elem = null;
    },
    insert: function($where){
        if (this.$elem) {
            this.$elem.css({
            width: this.width + "px",
            height: this.height + "px"
            }).appendTo( $where );
        }
    }
};
var Button = Object.create(Widget);

Button.setup = function(width,height,label){
    // 委托调用
    this.init( width, height );
    this.label = label || "Default";
    this.$elem = $( "<button>" ).text(this.label);
};
Button.build = function($where) {
    // 委托调用
    this.insert( $where );
    this.$elem.click(this.onClick.bind( this ));
};
Button.onClick = function(evt) {
    console.log("Button '" + this.label + "' clicked!");
};
$( document ).ready( function(){
    var $body = $ document.body);
    var btn1 = Object.create(Button);
    btn1.setup(125, 30, "Hello" );
    var btn2 = Object.create(Button);
    btn2.setup(150, 40, "World");
    btn1.build($body);
    btn2.build($body);
});

使用对象关联风格来编写代码时不需要把WidgetButton当作父类和子类。相反,Widget只是一个对象,包含一组通用的函数,任何类型的控件都可以委托,Button同样只是一个对象。从语法角度来说,我们同样没有使用任何构造函数、.prototypenew,实际上也没必要使用它们。对象关联可以更好地支持关注分离原则。

小结

行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。JavaScript[[Prototype]]机制本质上就是行为委托机制。对象关联是一种编码风格,它倡导的是直接创建和关联对象,不把它们抽象成类。对象关联可以用基于[[Prototype]]的行为委托非常自然地实现。

参考