call、apply、bind的用法和区别

在JavaScript中,call、apply和bind是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向。这也是这三个函数的共同点。

call()
语法:fun.call(thisArg, arg1, arg2, ...)
thisArg是在 fun函数运行时指定的 this值。arg1,arg2,...是底层调用fun方法传递的参数。

如果第一个参数指定为 null和 undefined或者不传值,那么this值会自动指向全局对象(浏览器中就是window对象)
如果第一个参数指定为数字、字符串或者布尔类型,那么this分别执行它们的包装对象
function fn() {
console.log(this)
}
var obj = {
name: '张三'
}
function show(){}
fn.call() // Window
fn.call(null) // Window
fn.call(undefined) // Window
fn.call(1) // Number
fn.call(false) // Boolean
fn.call('嘿嘿') // String
fn.call(obj) // {name: "张三"}
fn.call(show) // ƒ show(){}
举个简单的栗子来描述一下call方法吧:

function Animal(name) {
this.name = name
this.getName = function() {
return this.name
}
}

function Cat(name) {
this.name = name
}

var animal = new Animal('动物')
var cat = new Cat('可爱的猫')
// console.log(cat.getName()) // TypeError: cat.getName is not a function
console.log(animal.getName.call(cat)) // 可爱的猫
animal这个对象是有getName这个方法的,这个方法有个this,这个this指向animal对象。但是我们用call方法可以把这个this指向cat,这个时候输出的就是cat的name啦。

apply()
语法:fun.apply(this,arguments)

apply和call方法的作用和使用方式几乎一样,除了参数不同。arguments是一个数组

function fn1(a,b,c) {
this.getSum = function() {
return a+b+c
}
}

function fn2() {
var a = 1
var b = 5
var c = 10
fn1.apply(this,[a,b,c])
}
var f = new fn2()
console.log(f.getSum()) // 16
bind()
bind和apply以及call的区别在于:bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用。注意:bind方法的返回值是函数

function add(b,c) {
return this.a+b+c
}
var bind = add.bind({a:1},2)
console.log(bind(3)) // 6
第一个参数是{a:1}所以,add函数的this指向{a:1}这个对象。第二个参数2赋值给b。这个时候并没有立即指向add方法,而是返回bind函数,我们要主动调一下bind函数才会执行add函数。此时调用bind函数的时候传的3会赋值给c,所以最终返回6

其实了解了这三个函数的用法之后,我比较纳闷的是实战中会用在什么地方,所以去搜索各类资料,总结出一些常用的场景。下面一一介绍

使用场景
1、求数组最大值最小值

let a = [35,3,67,23,67,38,64,89,34,46,23,56,78,43,34,56,67,34,65]
var min = Math.min.apply(null,a)
var max = Math.max.apply(null,a)
console.log(min,max) // 3,89
Math.min()以及Math.max()的参数是不支持数组的,但是apply方法是支持数组的。并且apply的数组参数在底层会展开一个一个给max方法,这样就能很轻松的实现求数组最大最小值。其中apply 的第一个参数是null是因为没有对象去调这个方法,我们只要用这个方法帮我们完成运算得到返回值就行

2、实现两个数组合并

var arr1 = [1,2,3]
var arr2 = ['a','b','c']
Array.prototype.push.apply(arr1,arr2)
console.log(arr1) // [1, 2, 3, "a", "b", "c"]
console.log(arr2) // ["a", "b", "c"]
Array的push方法也是没有提供push数组的。但是它提供了push(param1,param,…paramN) 所以同样也可以通过apply来转换一下这个数组

3、继承

function Animal(name) {
this.name = name
this.getName = function() {
return this.name
}
}
Animal.prototype.age = 18
function Dog(name) {
Animal.call(this,name)
// Animal.apply(this,[name])
// Animal.bind(this,name)()
}

var dog = new Dog('小狗')
console.log(dog.getName()) // 小狗
Dog构造函数是没有getName方法的,但是通过改变this指向,Animal里的this指向Dog里的this,这样就实现继承啦

4、伪数组使用数组方法

var lis = document.querySelector('ul').childNodes
console.log(lis)
// lis.slice(0,1) TypeError: lis.slice is not a function
Array.prototype.slice.call(lis,0,1)
getElementsByTagName , document.childNodes等方法返回的对象都属于伪数组,伪数组是不能使用数组的方法的,所以我们可以用call转成真正的数组,之后就可以用数组的方法啦

了解更多前端培训相关面试问题欢迎关注小编!

上一篇:wex5当中,页面输入框选择中时,js去除输入框投影方法


下一篇:手写 bind 函数