美文网首页
两个方法真正掌握js中的this

两个方法真正掌握js中的this

作者: 一行代码解决不了问题那就写两行 | 来源:发表于2017-01-02 13:36 被阅读0次

    作为一个jser,编码生涯中最经常碰到的一个问题就是this指向问题,在看了N多资料后,大家得出了一个结论:this总是指向函数的调用者。当时看到这个答案的时候,我的想法是“我擦,终于有一个标准了”,但是在编码实践中,却发现这句标准很难应用起来,甚至不是准确的。

    最近在看了《你不知道的js》(You-Dont-Know-JS)之后,对this有了更加准确和完整的认识,现总结出来分享给大家。

    结论:掌握this的两个方法

    1 心法:this的指向是在运行时决定的

    2 操作方法:函数调用时,前面是否有对象修饰,有,则一般this指向该对象,无,则一般指向window(在严格模式下,为undefined)

    • 方法二的一般情况,指的是函数内部没有apply、call、bind、new、es6的箭头函数等,一般情况已经足以帮我们分辨this了,对于其他情况,由于有明确的指定this的操作,我们反而不容易被迷惑

    接下来详细讲解两个方法

    1 this的指向是在运行时决定的

    对于this,通常有两个错误认识

    1.1 错误认识:this指向function 本身

    function foo(num) {
        console.log( "foo: " + num );
    
        // keep track of how many times `foo` is called
        this.count++;
    }
    
    foo.count = 0;
    
    var i;
    
    for (i=0; i<10; i++) {
        if (i > 5) {
            foo( i );
        }
    }
    // foo: 6
    // foo: 7
    // foo: 8
    // foo: 9
    
    // how many times was `foo` called?
    console.log( foo.count ); // 0 -- WTF?
    

    可以看到foo.count并没有自增,说明this并不指向函数本身

    1.2 错误认识:this指向function 的作用域

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

    写出这段代码的同学,可能是想通过this来创建foo()bar() 作用域之间的桥梁,这样bar()就可以访问foo()中的变量a了,不过这样连接的桥梁不可能的。

    1.3 小结

    this不是编码阶段绑定的,而是在运行时绑定,指向的是由函数调用来决定的上下文环境

    2 this指向规则

    2.1 默认绑定

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

    独立函数调用时,如果没有其他规则,则启动默认规则,this指向全局对象,在浏览器环境中就是window对象,如果在严格模式,this指向undefined.

    2.2 隐式绑定

    function foo() {
        console.log( this.a );
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    obj.foo(); // 2
    

    隐式绑定有两点需要注意

    2.2.1 只有最后一层对象属性的引用链才决定函数调用中的this
    function foo() {
        console.log( this.a );
    }
    
    var obj2 = {
        a: 42,
        foo: foo
    };
    
    var obj1 = {
        a: 2,
        obj2: obj2
    };
    
    obj1.obj2.foo(); // 42
    
    2.2.2 隐式绑定丢失
    function foo() {
        console.log( this.a );
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    var bar = obj.foo; // function reference/alias!
    
    var a = "oops, global"; // `a` also property on global object
    
    bar(); // "oops, global"
    

    这点尤其需要注意,var bar = obj.foo;一行赋值语句就会造成this的丢失。
    我们日常写的回调函数和setTimeout函数,经过形参传递、赋值,都会造成引用丢失,这里一定要注意

    function foo() {
        console.log( this.a );
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    var a = "oops, global"; // `a` also property on global object
    
    setTimeout( obj.foo, 100 ); // "oops, global"
    function setTimeout(fn,delay) {
        // wait (somehow) for `delay` milliseconds
        fn(); // <-- call-site!
    }
    

    2.3 显式绑定

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

    显式绑定指的就是,通过call 或apply,以及bind 方法来制定this

    2.4 通过new 绑定

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

    我们知道通过new 调用function时,实际上编译器在内部先创建了一个object,然后把this指向该object,最后在隐含的return出来,所以在执行期间,this就是指向该新创建的对象

    3 规则生效优先级

    下面通过伪代码,来说明绑定规则

    switch(rule){
        case ‘函数通过new来调用’:
            var bar = new foo()
            this => 新创建的对象;
            break;
        case ‘函数通过call apply bind等来调用’: 
            var bar = foo.call( obj2 )
            this => 该明确指向的对象;
            break;
        case ‘函数通过上下文对象属性方法来调用’: 
            obj1.foo()
            this => 该上下文对象
            break;
        default ‘默认绑定’: 
            var bar = foo()
            非严格模式:this => 全局对象;
            严格模式: this=> undefined;
            break;
                                    
    }
    

    4 绑定的异常情况

    4.1 忽略this

    如果把nullundefined,作为this的参数传递给call,applybind,那这个值将会被忽略,而启用默认绑定规则

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

    4.2 ES6 箭头函数

    箭头函数不同于上述4条规则,把this绑定在当前函数作用域中,且不能被其他调用方式改变!

    function foo() {
        // return an arrow function
        return (a) => {
            // `this` here is lexically adopted from `foo()`
            console.log( this.a );
        };
    }
    
    var obj1 = {
        a: 2
    };
    
    var obj2 = {
        a: 3
    };
    
    var bar = foo.call( obj1 );
    bar.call( obj2 ); // 2, not 3!
    

    总结

    两个方法

    1 心法:this的指向是在运行时决定的

    2 操作方法:函数调用时,前面是否有对象修饰,有,则一般this指向该对象,无,则一般指向window(在严格模式下,为undefined)

    四种规则

    new > call apply bind > 上下文 > 默认

    特殊情况

    箭头函数绑定到函数作用域
    上下文隐式绑定,注意this丢失

    相关文章

      网友评论

          本文标题:两个方法真正掌握js中的this

          本文链接:https://www.haomeiwen.com/subject/piywvttx.html