面试必问的call,bind,apply

431 阅读3分钟

相信大家在开发的过程中都会用到call,bind,apply。 所以在面试的过程中个三者基本都会被问到,手写原理。

谈谈你对call,bind,apply的理解?

遇到这个问题首先就要想到this。那么就看看你对this的理解吧。

MDN上是这么解释的: 与其他语言相比,函数的 this关键字在JavaScript中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。

在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。ES5引入了bind方法来设置函数的this值,而不用考虑函数如何被调用的,ES2015 引入了支持this词法解析的箭头函数(它在闭合的执行环境内设置this的值)。

全局执行

//浏览器中
console.log(this)//这里打印出来的是window对象

//node中
console.log(this)//这里打印出来的是global

总结:在全局中执行的this指向当前的全局对象,浏览器中指向的是window对象,node中指向的是global

函数中执行

function fn(){
    console.log(this)
}
fn()//window
//这里指向的是window对象,可以理解为window.fn()

可以看到在函数中执行的this指向是window,但是在严格模式下this的执行和非严格模式下的指向是不一样

'use strict'
function fn(){
    console.log(this)
}
fn()//undefined
//可以看到严格模式下this的指向是undefined,所以在使用严格模式下,为了不对全局环境造成污染,可以把严格模式放在一个立即执行的函数中
(function (){
    'use strict'
    function fn(){
        console.log(this)
    }
    fn()//undefined
})()

在对象中执行

当我们在对象中调用一个函数方法,this的指向又是什么呢?

let obj={
    name:'dog',
    fn:function(){
        console.log(this)
    }
}
obj.fn()//obj 
//可以看到这里的指向是obj,在this指向没有被改变的情况下,谁调用函数函数中的this就指向谁


//======================当然还有一种形式的调用
let fun=function (){
    console.log(this)
}
let obj={
    name:'dog',
    fn:fun
}
obj.fn()//obj

this关键字的一些坑

直接看代码吧

let obj={
    name:'dog',
    fn:function(){
        console.log(this)//window
    },
    fn1:function(){
        console.log(this)//obj
        setTimeout(this.fn,0)
    }
}
obj.fn1()
//fn1中直接打印的 this是obj对象,但是setTimeout中调用的fn打印的this是window
//解决这个setTimeout中this的指向可以用闭包解决
let obj={
    name:'dog',
    fn:function(){
        console.log(this)
    },
    fn1:function(){
        console.log(this)//obj
        let _this=this
        setTimeout(function(){
            console.log(_this)//obj
            console.log(this)//window
        },0)
    }
}
obj.fn1()

//严格模式下的setTimeout的坑
'use strict'
setTimeout(function(){
   console.log(this) //这里的this指向的是window对象并非undefined
},0)

在构造函数中使用this

//构造函数中的this
function Person(name){
    this.name=name
    console.log(this)//person{name:'jone'}
}
let p=new Person('jone')

//非构造函数中的执行
function Person(name){
    this.name=name
    console.log(this)//window
}
let p=Person('jone')

下面我们就来说下bind、call、apply的区别

call 和 apply 的主要作用,是改变对象的执行上下文且立即执行。区别是传参的方式存在区别。

// call传参:
var obj={
    name:'mingzi'
}
function showName(name,name1){
    console.log('我的名字:'+name+'曾用名:'+name1)
}
showName.call(obj,'sjs','sjs1')//我的名字:sjs曾用名:sjs1

//apply传参:
var obj={
    name:'mingzi'
}
function showName(name,name1){
    console.log('我的名字:'+name+'曾用名:'+name1)
}
showName.apply(obj,['sjs','sjs1'])//我的名字:sjs曾用名:sjs1

bind 也可以改变对象的执行上下文,它与call和apply区别是:返回值是一个函数,并且需要调用一下才会执行,不会立即执行。

var cat={
    name:'咪咪'
}
var dog={
    name:'汪汪',
    eat:function(y , n){
        console.log(this.name + y + n +'骨头')
    }
}
var show=dog.eat.bind(cat,'吃','不吃');
show()

三者相似之处

  • 1、都可以改变函数this对象的指向
  • 2、三者的第一个参数都是this要指向的对象
  • 3、都可以利用后续的参数传参数

===后续有时间在说下原理。