从原理上详解JS中的this

前言

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

网传的一句话:

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

因而就延伸出了肯定this指向的几种方法:数组

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

虽然上面说的并无错,可是这么多种状况,在实际应用中也不必定可以准确的对应上,咱们不如来学一下this的原理,这样遇到问题也能够本身分析。bash

函数返回值的影响因素

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

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

经过例子学知识

非箭头函数

例子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指向调用它的对象,因此输出的结果是jackui

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

注意!!!spa

注意!!!

注意!!!

这里有一个须要注意的地方,当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指向

相关文章
相关标签/搜索