对JS里this的简单理解

对this的简单理解

  • this的绑定时间

    1
    2
    3
    4
    5
    6
    var length=10;
        function f(){
            var length=20;
            console.log(this.length);
        }
        f();   //10

    ​ 我们以这个简单的代码块为例子,可以看到this在方法f里面,那么this在什么时候绑定呢?顺着代码来,可以看到,我们这里是一个声明的函数方法(函数定义的三种方法之一)。先声明,然后分配地址空间(初始化),再定义函数体,那么this是在定义函数体后就绑定了吗,看结果,显然不是,那么this只会是在被调用的时候才绑定的。

    ​ 我们接着深度剖析一下,为什么是在调用时也就是运行时绑定的呢?

    ​ 我们知道JS是一个脚本语言,很多东西都是简化的,JS在声明函数方面,把构造函数/方法/lambda/全局函数等隐式地混合在一起了。

    ​ 我们这里要知道——当声明方法时需要找到对应的构造函数,让这个方法内部的 this 指向构造函数生成的实例;当声明构造函数的时候,this 则指向构造函数生成的这个实例;当声明 lambda 表达式的时候,不存在 this 指向,这时候 this 应按照闭包的规程逐次向上层作用域找,没找到则抛出异常

    ​ 在函数声明的时候,JS是不能通过静态分析去确定这到底是构造函数还是lambda等等,所以只能在动态调用方法时去绑定this。

    ​ 额外提一点,在lambda方面,JS在ES6也是引入了箭头函数,有兴趣的可以了解一下。

  • this的绑定方式

    ​ 一共有四种绑定方式(优先级从低到高),我们一一介绍。

    1、默认绑定

    ​ 我们知道了this是在调用时绑定的,那么绑定谁呢也就是说指向谁呢?具体的在下一部分讲。

    ​ 很容易理解,默认绑定就是说this会有一个默认的绑定对象。

    2、隐式绑定

    ​ 这里也容易理解,this隐式地改变了原来的默认绑定。简单来说,就是偷偷地改变了绑定对象,不再是默认的。

    3、显式绑定

    ​ 既然是显式的,那么就不再是偷偷的了,而是明目张胆地用一些方法去改变this绑定的对象。那么是哪些方法呢,这里简单介绍一下:call,apply,bind。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a={
        name:"shan",
        age:21,
        sex:"male"
    }
var b={
    name:"sun",
    getName:function (){
        console.log("name:"+this.name+
            "  age:"+this.age);
    }
}
 
b.getName.call(a);   //name:shan  age:21

​ 可以看到call把函数里this的绑定对象从b转到了a,并且执行了该函数。call和apply的区别就是,call后面的参数是一个参数队列,apply后面则是合在一起的一个数组;至于bind,它和call的区别就是不会执行函数,而是返回一个新函数对象。

4、new绑定

​ new关键字是用来实例化的,this自然是指向实例化后的实例对象。

1
2
3
4
5
6
7
8
class gn{
    name="shan";
    constructor() {
        console.log(this.name);
    }
}
var b=new gn();      //shan
console.log(b.name); //shan

​ 其实new关键字的实现原理也是涉及到了显式绑定方法,有兴趣的可以去了解一下。

  • this的指向

    1、函数声明
    1
    2
    3
    4
    5
    function f(){
        var length=100;
        console.log(this.length);
    }
    f();   //undefined

​ 无对象调用时,this是一个默认绑定,指向的是默认的全局对象也就是window,还有下面这种情况:

1
2
3
4
5
6
var length=10;
function f(){
    var length=100;
    console.log(this.length);
}
f();   //10

​ 这里涉及了一个数据声明的问题,有兴趣的可以去学习一下。

2、有对象调用
1
2
3
4
5
6
7
var a={
    name:"shan",
    getName:function(){
        console.log(this.name);
    }
}
a.getName();   //shan

​ 有对象调用时,this隐式绑定了该对象。所以指向该对象,如上,返回的是shan。如下,可以对比一下:

1
2
3
4
5
6
7
var name="dan";
var a={
     getName:function(){
         console.log(this.name);
     }
 }
 a.getName();    //undefined

​ 可见,this绑定的不再是默认的window对象。

3、函数表达式
1
2
3
4
5
var name="shan";
var a=function(){
    console.log(this.name)
}
a();

​ 这个也是无对象调用,所以是默认绑定,指向window。

4、实例化调用

​ 这里参考上方的new绑定,this指向的就是实例化后的对象

1
2
3
4
5
6
7
8
var a=function(){
    this.name="shan";
    console.log(this.name);
}
var b=new a();            //shan
console.log(b.name);    //shan
b.name="dan";
console.log(b.name);    //dan
5、call、apply调用

​ 这里参考上方的显式绑定,call、apply其实是一种上下文绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
var a={
    name:"shan",
    age:21,
    sex:"male"
}
var b={
    name:"sun",
    getName:function (){
        console.log("name:"+this.name+
            "  age:"+this.age);
    }
}
b.getNmae.apply(a);   //name:shan  age:21
6、箭头函数
1
2
3
4
5
6
7
var a={
    name:"dan",
    getName:function (){
        setTimeout(()=>console.log(this.name),100)
    }
}
a.getName();

​ 箭头函数本身是没有this关键字的,但是它会继承父环境,在这里体现出来的就是它继承了getName函数的this,指向了a对象。

posted @ 2020-10-25 22:08  YSLstudy  阅读(80)  评论(0)    收藏  举报