浅层介绍JavaScript的this

写在开头

仅以此篇短文回顾近期所学的js中的this,欢迎各位在一天无论几点开这篇文章,祝各位早安午安晚安୧⍢⃝୨chart2.jpg

这是正文

this是什么

this是指当一个函数被调用时,与函数上下文绑定,指向某个对象的引用,和C++、Java中的this指针有相似之处。

this的三种绑定

按书本编撰和老师讲授来说,this是有四种绑定的。但我认为,隐式丢失可以作为一种现象来理解,归于默认绑定中更好记忆,所以在本文中将他们拆分。

默认绑定

this所在词法作用域不带任何修饰地在哪里生效,即函数不带任何修饰地在哪里被调用,this就绑定在哪里。

如例一,函数fn()在全局环境下不带任何修饰地被调用,默认绑定到window。在node环境下没有window,打印undefined;在浏览器环境下打印2。

//例一
function fn(){
    console.log(this.a);
}
var a = 2
fn()//不带任何修饰的函数调用
复制代码

值得注意的是,在严格模式下全局对象无法进行默认绑定,this只能绑定到undefined。

隐式绑定

当一个函数以引用的方式被调用时,隐式绑定生效,函数中调用的this会被绑定到调用这个函数的上下文对象。

如例二,对象obj内有函数foo的引用,当obj引用调用foo时,foo内的this指向obj,打印出2。

//例二
function foo() {
    console.log(this.a);
}

var obj = {
    a: 2,
    foo: foo
}

obj.foo()//以引用的方式被调用
复制代码

显示绑定

显示绑定是指,利用三个函数call()、apply()、bind()中的一个,都可以强行指定函数的this指向。

三个函数的使用
call():参数一个一个传入,第一个实参是对象,实参传入个数可大于功能实现所需参数个数,返回函数运行结果。

apply():参数为一个对象和一个数组,数组内的元素个数可大于功能实现所需参数个数,返回函数运行结果。

bind():参数传入和call()一样,但bind()调用后会返回一个函数,需要一个新的变量来接收它返回的函数,再调用返回的新函数才能实现功能。用bind()进行显示绑定时,参数可以一次性传入,也可以分为两部分,第二部分传入返回的新函数。
复制代码

相同的函数、对象以三种不同方式实现显示绑定得出的结果是一致的,如例三,打印结果均为“南宫、3”。

//例三
var obj = {
    user: '南宫',
    fn: function (b, c) {
        console.log(b + c);
        console.log(this.user);
    }
}

obj.fn.call(obj, 1, 2)

obj.fn.apply(obj,[1,2,3])

var bar = obj.fn.bind(obj,1,2)
bar()

var bar = obj.fn.bind(obj)
bar(1, 2)
复制代码

this的隐式丢失

当一个函数进行了一次以上的隐式绑定,就会出现隐式丢失。隐式丢失发生后,隐式绑定的函数会丢失绑定对象,然后进行默认绑定,因此我将它归为默认绑定。

在例四中,函数foo作为对象obj的引用(foo函数被引用调用时,会发生一次隐式绑定),在标星一行中,作为函数dofoo的实参被传入(发生第二次隐式绑定),出现隐式丢失现象,进行默认绑定。在node环境下没有window,打印undefined;在浏览器环境下打印2。

//例四
function foo() {
    console.log(this.a);
}
function dofoo(fn) {
    var a = 123
    fn()
}

var obj = {
    a: 2,
    foo: foo
}
var a = 'global'
dofoo(obj.foo)//***
复制代码

显示绑定的实现

显示绑定所利用的三个函数,都是可以自己动手实现的。

call()函数

call()函数的实现运用了隐式绑定的原理,具体实现过程如下。apply()函数的实现与之类似,读者有兴趣可自己动手实现。

Function.prototype.myCall = function (context) {
//参数处理
    // var context = arguments[0]//myCall设置形参接收参数时,对象形参的获取方法

    //优化
    context = context || 'window'//如果不接收参数,则改为window,避免报错
  
    var args = [...arguments].splice(1)//获取除对象外的所有参数
//隐式绑定
    const fn = this//this指向调用myCall的函数
    context.fn = fn//调用myCall的函数添加到对象context

    //优化
    // const fn = Symbol('fn')//创建独一无二的变量fn,避免与window原本带有的属性冲突
    // context[fn] = this
//函数调用
    const res = context.fn(...args)//隐式绑定,将参数一个一个按fn接收的类型传进调用fn,得到执行结果
//移除fn,将实参对象恢复原样
    delete context.fn
//返回执行结果    
    return res
}
function fn(a, b) {
    console.log(a + b);
    console.log(this.a);
}
var obj = {
    a: 2
}
fn.myCall(obj,1,2)
复制代码

bind()函数

bind()函数的返回比较特殊,需要进行函数的嵌套和call()函数的运用,具体代码如下。

function fn(a, b) {
    console.log(a + b);
    console.log(this.a);
}
var obj = {
    a: 2
}
Function.prototype.myBind = function (context) {
//参数处理
    // var context = arguments[0]
    // var arge = [...arguments].splice[1]//参数处理
    var that = this//引用另存为that
    var bindArge = Array.prototype.slice.call(arguments, 1)
//构建要返回的函数
    function rBound() {
        var arge = Array.prototype.slice.call(arguments)//二次参数处理
        return that.apply(this instanceof rBound ? this : context, bindArge.concat(arge))
        //利用apply()实现显示绑定
    }

    return rBound
}
var bar = fn.myBind(obj)
bar(1, 2)
复制代码

写在结尾

新人入坑学前端,如果文中有错误敬请在评论区提出,也欢迎其他新人在评论区多多交流,还可以顺带点点赞(๑´ڡ`๑)

全文到此结束,感谢阅读。

chart1.jpg

猜你喜欢

转载自juejin.im/post/7119151297130922020