js中this指向的绑定例外

上一篇文章js中this指向全面解析——四种绑定规则讲了this指向哪里,
今天就来说一说四种绑定规则也有例外的情况,还有关于ES6箭头函数中this的问题
本文讲述两个内容:绑定例外、this词法

绑定例外

一、被忽略的this

null或者undefined 作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:(不懂默认绑定可以看上一篇文章)

function foo() {
    console.log( this.a );
}
var a = 2;
foo.call( null ); // 2

什么时候会把null或者undefined传给this呢?
如果函数并不关心this,但是仍然需要传入一个占位置,那么可以传入

介绍一种非常有用的方法,bind(…)可以对参数进行柯里化(预先设置一些参数)

function foo(a,b) {
    console.log( "a:" + a + ", b:" + b );
}
// 把数组“展开”成参数
foo.apply( null, [2, 3] ); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind( null, 2 );
bar( 3 ); // a:2, b:3

然而,总是使用 null 来忽略 this 绑定可能产生一些副作用。如果某个函数确实使用了this(比如第三方库中的一个函数),那默认绑定规则会把 this 绑定到全局对象(在浏览 器中这个对象是 window),这将导致不可预计的后果(比如修改全局对象)。

更安全的this

更安全的做法,创建一个“DMZ”对象——一个空的非委托的对象
创建一个空对象最简单的方法是Object.create(null),可以用任何名字来命名DMZ对象
object.create(null)和{ }很像,但是不会创建Object.prototype 这个委托,所以它比 {}“更空”

function foo(a,b) {
    console.log( "a:" + a + ", b:" + b );
}
// 我们的 DMZ 空对象
var ø = Object.create( null );
// 把数组展开成参数
foo.apply( ø, [2, 3] ); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind( ø, 2 );
bar( 3 ); // a:2, b:3

使用变量名ø可以让函数变得更加“安全”,提高代码可读性

二、间接引用

函数的间接引用时,调用这个函数会应用默认绑定规则

间接引用最容易在赋值时发生:

function foo() {
    console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2

(p.foo = o.foo)();
这句语句实际上调用的是foo(),不是p.foo 或者 o.foo 。
所以这里应用的是默认绑定

三、软绑定

硬绑定可以把this强制绑定到指定的对象(除了使用new时但是硬绑定之后无法用隐式绑定显式绑定的方法修改this,大大降低了函数的灵活性。

可以通过软绑定 的方法,该方法实现了和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改this的能力


if (!Function.prototype.softBind) {
    Function.prototype.softBind = function(obj) {
        var fn = this;
       // 捕获所有 curried 参数
        var curried = [].slice.call( arguments, 1 );
        var bound = function() {
        return fn.apply(
            (!this || this === (window || global)) ?
            obj : this
            curried.concat.apply( curried, arguments )
        );
    };
    bound.prototype = Object.create( fn.prototype );
    return bound;
    };
}

它会对指定的函数进行封装,首先检查调用时的 this,如果 this 绑定到全局对象或者 undefined,那就把指定的默认对象 obj 绑定到 this,否则不会修改 this

function foo() {
        console.log("name: " + this.name);
}

var   obj = { name: "obj" },
        obj2 = { name: "obj2" },
        obj3 = { name: "obj3" };

var fooOBJ = foo.softBind( obj );
fooOBJ(); // name: obj

obj2.foo = foo.softBind(obj);          //使用了隐式绑定
obj2.foo(); // name: obj2 <---- 看!!!

fooOBJ.call( obj3 ); // name: obj3 <---- 看! 使用了显示绑定

setTimeout( obj2.foo, 10 ); //实际上是传递了 foo ,使用了默认绑定

// name: obj <---- 应用了软绑定

分析一下,
var fooOBJ = foo.softBind( obj ); 使用了软绑定把foo的obj绑定到this上
obj2.foo = foo.softBind(obj); 首先使用了软绑定把obj绑定带this上,后来obj2.foo 应用了隐式绑定把obj2绑定到了this
fooOBJ.call( obj3 ); 将原来使用软绑定把this指向obj修改成指向obj3 这里应用的是显式绑定
setTimeout( obj2.foo, 10 ); 将obj2.foo作为参数,实际上是 foo 作为参数传递给setTimeout函数,绑定丢失了obj2对象

this词法

es6中的箭头函数无法使用四种标准规则,根据外层(函数或者全局)作用域来决定this,而且箭头函数被绑定后无法修改

function foo() {
    var a=5;
    // 返回一个箭头函数
    return (a) => {
    //this 继承自 foo()
    console.log( this.a );
};
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var a=4;

var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是 3

var b=foo();
b(); //4

分析:
foo()内部创建的箭头函数的this会指向foo函数的this。
var bar = foo.call( obj1 ); 由于 foo() 的 this 绑定到 obj1,bar(引用箭头函数)的 this 也会绑定到 obj1,
bar.call( obj2 ); 箭头函数的绑定无法被修改。

var b=foo(); foo()的this指向全局的a应用默认绑定
b(); //4

箭头函数常用于回调函数中,例如事件处理器或者定时器

function foo() {
    setTimeout(() => {
        // 这里的 this 在此法上继承自 foo()
        console.log( this},100);
    }var obj = {
    a:2
};
foo.call( obj ); // 2

es6之前跟箭头函数完全一样的模式:

function foo() {
    var self = this; // lexical capture of this
    setTimeout( function(){
        console.log( self.a );
    }, 100 );
}
var obj = {
    a: 2
};
foo.call( obj ); // 2

如果你经常编写 this 风格的代码,但是绝大部分时候都会使用 self = this 或者箭头函数
来否定 this 机制,那你或许应当:

  1. 只使用词法作用域并完全抛弃错误 this 风格的代码;
  2. 完全采用 this 风格,在必要时使用 bind(…),尽量避免使用 self = this 和箭头函数。

箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这其实和 ES6 之前代码中的 self = this 机制一样。

这篇文章是在看《你不知道的JavaScript》(上卷) this绑定例外之后再加上自己的一些尝试和理解写下的读书笔记。
参考:《你不知道的JavaScript》(上卷)

猜你喜欢

转载自blog.csdn.net/i_ViOLeT_i/article/details/86374248