call和apply都是解决this指向问题的方法,唯一的区别是apply传入的参数除了其指定的this对象之外的参数是一个数组,数组中的值会作为参数按照顺序传入到this指定的对象中。
bind是解决call和apply指定函数对象的问题。bind可以将this指定给一个指定的对象
下面将具体说明:
一、this指定问题
this
可以用在构造函数之中,表示实例对象。this
还可以用在别的场合。但不管是什么场合,this
都有一个共同点:它总是返回一个对象。
上面代码中,this.name
表示name
属性所在的那个对象。由于this.name
是在describe
方法中调用,而describe
方法所在的当前对象是person
,因此this
指向person
,this.name
就是person.name
。
由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this
的指向是可变的。
上面代码中,A.describe
属性被赋给B
,于是B.describe
就表示describe
方法所在的当前对象是B
,所以this.name
就指向B.name
。
使用场合
(1)全局环境
全局环境使用this
,它指的就是顶层对象window
。
上面代码说明,不管是不是在函数内部,只要是在全局环境下运行,this
就是指顶层对象window
。
(2)构造函数
构造函数中的this
,指的是实例对象。
上面代码定义了一个构造函数Obj
。由于this
指向实例对象,所以在构造函数内部定义this.p
,就相当于定义实例对象有一个p
属性。
(3)对象的方法
如果对象的方法里面包含this
,this
的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this
的指向。
但是,这条规则很不容易把握。请看下面的代码。
上面代码中,obj.foo
方法执行时,它内部的this
指向obj
。
但是,下面这几种用法,都会改变this
的指向。
上面代码中,obj.foo
就是一个值。这个值真正调用的时候,运行环境已经不是obj
了,而是全局环境,所以this
不再指向obj
。
可以这样理解,JavaScript 引擎内部,obj
和obj.foo
储存在两个内存地址,称为地址一和地址二。obj.foo()
这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,this
指向obj
。但是,上面三种情况,都是直接取出地址二进行调用,这样的话,运行环境就是全局环境,因此this
指向全局环境。
二、call apply bind的区别
JavaScript 提供了call
、apply
、bind
这三个方法,来切换/固定this
的指向。
1、call
函数实例的call
方法,可以指定函数内部this
的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
上面代码中,全局环境运行函数f
时,this
指向全局环境(浏览器为window
对象);call
方法可以改变this
的指向,指定this
指向对象obj
,然后在对象obj
的作用域中运行函数f
。
call
方法的参数,应该是一个对象。如果参数为空、null
和undefined
,则默认传入全局对象。
上面代码中,a
函数中的this
关键字,如果指向全局对象,返回结果为123
。如果使用call
方法将this
关键字指向obj
对象,返回结果为456
。可以看到,如果call
方法没有参数,或者参数为null
或undefined
,则等同于指向全局对象。
如果call
方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call
方法。
上面代码中,call
的参数为5
,不是对象,会被自动转成包装对象(Number
的实例),绑定f
内部的this
。
call
方法还可以接受多个参数。
call
的第一个参数就是this
所要指向的那个对象,后面的参数则是函数调用时所需的参数。
上面代码中,call
方法指定函数add
内部的this
绑定当前环境(对象),并且参数为1
和2
,因此函数add
运行后得到3
。
2、apply
apply
方法的作用与call
方法类似,也是改变this
指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。
apply
方法的第一个参数也是this
所要指向的那个对象,如果设为null
或undefined
,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call
方法中必须一个个添加,但是在apply
方法中,必须以数组形式添加。
3、bind
bind
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数。
上面代码中,我们将d.getTime
方法赋给变量print
,然后调用print
就报错了。这是因为getTime
方法内部的this
,绑定Date
对象的实例,赋给变量print
以后,内部的this
已经不指向Date
对象的实例了。
bind
方法可以解决这个问题。
上面代码中,bind
方法将getTime
方法内部的this
绑定到d
对象,这时就可以安全地将这个方法赋值给其他变量了。
this
绑定到其他对象也是可以的。
上面代码中,bind
方法将inc
方法内部的this
,绑定到obj
对象。结果调用func
函数以后,递增的就是obj
内部的count
属性。
bind
还可以接受更多的参数,将这些参数绑定原函数的参数。
上面代码中,bind
方法除了绑定this
对象,还将add
函数的第一个参数x
绑定成5
,然后返回一个新函数newAdd
,这个函数只要再接受一个参数y
就能运行了。
如果bind
方法的第一个参数是null
或undefined
,等于将this
绑定到全局对象,函数运行时this
指向顶层对象(浏览器为window
)。
上面代码中,函数add
内部并没有this
,使用bind
方法的主要目的是绑定参数x
,以后每次运行新函数plus5
,就只需要提供另一个参数y
就够了。而且因为add
内部没有this
,所以bind
的第一个参数是null
,不过这里如果是其他对象,也没有影响。
文件参考:https://wangdoc.com/javascript/oop/this.html