晴明的博客园 GitHub      CodePen      CodeWars     

[js] underscore 的 bind 与 bindAll , backbone 的 on 与 listenTo

apply

function Person(name) {
    this.name = name;
}
Person.prototype.say = function() {
    console.log(this.name);
}

var wenyi = new Person('wenyi');
wenyi.say(); // wenyi

var func = wenyi.say;
func(); // 空
//因为赋值给 func 后,这里的 this 指向的是 window,而 window.name 为空。

//为了改变 this 的指向,我们可以使用 apply 方法:
func.apply(wenyi); // wenyi

在实际的使用中,我们常常看到这样的代码:

$('button').click(function() {
    wenyi.say();
});

它并不美观,因为还得嵌套多一层 function 来解决 this 的指向问题.
像上面将 say 函数赋值给 func 变量后,转移的责任者并不能正确的使用原来的函数.

_.bind 、 _.bindAll

使用 _.bind 和 _.bindAll 函数可以很好的解决上面的问题:

        var func1 = _.bind(wenyi.say, wenyi);
        func1(); // wenyi

        _.bindAll(wenyi, 'say');
        var func2 = wenyi.say;
        func2(); // wenyi

自己实现bind与bindAll

        function bind(func, obj) {
            return function () {
                return func.apply(obj, Array.prototype.slice.call(arguments));
            };
        }
        function bindAll(obj) {
            var funcs = Array.prototype.slice.call(arguments, 1);
            for (var i = 0; i < funcs.length; i++) {
                obj[funcs[i]] = bind(obj[funcs[i]], obj);
            }
            return obj;
        }

一个使用示例

        function Person(name, age) {
            this.name = name;
            this.age = age;

            bindAll(this, 'sayName', 'sayAge');
        }
        Person.prototype.sayName = function () {
            console.log(this.name);
        }
        Person.prototype.sayAge = function () {
            console.log(this.age);
        }

        var wenyi = new Person('wenyi', 26);

        var func = wenyi.sayName;
        func(); // wenyi

        $('button').click(wenyi.sayAge); // 26

backbone中使用bind与bindAll

       //这样做的结果是change触发的是原this.render,方法中的this依然是不可性预计
        window.ProductView = Backbone.View.extrend({
            initialize: function () {
                _.bind(this.render, this);
                this.model.bind('change', this.render);
            }
        });

        //这是正确做法
        window.ProductView = Backbone.View.extrend({
            initialize: function () {
                var f_render = _.bind(this.render, this);
                this.model.bind('change', f_render);
            }
        });

        //这是正确做法,更直接简单:
        window.ProductView = Backbone.View.extrend({
            initialize: function () {
                this.model.bind('change', _.bind(this.render, this));
            }
        });

        //最简单当然是用_.bindAll:
        window.ProductView = Backbone.View.extrend({
            initialize: function () {
                _.bindAll(this, this.render);
                this.model.bind('change', this.render);
            }
        });

用listenTo的方式替代bind与bindAll

  (function ($) {

        var M = Backbone.Model.extend({
            defaults: {name: "hello"}
        });

        var V = Backbone.View.extend({

            //new V时,会跟这个视图的model绑定change事件,回调方法是视图的show方法
            initialize: function () {
                //给this.model绑定change事件。
                //listenTo方法跟on一样是绑定事件的,listenTo多一个参数,可以设置this的指向。
                this.listenTo(this.model, "change", this.show);
            },
            show: function (model) {
                console.warn(model.get("name"));
            }
        });

        var m = new M();

        var v = new V({model: m});

        //改变模型的name值时,就会触发change事件,在视图中弹出模型设置的name值。
        m.set("name", "hi");    //hi
        m.set("name", "bye");   //bye
    })(jQuery);

backbone中on与listenTo的区别

object.listenTo(other, event, callback)

object.on(event, callback, [context]) //别名: bind
view.listenTo(model, 'change', view.render)

//上面监听方法也等价于如下代码:
model.on('change', view.render, view)

其中,第三个参数为上下文环境对象,此时它的值为view,即model对象在触发change事件时,关联view对象进行执行view.render动作。
obj1.listenTo(obj2, eventName, function)

参数obj1,obj2都为对象,
参数eventName是obj2对象触发的事件名称,
参数function为当obj2触发指定的eventName事件时,
obj1所执行的自定义函数。
posted @ 2017-06-05 20:42  晴明桑  阅读(198)  评论(0编辑  收藏  举报