this的指向问题

this是JavaScript中的一个关键字,不是变量,也不是属性,它不能被赋值。
在绝大多数情况下,它的值取决于函数的调用方式。当然javascript中也有一些方式可以设置this。本文总结下this的指向问题。

函数调用时

这里的函数调用指的是,函数表达式是普通函数,而不是对象的属性。例如 x(a1,a2) 此类。

此时如果是非严格模式,函数中的this就会指向window/全局对象;严格模式时,会是undefined

    function normal() {
        console.log(this);
    }

    // 非严格模式
    normal() // window

    // 严格模式
    normal() // undefined

所以,可以使用普通函数中的this关键字来判断当前是否是严格模式

    const STRICT = (function(){return !this;}());

方法调用时

方法调用指的是函数表达式是对象的属性时。例如:a.x(a1,a2)此类。

此时方法中的this指向调用该方法的对象,函数体中可以通过this引用该对象。

    let obj = {
        name:'zhao',
        age:18,
        say: function(){
            console.log(this);
            console.log(this.name);
        }
    }

    obj.say(); // obj
               // 'zhao'

函数say()是作为对象obj的方法调用的,this指向obj

    // 对temp赋值
    let temp = obj.say;
    temp();   // window
              // undefined

temp赋值后,虽然和obj.say是同一个函数体,但是却是普通函数,此处this指向的判断方式和函数调用时一致。此处是非严格模式,所以指向window

    let _obj = {
        name:'qian',
        _say:obj.say // _say的函数表达式和say一致
    }

    _obj.say(); // _obj
                // 'qian'

同样是同一个函数体,此处由_obj调用的,this指向_obj

this没有作用域限制

this关键字没有作用域的限制,所以嵌套函数不会使用函数中的this

    let obj = {
        name:'zhao',
        say: function(){
            console.log(this);

            test();

            function test() { // 新增一个嵌套函数
                console.log(this)
            }
        }
    }

    obj.say() // obj
              // window

testsay函数体中是作为函数调用的,所以指向window或者undefined(严格模式下),和say函数体中的this没有任何关系,还是按照嵌套函数的调用方式判断

构造函数调用时

当函数或者方法调用之前带有关键字new,那就是构造函数调用。例如:new a()

构造函数执行时,this指向正在构造的新对象;但是需要注意:

虽然构造函数默认返回this指向的对象,但是也可以手动返回其他对象(如果返回值不是一个对象,则返回this对象)

    let temp;
    function Person(name) {
        this.name = name;
        temp = this; // 暂存this
    }

    let x = new Person('name1');
    console.log(x === temp); // true

Person在此次执行时,this指向了它正在构造的对象x

    let _temp;
    function _Person(name) {
        this.name = name;
        _temp = this; // 暂存this
        return { // 返回指定的对象
            name:'name999'
        }
    }

    let _x = new _Person('name2');
    console.log(_x === _temp); // false

在构造函数中返回指定的对象,此时的this就不会指向最后生成的对象

事件处理函数

在作为DOM的事件处理函数时,this指向触发事件的元素

    let btn = document.querySelector('#target');

    btn.addEventListener('click', function(e){
        console.log(this === e.currentTarget); 
    },false)

触发事件时,会输出true,可见this指向了触发元素

设置this指向

JavaScript中有多个方式可以显示的设定所需的this值。

call()和 apply()

callapply都可以设定函数执行时this的指向

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数
call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

applycall的作用和使用方式都类似,唯一的差别就是传递参数不一样

    // 使用方式
    func.apply(thisArg, [argsArray])   //参数数组
    fun.call(thisArg, arg1, arg2, ...) //参数列表

指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象


    let person = {
        name:'shun',
        say :function(){
            console.log(this.name);
        }
    }

    person.say(); //'shun'
    person.say.call({name:'li'}); // 'li'
    person.say.apply({name:'zhou'}); // 'zhou'

上例中person.say在执行时,this指向了不同的对象。

bind()

bind也用于指定函数的this和参数,且会返回一个新的函数。新函数在执行时会使用指定的this和参数

    this._name = 'lei';

    function say() {
        console.log(this._name);
    }

    let _say = say.bind({_name:'zhang'}); // 返回一个新的函数,绑定了this到指定的对象

    say();  // 'lei' 普通调用,非严格模式下的this指向window
    _say(); // 'zhang' this指向 {_name:'zhang'}

箭头函数

在箭头函数中,this与封闭词法上下文的this保持一致。在全局代码中,它将被设置为全局对象

箭头函数中的this无法通过call、bind、或者apply改变,传入的this值会被忽略,它的this值始终是它被创建时的上下文

    let g,temp,j;

    obj = {
        say : function () {
            g = this;
            return (() => this);
        }
    }

    temp = obj.say();
    j = temp();

    console.log(g === obj,j ===obj); // true,true

obj.say()执行时,say中this指向obj,也就是在此时创建了箭头函数,并会返回赋值给temp,temp()是作为普通函数调用(按照之前的判断来说,应该指向window),但是由于它是箭头函数,this值为创建时的上下文,所以也this也指向obj。再看下面的情况,换一种调用方式

    let x = {name:'xi'}
    // 改变箭头函数在创建时,say中的this指向为x
    temp = obj.say.call(x);
    
    j = temp();
    console.log(j); // {name:'xi'}

箭头函数的this还是指向创建时的上下文,这里是对象x

    // 接上代码
    // 尝试用call改变this指向
    j = temp.call({name:'call'});
    console.log(j); // {name:'xi'} 改变无效

尝试用call改变箭头函数的this指向,当然是无效的。

posted @ 2018-12-19 12:42  Shapeying  阅读(542)  评论(0编辑  收藏  举报