首页 > 其他 > 详细

this 和作用域

时间:2021-02-09 18:16:24      阅读:41      评论:0      收藏:0      [点我收藏+]
1.概述
在文章开始之前让我们思考如下几个问题:
1. this是什么?一个引用?
2.this对象存储在哪里?
3.根据规范,箭头函数执行的时候,是没有自己this的,但是为什么能访问到this呢?
4.obj.func() 和 let func = obj.func; func(); 其中this不同,但是调用函数是一样的,这是如何实现的呢?
 
2.this表达式
我们先简单说说另外两个涉及到的概念
a.引用,其结构为:
{
  base: js的所有值,除了undefined 和null,作用域,unresolvable
  referenceName:引用名字,比如 obj.func ,func就是referenceName
  strict: 是否是严格模式
  this: super对应值
}
b.作用域:一个包含一些特殊方法的对象,用来存储声明的标识符,继承于:声明作用域或者对象作用域,不同的作用域是不同的对象,
比如全局作用域,函数作用域
 
this是一个表达式,执行this表达式会返回一个值,有可能是引用(对象或者原始值),其内部执行原理如下:
1.let env = runningExeutionEcontext.lexicalEnv
2.循环:
//断言:这里env肯定不会为null, 因为最外部的全局作用域的hasThisBinding为true
如果env.hasThisBinding() 则 break
否则 env = env.outer
3.得到一个含有this的env,执行 env.getThisBinding()
 
上面的逻辑很好理解,需要注意的是env可能是不同的作用域,下面是不同作用域下,hasThisBinding和getThisBinding的执行逻辑:
1.如果 env为全局作用域:
执行hasThisBinding返回true,因为肯定有全局对象
执行getThisBinding返回全局对象
2.如果 env为模块作用域:
执行hasThisBinding返回true
执行getThisBinding返回undefined
这个比较好玩的是判定的时候返回为true,结果获取的时候却返回null
3.如果 env为函数作用域:
执行hasThisBinding:如果当前的函数作用域的thisMode为lexical那么返回false,否则返回true
执行getThisBinding返回作用域对象的thisValue
4.如果 env为块级作用域:
执行hasThisBinding返回false
因为hasThisBinding始终返回false,所以不可能在块级作用域执行getThisBinding
 
3.this的绑定
this的绑定只存在两种情况:
1.创建全局作用域的时候
2.创建函数作用域的时候
 
创建全局作用域的时候,也会自动创建全局对象,这个没什么说的,接下来重点说下载创建函数作用域的时候,this是如何创建的,
其核心的方法是:OrdinaryCallBindThis
 
a.在执行普通函数(调用函数对象内部call)的时候会创建函数作用域,然后调用OrdinaryCallBindThis(funcObj,context, thisArg):
1.如果funcObj(数对象)的thisMode为lexical,那么返回
2.如果funcObj(数对象)的thisMode为strict,那么thisValue = thisArg
3.否则:
如果thisArg为null或者undefined,那么thisValue设为全局对象
否则:thisValue = toObject(thisArg)
4.设置 env.thisValue = thisValue
 
从上面看出来,this的值不仅仅是在函数运行的时候决定,还跟函数的声明有关系,因为thisMode是在声明函数的时候决定的
 
好了,我们现在能够结合this的绑定和执行来解释问题3了:根据规范,箭头函数是没有自己this的,但是为什么能访问到this呢?
1.箭头函数在声明时候,thisMode为lexical,所以当运行箭头函数的时候,并不会绑定this值,并且设置函数作用域的thisMode为lexical
2.当运行this表达式的时候,发现thisMode为lexical,所以判定当前没有this值,会到外部作用域去寻找this值
 
b.执行特殊函数,比如apply,call,bind
1.apply 和call 执行:调用OrdinaryCallBindThis,thisArg是执行时候传入的thisArg
2.bind之后再执行:调用OrdinaryCallBindThis,thisArg为调用bind的时候传入的thisArg
 
c.上面是直接执行函数的时候绑定this的执行过程,也就是类型与obj.func()或func()这样调用的执行过程,但是还有另外一种情况,
就是通过 new 执行函数,而使用new的时候,this值就不由我们决定了,而是内部自动创建,具体执行过程如下:
1.创建context, 创建函数作用域
2. 如果funcObj.prototype 是对象,那么proto = funcObj.prototype
3.否则proto = Object.prototype
4. thisValue为Object.create(proto)的结果值
5. 调用OrdinaryCallBindThis(funcObj,context, thisValue)
 
3.CallExpression
现在我们来尝试回答最后一个问题:obj.func() 和 let func = obj.func; func(); 其中this不同,但是调用函数是一样的,这是如何实现的呢?
 
执行函数调用就是执行call表达式,而CallExpression = MemberExpression + ArgumentS
Aruments = () | (argumentsList)
MemberExpression = PrimaryExpression | MemberExpression. IdentiferName | ...
PrimaryExpression = this | IdentiferReference | Literal | ...
PropertyAccessor = MemberExpression. IdentiferName,返回一个引用,base为obj
IdentiferReference = 调用 resolveBinding, 返回一个引用,base为env
 
然后callExpression执行过程:
1. 执行子表达式也就是 MemberExpression, 得到一个值ref,可能是引用可能一个值
2. 执行子表达式PropertyAccessor, 得到一个引用, 其base为obj
3. 执行call表达式,执行getValue,得到func对象 //这里getValue会对ref的base执行装箱操作
4.执行evaluateCall(funcObj, ref, Arguments, tailCall--是否是尾调用)
1.如果ref是一个引用,this = ref.base,否则ref的base值肯定为env, this = env.withBaseObj()
//注:env.withBaseObj() 只在全局作用域中会返回全局对象,其他作用域都会返回undefined
2.执行上面第2节说的流程
 
通过上面的流程我们就能解释:obj.func() 调用时候this为obj是因为obj.func 返回了一个base为obj的引用
而func()的this不是obj,而且是不确定的,可能是全局对象也可能是null,这和 所属作用域有关
 
4.总结
现在我们就来回答开篇的问题:
1. this是什么?一个引用?
答:this是一个表达式,动态执行,并返回一个值,可能是引用可能是值
2. this对象存储在哪里?
答:this存储在作用域中,但是不是每个作用域都会存储this
3.根据规范,箭头函数执行的时候,是没有自己this的,但是为什么能访问到this呢?
答:执行this表达式的时候,如果当前作用域没有this,那么就会到外部作用域寻找this
4.obj.func() 和 let func = obj.func; func(); 其中this不同,但是调用函数是一样的,这是如何实现的呢?
答:请看第3节
 
影响绑定this对象有几个方面:
1.是否是严格模式:决定thisMode为strict
2.声明是否是箭头函数,决定thisMode为lexical
3.运行时,根据调用CallExpression之前得到的引用来影响this
4.通过内置方法:call,apply,bind
 
引用:
 
 

this 和作用域

原文:https://www.cnblogs.com/tanghansan/p/14393426.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!