likes
comments
collection
share

换个姿态学javascript的this

作者站长头像
站长
· 阅读数 3

前言

this作为JavaScript中一个大难点,很多初学者一开始都理解不了,或者理解不准确之类的。我在看了《javascript高级程序设计》以及一些大佬的博客后,对this有了新的认识,于是写下来供大家参考。如果该博客有什么错误的地方,请读者指正。

网传的一句话:

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才确定this到底指向谁,实际上this的最终指向的是最后调用它的对象

于是就延伸出了确定this指向的几种方法:

  • this是上下文的属性
  • 全局环境执行函数时,this是全局对象
  • 调用对象的方法时,this是指向该对象
  • 函数里调用函数时,this是全局对象
  • 还有人说箭头函数的this指向的是外面的this
  • 用new构造函数时,this指向的是实例对象
  • 可以用call/apply/bind指向this

虽然上面说的并没有错,但是这么多种情况,在实际应用中也不一定能够准确的对应上,我们不如来学一下this的原理,这样遇到问题也可以自己分析。

函数返回值的影响因素

  • 函数调用时传入的参数(非箭头函数)
  • 函数定义时的所在的作用域(箭头函数)

在接下来的实例讲解中,我将函数分为 箭头函数非箭头函数。在非箭头函数中,this是作为一个隐式的参数传入,而在箭头函数中,this与其他变量一视同仁,不会被特殊处理。

通过例子学知识

非箭头函数

例子1:

function a(){
    var name='ming';
    var age=13;
    console.log(this.name,age);
}
var name='ye';
var age=15;

a();    //输出的结果是ye   13

我们来分析一下:

a()作为一个普通的函数,this是作为一个隐式的参数传入到函数参数中。它完整的写法应该是window.a.call(window);由于我们没有显式的调用call来指定this的指向,那么JS就会帮我们完成这一步,让调用该函数的对象作为this。所以this在这里指向的是window; 而函数的参数是在调用时候才确定的,所以this.name输出的是ye;但是对于age,他在函数里面只是一个普通的变量,加上作用域链的查找规则,会使用离他最近的变量。与会这里的age会输出13。

例子2:

var name='outter';
let obj={
  name:'jack',
  say(){
    console.log(this.name)
  };
};

(1) obj.say();    //输出的结果为'jack'

let c=obj.say
(2) c()    //这里输出的是outter;

我们先来看一下第一个输出结果。 通过对象obj来调用say方法,这段代码相当于obj.say.call(obj)。因为不通过call强行指定this指向,那么JS会默认帮我们把this指向调用它的对象,所以输出的结果是jack

换个姿态学javascript的this

第二个输出结果我们来看看上面的内存图,变量C保存的是say的地址,也就是说我们调用C的时候,并不经过obj的影响,那么这种情况也是相当于window.c.call(window),所以JS默认情况下会让this指向调用它的对象,也就是全局对象window

注意!!!

注意!!!

注意!!!

这里有一个需要注意的地方,当var name='outter' 改为let name='outter'的时候,情况却变了。

let name='outter'
let obj={
  name:'jack',
  say(){
    console.log(this.name)
  }
}

let c=obj.say
c()     //输出的结果是undefined

这是因为let声明的变量并没有成为window的变量,而var声明的变量成为了window的变量,所以this指向的对象window并没有name这个属性

例子3

var obj1={
    name:'pony',
    say(num=''){
        console.log(this.name+num)
    }
}

var obj2={
    name:'jack'
}

(1) obj1.say()         //输出pony
(2) obj1.say.call(obj2,2)      //输出jack2
(3) obj1.say.apply(obj2,[2])     //输出jack2

callapply的最大区别就是apply传入参数需要放在一个数组里面。 这里通过call显式的指定this的指向,于是这里就把obj2作为say的调用对象了。那么你可能会有疑问,为什么我的say写在obj1里面,用call就可以改变指向呢。这个只是恰好say写在了obj1里面而已。

例子4

var name='one'
let a=function f1(){
  console.log(this.name)
}

function f1(){
  var name='inner'
  a()
}

f1()    //输出的结果为one,而不是inner

箭头函数

箭头函数不绑定this,他会捕捉定义的位置或者作用域链上最近的this,作为自己的this。所以 call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。

注意,{}并不会形成一个作用域,在JS中只有全局作用域与函数生成的局部作用域。

例子1

var i=10;
var a={
  i:20,
  b:()=>{
    console.log(this.i)
  },
  c:function(){
  console.log(this.i)
}
}

a.b()      //输出10
a.b.call(a)     //输出10
a.c()      //输出20

因为{}并不会形成作用域,箭头函数沿着作用域链向上查找,在全局作用域找到了i,于是返回全局作用域的i,由于箭头函数并没有绑定this,所以使用call指定也是没有办法改变this,因为它压根没有this。而对于a.c(),作为非箭头函数,上面的例子已经解析了。

var i=10
function f1(){
  i=12;
  var f=()=>{
    console.log(this.i)
  }
  f()
}

f1()      //输出12

因为函数会形成局部作用域,箭头函数查找的时候,在f1的作用域就能找到i了。

以上为个人学习整理的内容,全部的例子都是经过个人检验的,欢迎一起交流学习。

参考文献

this - JavaScript | MDN

箭头函数 - JavaScript | MDN

this指向