在JavaScript中this的概念较为抽象,一共可以分为五种场景:
- 默认绑定
- 隐式绑定
- 显式绑定
- new绑定
- 箭头函数绑定
先讲一下this的概念吧,this实际上是在函数被调用的时候才发生的绑定,也就是说this具体指向什么,取决于你怎么调用函数。
而apply和call是属于函数应用,指定this的同时也执行方法,bind则不同,它只是负责绑定this并返回一个新的方法,不会执行,所以我们经常看到bind后面会加一个()。
先就解释这么多,正文开始。
this的默认绑定
这么说,在函数调用没有任何前缀的时候,就属于this默认绑定。
在非严格模式时,默认绑定this指向全局对象。
function fn3() {
console.log(this); //window
};
f3();
在这段代码中,函数调用没有任何前缀,没有指定任何对象,那么this指向全局对象window。
注意:在严格模式下,默认绑定的this指向undefined,一试便知:
function fn1() {
console.log(this); //window
};
function fn2() {
"use strict";
console.log(this); //undefined
};
fn1();
fn2();
上面这个例子是在fn2的本地执行上下文里面使用"use strict",如果说是fn2函数外面使用严格模式将函数包裹住,那么与使用在里面的效果是相同的。
但是要注意,比如一个函数里是设置了严格模式,另外一个函数没有设置,在已经设置了严格模式的函数里面调用了没有严格化的函数,那么并不会影响这个被调用函数的this指向。
this的隐式绑定
在函数被调用之前,如果前面存在调用它的对象,那么this就会隐式绑定到这个对象上。
function fo() {
console.log(this.name);
};
let obj = {
name: 'henu_GM',
fo: fo
};
obj.fo();
如果函数调用前存在多个对象,那么this指向距离自己调用最近的对象,采取就近原则。
function fo() {
console.log( this.a );
}
var a = 7;
var obj1 = {
a: 4,
fo: fo
};
var obj2 = {
a: 3,
obj1: obj1
};
obj2.obj1.fo(); //4
注意,在特定情况下会发生隐式绑定丢失的问题,其中最主要的两个问题,一,将含this的函数作为参数传递给了另一个函数,这里的this并没有与之前那个函数绑定,而是与调用之前那个函数的对象隐式绑定,所以这里this脱离了绑定对象,this指向window;二,对已经隐式绑定了的函数进行变量赋值,在用新的变量调用这个函数,也会造成this指向window。
就这么说吧,把函数作为参数进行传递给其他函数或者对象,如果接收的是一个新的对象,那么这个函数的this就指向新的对象,如果接收的是函数一类的,那么就直接指向window,很清楚了吧。
this的显式绑定
显式绑定不同于隐式,显式绑定是指我们通过call,apply以及bind方法改变this的行为。
通俗来讲,call和appply让函数从被调用变为主动选择自己的上下文,称为函数应用。
需要注意的是,无论是call还是apply,亦或是bind,如果选择的参数是undefined或者null那么this将指向全局变量。
下文将具体讨论call,apply以及bind对于this指向的作用。
this的new绑定
JavaScript中的构造函数相信都不陌生,它只是使用new调用的一个普通函数,而并不是一个类,最终返回的对象也不是一个实例。
那么new一个函数究竟发生了什么:
- 以构造器的prototype属性为原型,创建对象。
- 将this和调用参数传给构造器。
- 如果构造器没有手动返回对象,则返回第一步创建的对象。
则叫做,构造调用。
那么就可以这么理解,this的new绑定无非是创建一个对象是要调用的一个函数,而这个要使用到的构造函数的this在调用的过程中指向发起这次构造的对象,这就是this的new绑定了。
箭头函数的this绑定
箭头函数ES6新增的属性,箭头函数中没有this,箭头函数的this指向外层作用域中的this,外层作用域或者函数的this指向谁,箭头函数中的this就指向谁。
这一点就很方便了,一般来说,在内层的函数我们并不希望它拥有自己的this,直接指向外侧作用域极其方便。
有一点要注意,this的绑定类似于bind的硬绑定(call和apply失效),就是说,一旦箭头函数的this绑定成功,就无法被再次改变,不过如果我们曲线救国的话,我们可以直接改变箭头函数的外层作用域的this指向,也成。
this五种绑定方法的优先级
显式绑定 > 隐式绑定 > 默认绑定
new绑定 > 隐式绑定 > 默认绑定
据说new绑定是优先于显示绑定的,我试了一下,发现new的使用和显示绑定完全不在一个场景,不能判断它俩的优先级。
call与apply
上文我说过了,call和apply的应用属于函数应用,为函数较为主动的行为。
在call方法中,第一个参数是选择的上下文,用来绑定this,其他的参数在函数执行时都会作为函数的形参传入函数。
而apply与call不同,除了第一个this绑定的参数以外,其他的参数都被包裹在一个数组中,在函数执行时作为一个数组整体传入。
除此之外,效果完全相同。
bind
call与apply在改变this指向的同时就会立即执行函数,而bind绑定this后不会立马执行,而是返回一个新的绑定函数。
let o = {
a: 1
};
function fn(b, c) {
console.log(this.a + b + c);
};
let fn1 = fn.bind(o, 2, 3);
fn1();//6
除此之外,bind属于硬绑定,也就是说,this指向与传进去的形参在bind方法执行的时候就已经确定了,无法再次改变,再运行这个函数,就相当于无参。
这三种方法用处很大,比如,我们知道的Math.max函数是无法传入数组的,而如果使用apply,则可以变相将数组作为形参传入这个方法里面去,岂不妙。
好了,this的绑定基本就是这些,还有就是作用域链和原型链不太清楚,call,apply和bind也都是很好理解,那么,完结撒花。