对JS里this的简单理解
对this的简单理解
-
this的绑定时间
123456var 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(); //shanconsole.log(b.name); //shan |
其实new关键字的实现原理也是涉及到了显式绑定方法,有兴趣的可以去了解一下。
-
this的指向
1、函数声明
12345function 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(); //shanconsole.log(b.name); //shanb.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对象。

浙公网安备 33010602011771号