首页 > 代码库 > 【javaScript】早绑定和迟绑定

【javaScript】早绑定和迟绑定

javaScript会在调用时会设置执行上下文“this”的值。

一些经常使用的错误例子

我们举一个例子,给一个Menu构造函数,来接受一个元素来创建一个菜单。

function Menu(elem){

    //...

}

//使用
var elem = document.getElementById(‘something‘) // a DOM element
var menu = new Menu(elem);

当我们在构造函数里调用setTimeout时,你也许想引用这个menu对象,我们可能会像如下这样做:

function Menu(elem){

    setTimeout(function(){
        alert(this);    //window, not menu!

    }, 1000);

}

new Menu(document.createElement(‘div‘));
但是this指向的是window,因为setTimeout总是在window上下文环境中执行,再看下面的例子:

function Menu(elem){

    elem.onclick = function(){
        alert(this);    //elem, not menu!
    }

}
元素事件处理器将this设置为指向elem而不是menu。

私有方法或本地函数:本地函数经常被用来作为私有方法。

function Menu(elem){

    function privateMethod(){
        alert(this);    //window,not menu!
    }

    //调用私有方法
    privateMethod();

}

new Menu(document.createElement(‘div‘));
调用私有方法,this也是指向window。

用var self = this来绑定

首先,我们可以在闭包中存储this。

下面的例子中,将this赋值给self,用self来代替this的使用。

function Menu(elem){
    var self = this;
    setTimeout(function(){
        alert(self);    //object, menu!
    }, 1000);
}

new Menu(document.createElement(‘div‘));


早绑定

我们需要用到一个帮助函数bind来强制绑定到this。

function bind(func, fixThis){
    return function(){
        return func.apply(fixThis, arguments);
    }
}

function Menu(elem){
    elem.onclick = bind(function(){
        alert(this);    //object!(menu) 
    },this);
}

其实原生的方法Function.prototype.bind已经实现了,目前大多数浏览都支持,如果不支持我们也可以进行模拟。

Function.prototype.bind = Function.prototype.bind || function(fixThis){

    var func = this;
    return function(){
        return func.apply(fixThis, arguments);
    }
}

能有些人觉得这样做污染了原生的属性,不推荐这种用法。但是改变Function.prototype有时也是能够接受的。它不会影响对象或数组的迭代,副作用非常小的。

如果用上面这种方法,那会简单很多。

setTimeout

function Menu(elem) {
 
  setTimeout(function() {
    alert(this)  // object! (menu)
  }.bind(this), 1000)
 
}
 
new Menu(document.createElement(‘div‘))

onclick

function Menu(elem) {
 
  elem.onclick = function() {
    alert(this)  // object! (menu)
  }.bind(this)
 
}

私有方法或本地方法

有点问题,在函数声明时不能调用bind,在函数表达式时是可以的。

function Menu(elem) {
  function privateMethod() {
    alert(this);
  }.bind(this); // syntax error, can‘t call method in-place
}

function Menu(elem) {
  var privateMethod = function() {
    alert(this);
  }.bind(this);
 
  privateMethod();
}
 
new Menu(document.createElement(‘div‘));


迟绑定

迟绑定的意思就是在调用时进行绑定,而早绑定是及时绑定,也就是在函数声明时就已经确定好了。

早绑定的问题

以下例子我们点击body后我们发现输出的不是"SuperMenu",而是"Menu"。

<!DOCTYLE HTML>
<html>
<body>
<script>
function bind(func, fixThis) {
    return function() {
        return func.apply(fixThis, arguments);
    }
}

function Menu(elem) {

    this.sayHi = function() { alert(‘Menu‘); }

    elem.onclick = bind(this.sayHi, this);
}

function SuperMenu(elem) {
    Menu.apply(this, arguments);

    this.sayHi = function() { alert(‘SuperMenu‘); }
}

new SuperMenu(document.body);
</script>
</body>
</html>

真正的晚绑定

这次我们看到了我们想要的结果,输出的是“SuperMenu"。关键一步是:fixThis[funcName]是动态从去获取对象的属性,当运行时改变该属性时也能正确执行调用。其实如果你目前用的是jquery库的话,$.proxy已经实现了早绑定和迟绑定,具体可以查看相关API。

<!DOCTYLE HTML>
<html>
<body>

<script>
function bindLate(funcName, fixThis) {
    return function() {
        return fixThis[funcName].apply(fixThis, arguments);
    }
}

function Menu(elem) {

    this.sayHi = function() { alert(‘Menu‘); }
    elem.onclick = bindLate(this.sayHi, this);
}

function SuperMenu(elem) {

    Menu.apply(this, arguments)
    this.sayHi = function() { alert(‘SuperMenu‘); }
}

new SuperMenu(document.body);

</script>
</body>
</html>