揭开this的神秘面纱

123 阅读3分钟

前端开发this作为老生常谈的一个话题,但是this是我们每个开发都应该必须掌握的知识。合理的使用this使我们的代码更加优雅,高效。

调用上下文

理解this的绑定过程, 你首先要了解this的调用位置,调用位置是函数在代码中的调用位置(非声明位置) 举个例子:

let name = "foo"
function foo(){
    console.log(this.name)
}
foo()

则foo()才是调用的位置,默认情况下,this指向window,输出foo

隐式绑定

思考以下代码


let obj = {
    name:'objName',
    foo:function(){
        console.log(this.name)
    }
}
obj.foo() //objName

从上面代码可以看出,当foo被调用时,调用者是obj,此时this指向obj。也就是说,隐式绑定规则会把函数调用this绑定到这个上下文对象,因此this.name 和 obj.name是一样的。 但要注意的是对象属性引用链中只有上一层或者最后一层在调用位置中起作用。如下:

function foo (){
  console.log(this.name)
}
let obj1 = {
    name:"obj1",
    foo:foo
}
let obj2 = {
     name:"obj2",
    foo:foo
}
obj1.obj2.foo(); // obj2
obj2.obj1.foo(); // obj1

但是要注意的是,以上代码都是忽略了严格模式(strict mode),如果使用了严格模式,则不能将全局对象用于默认绑定

let name = "foo"
function foo(){
   "use strict";
    console.log(this.name)
}
foo()
//  Uncaught TypeError: Cannot read property 'name' of undefined

显式绑定

以上隐式绑定的分析,都是在一个对象内部包含一个指向函数的属性,从而改变对象内的指向。 但是如果我们不想在对象内部包含函数引用,而想在某个对象强制调用函数,该怎么做呢?现在主流就是call和apply 思考以下代码:

function foo(){
  console.log(this.name)
}
var obj = {
    name:"jack"
}
foo.call(obj)  //jack

通过foo.call...,我们可以在调用foo时强制把它的this绑定到obj上。
同理apply在面对上面代码也是输出一样的结果,从this的绑定角度来说,call和apply是一样的,区别在于两者的其他参数上。具体自行百度。 emmmm,你可以这么快速理解,对象.函数,则是隐式绑定,反之,显示绑定

绑定丢失

不知道你们有没有遇到过以下问题
面试题:

var name = "window";
function foo(){
    console.log(this.name)
}
var obj = {
    name:"jack",
    foo:foo
}
var _foo = obj.foo;
_foo()

以上代码输出结果是?
答案是window
很明显这不是我们想要的结果,当_foo执行时,foo函数内的this指向window,导致了现在这个结果, 那我们怎么做才能把this绑定到我们的目标对象里面呢?

硬绑定

思考以下代码

var name = "window";
function foo(){
    console.log(this.name)
}
var obj = {
    name:"jack"
   }
var bar =  function(){
    foo.call(obj)
}
bar(); //jack
bar.call(window) //jack

以上代码无法修改bar函数里的值,这样解决了我们绑定丢失的问题, 硬绑定的典型运用场景,就是创建一个包裹函数,负责接收参数并返回值

function foo(n){
    return this.a + n ; 
}
var obj = {
    a:2
}
var bar = function(){
    return foo.apply(obj,arguments)
}
var _num  = bar(3) 
console.log(_num) //5

那我们又思考了,假如隐式绑定和同时存在,那结果是什么呢?
思考以下代码

function foo (){
    console.log(this.a)
}
var obj1 = {
    a:1,
    foo:foo
}
var obj2 = {
    a:2,
    foo:foo
}
obj1.foo.call(obj2) //2 
obj2.foo.call(obj1) //1

相信看到输出结果,大家已经明白了。显示绑定的优先级更高,也就是说先会去判断有没有显示绑定再考虑隐式绑定。

结语

讲到这里,大家是不是对this有更深一点的理解呢,
有兴趣的朋友也可以去了解下函数柯里化,跟硬绑定有点相似,算是题外话吧。
学习js的道路上,还是要多写多练多实践,才是进步的源泉啊。 文章就写(shui)到这里,觉得对你有帮助,就点个赞吧~~