Backbone.js扩展与继承 #

一、extend方法 #

1.1 基本用法 #

javascript
var User = Backbone.Model.extend({
    defaults: {
        name: '',
        email: ''
    },
    
    initialize: function() {
        console.log('User created');
    }
});

1.2 extend原理 #

javascript
var extend = function(protoProps, staticProps) {
    var parent = this;
    var child;
    
    if (protoProps && _.has(protoProps, 'constructor')) {
        child = protoProps.constructor;
    } else {
        child = function() { return parent.apply(this, arguments); };
    }
    
    _.extend(child, parent, staticProps);
    
    var Surrogate = function() { this.constructor = child; };
    Surrogate.prototype = parent.prototype;
    child.prototype = new Surrogate;
    
    if (protoProps) _.extend(child.prototype, protoProps);
    
    child.__super__ = parent.prototype;
    
    return child;
};

1.3 实例属性与类属性 #

javascript
var User = Backbone.Model.extend({
    defaults: {
        name: ''
    }
}, {
    findByName: function(name) {
        return new this({ name: name });
    }
});

var user = User.findByName('张三');

二、继承模式 #

2.1 基本继承 #

javascript
var Animal = Backbone.Model.extend({
    defaults: {
        name: '',
        age: 0
    },
    
    speak: function() {
        console.log(this.get('name') + ' makes a sound');
    }
});

var Dog = Animal.extend({
    speak: function() {
        console.log(this.get('name') + ' barks');
    }
});

var dog = new Dog({ name: 'Buddy' });
dog.speak();

2.2 调用父类方法 #

javascript
var Animal = Backbone.Model.extend({
    initialize: function() {
        console.log('Animal initialized');
    }
});

var Dog = Animal.extend({
    initialize: function() {
        Animal.prototype.initialize.apply(this, arguments);
        console.log('Dog initialized');
    }
});

var dog = new Dog();

2.3 使用__super__ #

javascript
var Animal = Backbone.Model.extend({
    speak: function() {
        return 'sound';
    }
});

var Dog = Animal.extend({
    speak: function() {
        return Dog.__super__.speak.call(this) + ' - bark';
    }
});

var dog = new Dog();
console.log(dog.speak());

2.4 多层继承 #

javascript
var Base = Backbone.Model.extend({
    initialize: function() {
        console.log('Base');
    }
});

var Middle = Base.extend({
    initialize: function() {
        Middle.__super__.initialize.apply(this, arguments);
        console.log('Middle');
    }
});

var Child = Middle.extend({
    initialize: function() {
        Child.__super__.initialize.apply(this, arguments);
        console.log('Child');
    }
});

var child = new Child();

三、混入模式 #

3.1 基本混入 #

javascript
var TimestampMixin = {
    getTimestamp: function() {
        return new Date().toISOString();
    },
    
    setCreatedAt: function() {
        this.set('createdAt', this.getTimestamp());
    }
};

var User = Backbone.Model.extend({
    initialize: function() {
        this.setCreatedAt();
    }
});

_.extend(User.prototype, TimestampMixin);

3.2 多个混入 #

javascript
var TimestampMixin = {
    touch: function() {
        this.set('updatedAt', new Date().toISOString());
    }
};

var ValidationMixin = {
    validateRequired: function(fields) {
        var errors = [];
        fields.forEach(function(field) {
            if (!this.get(field)) {
                errors.push(field + ' is required');
            }
        }, this);
        return errors.length > 0 ? errors : undefined;
    }
};

var User = Backbone.Model.extend({
    validate: function(attrs) {
        return this.validateRequired(['name', 'email']);
    }
});

_.extend(User.prototype, TimestampMixin, ValidationMixin);

3.3 混入函数 #

javascript
function mixin(target) {
    var sources = Array.prototype.slice.call(arguments, 1);
    
    sources.forEach(function(source) {
        Object.keys(source).forEach(function(key) {
            target[key] = source[key];
        });
    });
    
    return target;
}

var User = Backbone.Model.extend({});

mixin(User.prototype, TimestampMixin, ValidationMixin);

四、自定义扩展 #

4.1 基类扩展 #

javascript
var BaseModel = Backbone.Model.extend({
    initialize: function(attrs, options) {
        this.options = options || {};
        this.on('change', this.onChange, this);
    },
    
    onChange: function() {
        console.log('Model changed');
    },
    
    fetch: function(options) {
        options = options || {};
        options.data = _.extend(options.data || {}, {
            timestamp: Date.now()
        });
        return Backbone.Model.prototype.fetch.call(this, options);
    }
});

var User = BaseModel.extend({
    defaults: {
        name: ''
    }
});

4.2 视图基类 #

javascript
var BaseView = Backbone.View.extend({
    initialize: function(options) {
        this.options = options || {};
        this.childViews = [];
    },
    
    render: function() {
        this.$el.html(this.template(this.getTemplateData()));
        this.afterRender();
        return this;
    },
    
    getTemplateData: function() {
        if (this.model) {
            return this.model.toJSON();
        }
        if (this.collection) {
            return { items: this.collection.toJSON() };
        }
        return {};
    },
    
    afterRender: function() {},
    
    addChild: function(view) {
        this.childViews.push(view);
        return view;
    },
    
    remove: function() {
        this.childViews.forEach(function(view) {
            view.remove();
        });
        this.childViews = [];
        
        Backbone.View.prototype.remove.call(this);
    }
});

4.3 集合基类 #

javascript
var BaseCollection = Backbone.Collection.extend({
    initialize: function(models, options) {
        this.options = options || {};
    },
    
    fetch: function(options) {
        options = options || {};
        
        this.trigger('fetch:start');
        
        var success = options.success;
        var error = options.error;
        var self = this;
        
        options.success = function() {
            self.trigger('fetch:success');
            if (success) success.apply(this, arguments);
        };
        
        options.error = function() {
            self.trigger('fetch:error');
            if (error) error.apply(this, arguments);
        };
        
        return Backbone.Collection.prototype.fetch.call(this, options);
    }
});

五、实用示例 #

5.1 可复用组件 #

javascript
var SelectableView = BaseView.extend({
    events: {
        'click': 'toggleSelect'
    },
    
    initialize: function(options) {
        BaseView.prototype.initialize.call(this, options);
        this.selected = false;
    },
    
    toggleSelect: function(e) {
        e.stopPropagation();
        this.selected = !this.selected;
        this.$el.toggleClass('selected', this.selected);
        this.trigger('select:toggle', this.selected);
    },
    
    select: function() {
        this.selected = true;
        this.$el.addClass('selected');
        this.trigger('select', true);
    },
    
    deselect: function() {
        this.selected = false;
        this.$el.removeClass('selected');
        this.trigger('select', false);
    }
});

5.2 状态管理 #

javascript
var StatefulModel = Backbone.Model.extend({
    initialize: function() {
        this.state = 'idle';
        this.previousState = null;
    },
    
    setState: function(newState) {
        this.previousState = this.state;
        this.state = newState;
        this.trigger('state:change', newState, this.previousState);
        this.trigger('state:' + newState);
    },
    
    getState: function() {
        return this.state;
    },
    
    isState: function(state) {
        return this.state === state;
    }
});

5.3 插件系统 #

javascript
var PluginSystem = {
    plugins: [],
    
    use: function(plugin, options) {
        this.plugins.push({
            plugin: plugin,
            options: options || {}
        });
        
        if (plugin.install) {
            plugin.install(this, options);
        }
    },
    
    initializePlugins: function() {
        this.plugins.forEach(function(item) {
            if (item.plugin.initialize) {
                item.plugin.initialize(this, item.options);
            }
        }, this);
    }
};

var AppModel = Backbone.Model.extend({
    initialize: function() {
        _.extend(this, PluginSystem);
        this.initializePlugins();
    }
});

六、总结 #

6.1 扩展方法 #

方法 说明
extend(protoProps, staticProps) 创建子类
super 访问父类原型
_.extend 混入属性

6.2 最佳实践 #

  1. 使用基类封装通用逻辑
  2. 合理使用继承层次
  3. 使用混入实现代码复用
  4. 调用父类方法时保持上下文
  5. 避免过深的继承层次
最后更新:2026-03-28