JavaScript中call、apply和bind方法详解及它们的区别

日期:2020年07月09日 阅读次数:2378 分类:javascript

前言

JavaScript中,call、apply和bind方法都是可以用来改变this指向进行上下文绑定的,它们之间既有相似之处也有不同之处。

相似之处

  1. 都是用来改变函数中this的指向
  2. 第一个参数都是this要指向的对象
  3. 都可以利用后续参数传参(但是传参方式有所不同,下面介绍)

区别

1、执行方式

call和apply都是对函数的直接调用(即:修改this指向后并立马执行),而bind方法返回一个(被修改了this指向)的新函数,函数不会被执行。

function fn () {
    console.log(this.a)
}
// 因为这时fn中的this指的是window,window下没有a,所以是undefined
fn() // undefined


// call、apply方法修改函数的this指向后会立即执行,输出结果
let obj = {a: '1'}
fn.call(obj) // 1
fn.apply(obj) // 1

// 因为使用call、apply把fn中的this指向修改成了obj,所以this.a就是obj.a => 1


// bind方法修改函数的this指向后不会立即执行,只是返回一个新函数,要想执行需要加上()去调用。
var fn1 = fn.bind(obj) // 这时不会执行,只是返回一个新函数

// 返回的新函数加上()去调用执行,输出结果
fn1() // 1

2、入参方式

2.1、call和apply都可以传参数。call后面的参数与fn方法中是一一对应的,而apply的第二个参数是一个数组,数组中的元素是和fn方法中一一对应的,这就是两者最大的区别。

function fn (age, job) {
    console.log(this.name, age, job)
}

fn.call({name: 'yang'}, 26, 'IT') // yang 26 IT
fn.apply({name: 'wang'}, [24, 'worker']) // yang 24 worker

// call和apply都是会直接调用函数
// 第一个参数都是函数执行时的this
// call后面的参数与fn方法中是一一对应的
// apply的第二个参数是一个数组,数组中的元素是和fn方法中一一对应

apply的多种用法

function sum(num1, num2){
    return num1 + num2;
}

function callSum1(num1, num2){
    return sum.apply(this, arguments);        // 传入arguments对象
}

function callSum2(num1, num2){
    return sum.apply(this, [num1, num2]);    // 传入数组
}

alert(callSum1(10,10));   //20
alert(callSum2(10,10));   //20

2.2、bind是ES5中的方法,可以像call一样传参,也可以在调用的时候再进行传参。

function fn (age, job) {
    console.log(this.name, age, job)
}
// 可以像call一样传参,第一个是this指向的对象,后面依次是与fn方法中是一一对应的参数。
var fn1 = fn.bind({name: 'yang'}, 26, 'IT')
fn1() // yang 26 IT

// 也可先修改this指向,然后在调用新方法时传入参数
var fn1 = fn.bind({name: 'yang'})
fn1(26, 'IT') // yang 26 IT

其它注意事项

  1. bind只会改变一次this指向。
function fn () {
    console.log(this.a)
}
var fn1 = fn.bind({a: 1})
var fn2 = fn.bind({a: 2})
var fn3 = fn.bind({a: 3})
fn()
// 可以在原函数(fn)上多次用bind修改this指向,返回多个被修改了this指向的新函数,且不会影响原函数
fn1() // 1
fn2() // 2
fn3() // 3

// bind只会改变一次this指向
// fn1已经的this指定已经被修改为了{a: 1},所以再使用fn1调用bind方法修改this指定是无效的。
var fn4 = fn1.bind({a: 100})
fn4() // 1
  1. 如果传入的不是对象,会调用相对的构造函数进行隐式转换。
function fn () {
    console.log(this)
}
fn.call(1) // Number {1}
// 如果传入的不是对象,而是数值1,则会被隐式转换对象形式

实例

1、使用Object.prototype.toString.call判断数据类型

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用

总结

  1. 当我们使用一个函数需要改变this指向的时候才会用到call apply bind
  2. 如果你要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 …)
  3. 如果你要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 …])
  4. 如果你想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用const newFn = fn.bind(thisObj); newFn(arg1, arg2…)

参与文章

JavaScript中apply、call和bind方法的区别-简书

文章标签: