上文分享了this的应用,这次分享call、apply、bind的应用
1.每个函数都有call、apply、bind方法,通俗点说就是function foo()这样形式就能使用foo.call()/apply()/bind()方法。
2.它们都能改变函数执行上下文。区别在于:
1)call、apply返回值为undefined,且函数立即执行,bind返回值为函数,不会立即执行。
2)call(this,arg2,arg4,arg4…)接收的参数为数据列(通俗点说就是一个一个用逗号隔开的值),apply(this,[a1,a2,a3])接收的第二个参数为数组。
下面通过实例来看运用
定义一个全局变量obj、foo函数,此时foo函数中的this指向window,要访问obj中的属性,需要先访问window,再从window中取值this.obj.name
var obj = {
name:'hehe',
age:23
}
function foo(){
console.log(this.obj.name) // 'hehe'
}
var obj = {
name: 'hehe',
age: '23',
foo: function () {
console.log(this.name) // 'hehe'
}
}
obj.foo()
上段代码和下段有相似之处:obj都是全局变量,都是访问全局的obj里的name属性。不同的是foo函数里的this指向:上段代码中this指向Window,下段指向obj。
call:
使用call可以改变执行上下文, foo中的this指向obj而不是Window了。
var obj = {
name:'hehe',
age:23
}
function foo(){
console.log(this.name) // 'hehe'
}
foo.call(obj)
同理原先指向obj的写法,使用call后,可以使this指向其他变量。
var o = {
name: 'aaaaaaa'
}
var obj = {
name: 'hehe',
age: '23',
foo: function () {
console.log(this.name) // 'aaaaaaa'
}
}
obj.foo.call(o)
对于call第一个参数之后的参数,即为函数可以使用的参数,例如:
var o = {
name: 'aaaaaaa'
}
var obj = {
name: 'hehe',
age: '23',
foo: function (n, m) {
console.log(this, n, m) // { name: 'aaaaaaa' } { a: '4' } '6'
}
}
obj.foo.call(o, { a: '4' }, '6')
apply改变this的使用和call一毛一样,只是后面的参数为数组:
var o = {
name: 'aaaaaaa'
}
var obj = {
name: 'hehe',
age: '23',
foo: function (n, m) {
console.log(this, n, m) // 同样分别获得值 { name: 'aaaaaaa' } { a: '4' } '6'
}
}
obj.foo.apply(o, [{ a: '4' }, '6'])
call的应用,例如Object.prototype.toString.call()、Array.prototype.slice.call() //apply
Object和Object.prototype都有toString方法,Object.toString()返回函数"function Object() { [native code] }"
,Object.prototype.toString()返回类型"[object Object]"
,那么call在这里充当了什么角色?call改变了toString执行的上下文,Object.prototype.toString()
toString this指向Object,Object.prototype.toString.call([]) this就指向[],Object.prototype.toString.call(1) this指向1。那么为什么是使用Object.prototype而不是Array.prototype或者Number.prototype?因为所有对象继承自Object,而Array、Number并不互相包含其他继承对象。
所有的对象都继承自Object,Object.prototype 指向 Object.prototype原型对象,Object.prototype.constructor指向Object。当我们使用arr.toString()时,不能进行复杂数据类型的判断,因为它调用的是Array.prototype.toString,虽然Array也继承自Object,但js在Array.prototype上重写了toString,而我们通过toString.call(arr)实际上是通过原型链调用了Object.prototype.toString。
Array.prototype.slice.call(arguments):slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。call()方法的第二个参数表示传递给slice的参数即截取数组的起始位置。
function list() {
return Array.prototype.slice.call(arguments,1); //apply(arguments,[1])
}
var list1 = list(1, 2, 3); // [2, 3]
再来看看bind,bind接收参数形式和call一毛一样(this,arg2,arg3,arg4…)。bind返回的是个函数,但不会立即执行,所以我加了个()执行它。
var o = {
name: 'aaaaaaa'
}
var obj = {
name: 'hehe',
age: '23',
foo: function (n, m) {
console.log(this, n, m) // { name: 'aaaaaaa' } { a: '4' } '6'
}
}
obj.foo.bind(o, { a: '4' }, '6')() // 这里执行了bind返回的函数 foo
bind的应用,例如在react中写了一个方法,调用这个方法之前需要将这个方法的this指向绑定到constructor上下文。例如:
constructor(props){
super(props)
this.foo = this.foo.bind(this)
}
这样在jsx中调用foo方法this指向组件实例。
不使用bind改变this指向,this的指向就会丢失。例如:
var obj = {
name: 'hehe',
age: '23',
foo: function () {
console.log(this) // { name: 'hehe', age: '23', foo: [Function: foo] }
}
}
obj.foo()
var Fo = obj.foo
Fo() // window
this指向最终调用它的执行环境。在react中使用bind改变上下文执行环境后,由于react是虚拟DOM结构,所以在div标签中使用onClick事件,this指向的不是DOM而是组件。