this指向 call apply bind 原理

168 阅读2分钟

this指向

在 ES5 中,this 永远指向最后调用它的那个对象

const o = { name:'global' }
const a = {
    name: "active",
    fn : function () {
        console.log(this.name);     
    }
}
a.fn();  // active

箭头函数

箭头函数中没有this,箭头函数的this指向函数定义时的this,而非执行时。如果非箭头函数中包含箭头函数,则箭头函数内部的this则是非箭头函数的 this

 const o = { name:'global' }
 const a = {
    name : "active",
    func2: function () {
        setTimeout( () => {
            console.log(this.name); 
        },100);
    }
}
a.func2()     // active

call

如何使用

const o = { name:'global' }
const a = {
    name: "active",
    fn : function () {
        console.log(this.name);     
    }
}
a.fn.call(o) // global

如何传参

const o = { name:'global' }
const a = {
    name: "active",
    fn : function (a,b) {
        console.log(this.name,a,b);     
    }
}
a.fn.call(o,1,2) // global 1 2

如何实现

Function.prototype.call = function(context,...args){
    context = context ? Object(context) : window // 将上下文设置成包装对象
    args ||= []
    const sym = Symbol()
    context[sym] = this
    const res = context[sym](...args) // this 永远指向最后调用它的那个对象 所以函数的this指向了context
    delete context[sym]
    return res
}

apply

如何使用

const o = { name:'global' }
const a = {
    name: "active",
    fn : function () {
        console.log(this.name);     
    }
}
a.fn.apply(o) // global

如何传参

const o = { name:'global' }
const a = {
    name: "active",
    fn : function (a,b) {
        console.log(this.name,a,b);     
    }
}
a.fn.apply(o,[1,2]) // global 1 2

如何实现

Function.prototype.apply = function(context,args){
    context = context ? Object(context) : window 
    args ||= []
    const sym = Symbol()
    context[sym] = this
    const res = context[sym](...args)
    delete context[sym]
    return res
}

call和apply区别

由上得:call 和 apply 使用上没区别,区别是传承方式不同。call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。

思考:箭头函数能调用call吗

箭头函数也是函数,继承自Function,所以能调用call,但是不会改变其内部的this,因为箭头函数内部的this在其被定义的时候就确定了,不会改变。

bind

如何使用

bind执行后返回一个函数

const o = { name:'global' }
const a = {
    name: "active",
    fn : function () {
        console.log(this.name);     
    }
}
const bindFn = a.fn.bind(o)
bindFn() // global

如何传参

const o = { name:'global' }
const a = {
    name: "active",
    fn : function (a,b) {
        console.log(this.name,a,b);     
    }
}
// 这里两个函数执行都可以传参
const bindFn = a.fn.bind(o,1)
bindFn(2) // global 1 2

如何实现

Function.prototype.bind = function(context,args){
    args ||= []
    context = context ? Object(context) : window

    const self = this

    // return出去的函数是否被new 如果new 则内部的this指向实例
    const resultFn = function (...args2) {
        args2 ||= []
        self.apply(
            this instanceof resultFn ? this : context,
            args.concat(args2))
    }
    // 继承构造函数上的方法
    resultFn.prototype = Object.create(self.prototype)
    return resultFn
}

思考:函数一直.bind结果如何?

const foo = function(){
    console.log(this)
}
const bindFn = foo.bind(1).bind(2)
bindFn()

上面代码输出的this是什么呢? 从左到右先分析一波:

 foo.bind(1)得到一个f1。 类似 f1 = function(){ foo.apply(1) }
 foo.bind(1).bind(2) --> f1.bind(2)
 得到一个f2 ,类似 f2 = function(){ f1.apply(2) }
 这个f2就是上面的bindFn,然后bingFn执行,可以看到不管套了多少层,foo这个函数还是由foo.apply(1)执行,所以其内部的this还是1。都是第一次bind的对象!