JS中this的指向

1. 关于this

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象。

包括我在内的前端学习者在学习过程中可能都被this的指向搞蒙圈过;而且this在很多中不同的编程方式里略有不同,this在面向对象中尤为重要,比如可以帮助我们获取操作的节点等等。

2.一般函数中的this

function fn() {
    var str = "hello,world!";
    console.log(this.str); //undefined
    console.log(this); //Window
}
fn();
//window.fn();

我们在一般的函数中打印this,输出的window,根据定义我们可以理解成fn这个函数其实是被Window对象调用出来的,因此this便指向了window;

var obj = {
    str:"hello,world!",
    fn:function(){
        console.log(this.str);  //hello,world!
    }
}
obj.fn();

在这里fn函数有obj对象调用,因此this就指向了obj对象,从而可以输出对象里面的str;

var obj = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //12
        }
    }
}
obj.b.fn();

由这个多层嵌套的例子,我们可以进一步详细的理解为:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

3. 构造函数里的this

function Fn(){
    this.str = "hello,world!";
}
var fn = new Fn();
console.log(fn.str); //hello,world!

我们用new关键字和变量a创建了一个Fn()的实例化对象(相当于复制了一份Fn到对象fn里面),仅创建并没有执行,最终调用这个函数Fn的是对象fn,那么this指向的自然是对象fn,已经复制了一份Fn函数到对象fn中,因此对象fn中会有str,并可以打印;

4. 当this和return相遇

//例子1:
function Fn1()  
{  
    this.str = "hello,world!";
    return {};  
}
var fn1 = new Fn1;  
console.log(fn1.str); //undefined
//例子2:
function Fn2()  
{  
    this.str = "hello,world!"; 
    return function(){};
}
var fn2 = new Fn2;  
console.log(fn2.str); //undefined
//例子3:
function Fn3()  
{  
    this.str = "hello,world!"; 
    return 1;
}
var fn3 = new Fn3;  
console.log(fn3.str); //hello,world!

通过对比上述举例的结果,我们可以理解在有return的情况下,如果return返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象,那么this还是指向构造函数的示例化对象。

5. call和apply

/*apply和call调用*/
    let obj1 = {
        str: 'hello,world!',
        name: 'Tony'
    };
    let obj2 = {
        name: 'Frank',
        fn: function () {
            console.log(this.name);
        }
    }
    obj2.fn.call(obj1); //Tony
    obj2.fn.apply(obj1); //Tony
    obj2.fn(); //Frank

此时虽然是 obj2 调用方法,但是使用了call或者apply ,动态地把 this 指向变到了 obj1 ;相当于这个 obj2.fn 这个执行环境是 obj1 ;从而输出的name是obj1中的Tony,而不是obj2中的Frank;

call 和 apply 两个主要用途:

  1. 改变 this 的指向(把this从obj2指向到obj1);

  2. 方法借用(obj1没有fn ,只是借用obj2方法);

call与apply区别

  1. call和apply的作用完全一样,唯一的区别就是在参数上面;

  2. call 接收的参数不固定,第一个参数是函数体内this的指向,第二个参数以下是依次传入的参数;

  3. apply接收两个参数,第一个参数也是函数体内 this 的指向。第二个参数是一个集合对象(数组或者类数组);

    /*apply和call区别*/
        let fn = function (a, b, c) {
            console.log(a, b, c);
        }
        let arrArray = [1, 2, 3];
        fn.call(window, arrArray); //[1, 2, 3] undefined undefined
        fn.apply(window, arrArray); //1 2 3
    

6. bind

    let obj1 = {
        str: 'hello,world!',
        name: 'Tony'
    };
    let obj2 = {
        name: 'Frank',
        fn: function () {
            console.log(this.name);
        }
    }

    let a = obj2.fn.bind(obj1);
    a();

对比call和apply的例子,我们不难看出,bind也是改变this指向,但有所区别:call和apply都是即时调用,绑定既是调用,而bind不是,bind会返回绑定后的新函数,改变新函数的this指向,而后需要的时候再调用;

7. 箭头函数中的this

但是在箭头函数里面,没有this,因此箭头函数里面的this指向它的宿主对象。

    //例子1:
    let obj1 = {
        str: 'hello,world!',
        fn: function () {
            setTimeout(function () {
                console.log(this.str);
            })
        }
    }
    obj1.fn(); //undefined
    //例子2:
    let obj2 = {
        str: 'hello,world!',
        fn: function () {
            setTimeout(()=>{
                console.log(this.str);
            })
        }
    }
    obj2.fn(); //hello,world!

对比两个举例不难看出,第一个用的一般函数,因此指向window,打印window.str结果为未定义;第二个使用箭头函数,箭头函数里面没有this,便向上层作用域查找,在这个例子2中, setTimeout的上层作用域是fn,而fn里面的this指向obj2,所以setTimeout里面的箭头函数的this,指向obj2,因此能打印obj2中的str;

结尾:仅根据个人学习后的理解整理如上知识点,如有错误,请不吝赐教~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容