阅读更多

1顶
0踩

编程语言
前言
在《JavaScript深入之执行上下文栈》中讲到,当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。

对于每个执行上下文,都有三个重要属性
  • 变量对象(Variable object,VO)
  • 作用域链(Scope chain)
  • this
今天重点讲讲this,然而不好讲。
……
因为我们要从ECMASciript5规范开始讲起。

先奉上ECMAScript 5.1规范地址:

英文版:http://es5.github.io/#x15.1
中文版:http://yanhaijing.com/es5/#115

让我们开始简单的了解规范吧!
Types
首先是第8章Types:
引用
Types are further subclassified into ECMAScript language types and specification types.
An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language. The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.
A specification type corresponds to meta-values that are used within algorithms to describe the semantics of ECMAScript language constructs and ECMAScript language types. The specification types are Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, and Environment Record.

我们简单的翻译一下:
ECMAScript的类型分为语言类型和规范类型。
ECMAScript语言类型是开发者直接使用ECMAScript可以操作的。其实就是我们常说的Undefined, Null, Boolean, String, Number, 和 Object。
而规范类型相当于meta-values,是用来用算法描述ECMAScript语言结构和ECMAScript语言类型的。规范类型包括:Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, 和 Environment Record。

没懂?没关系,我们重点看其中的Reference类型。

Reference
那什么又是Reference?

让我们看8.7章 The Reference Specification Type:
引用
The Reference type is used to explain the behaviour of such operators as delete, typeof, and the assignment operators.

所以Reference类型就是用来解释诸如delete、typeof以及赋值等操作行为的。

抄袭尤雨溪大大的话,就是:
引用
这里的 Reference 是一个 Specification Type,也就是 “只存在于规范里的抽象类型”。它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的 js 代码中。

再看接下来的这段具体介绍Reference的内容:
引用
A Reference is a resolved name binding.
A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag.
The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1).
A base value of undefined indicates that the reference could not be resolved to a binding. The referenced name is a String.

这段讲了Reference有三个组成部分,分别是:
  • base value
  • referenced name
  • strict reference
而且base value是undefined, an Object, a Boolean, a String, a Number, or an environment record其中的一种reference name是字符串。

可是这些到底是什么呢?

让我们简洁的理解base value是属性所在的对象或者就是EnvironmentRecord,referenced name就是属性的名称

嗯,举个例子:
var foo = 1;

var fooReference = {
  base: EnvironmentRecord,
  name: 'foo',
  strict: false
};

再举个例子:
var foo = {
  bar: function () {
    return this;
  }
};
 
foo.bar(); // foo

var fooBarReference = {
  base: foo,
  propertyName: 'bar',
  strict: false
};

而且规范中还提供了可以获取Reference组成部分的方法,比如 GetBase 和 IsPropertyReference

这两个方法很简单,简单看一看:
1.GetBase
引用
GetBase(V). Returns the base value component of the reference V.

返回reference的base value

2.IsPropertyReference
引用
IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.

简单的理解:base value是object,就返回true

GetValue
除此之外,紧接着规范中就讲了一个GetValue方法,在8.7.1章

简单模拟GetValue的使用:
var foo = 1;

var fooReference = {
  base: EnvironmentRecord,
  name: 'foo',
  strict: false
};

GetValue(fooReference) // 1;

GetValue返回对象属性真正的值,但是要注意,调用GetValue,返回的将是具体的值,而不再是一个Reference,这个很重要。

那为什么要讲References呢?

如何确定this的值
看规范11.2.3 Function Calls。

这里讲了当函数调用的时候,如何确定this的取值

看第一步 第六步 第七步:
引用
1.Let ref be the result of evaluating MemberExpression.
6.If Type(ref) is Reference, then
  a.If IsPropertyReference(ref) is true, then
     i.Let thisValue be GetBase(ref).
  b.Else, the base of ref is an Environment Record
     i.Let thisValue be the result of calling the ImplicitThisValue concrete      method of GetBase(ref).
7.Else, Type(ref) is not Reference.
  a. Let thisValue be undefined.

让我们描述一下:
1.计算MemberExpression的结果赋值给ref
2.判断ref是不是一个Reference类型,
2.1.如果ref是Reference,并且IsPropertyReference(ref)是true, 那么this = GetBase(ref) 2.2.如果ref是Reference,并且base值是Environment Record, 那么this = ImplicitThisValue(ref), 2.3.如果ref不是Reference,那么 this = undefined

让我们一步一步看:
1、计算MemberExpression

什么是MemberExpression?看规范11.2 Left-Hand-Side Expressions:

MemberExpression :
  • PrimaryExpression // 原始表达式 可以参见《JavaScript权威指南第四章》
  • FunctionExpression // 函数定义表达式
  • MemberExpression [ Expression ] // 属性访问表达式
  • MemberExpression . IdentifierName // 属性访问表达式
  • new MemberExpression Arguments // 对象创建表达式
举个例子:
function foo() {
    console.log(this)
}

foo(); // MemberExpression是foo

function foo() {
    return function() {
        console.log(this)
    }
}

foo()(); // MemberExpression是foo()

var foo = {
    bar: function () {
        return this;
    }
}

foo.bar(); // MemberExpression是foo.bar

所以简单理解MemberExpression其实就是()左边的部分

接下来就是判断MemberExpression的结果是不是Reference,这时候就要看规范是如何处理各种MemberExpression,看规范规定这些操作是不是会返回一个Reference类型。

举最后一个例子:
var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}

//试验1
console.log(foo.bar());
//试验2
console.log((foo.bar)());
//试验3
console.log((foo.bar = foo.bar)());
//试验4
console.log((false || foo.bar)());
//试验5
console.log((foo.bar, foo.bar)());

在试验1中,MemberExpression计算的结果是foo.bar,那么foo.bar是不是一个Reference呢?

查看规范11.2.1 Property Accessors,这里展示了一个计算的过程,什么都不管了,就看最后一步
引用
Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.

返回了一个Reference类型!

该值为:
var Reference = {
  base: foo,
  name: 'bar',
  strict: false
};

然后这个因为base value是一个对象,所以IsPropertyReference(ref)是true,

那么this = GetBase(ref),也就是foo, 所以this指向foo,试验1的结果就是 2

唉呀妈呀,为了证明this指向foo,累死我了!

剩下的就很快了:

看试验2,使用了()包住了foo.bar

查看规范11.1.6 The Grouping Operator
引用
Return the result of evaluating Expression. This may be of type Reference.
NOTE This algorithm does not apply GetValue to the result of evaluating Expression.

实际上()并没有对MemberExpression进行计算,所以跟试验1是一样的。

看试验3,有赋值操作符 查看规范11.13.1 Simple Assignment ( = ):

计算的第三步:
引用
3.Let rval be GetValue(rref).

因为使用了GetValue,所以返回的不是reference类型,this为undefined

看试验4,逻辑云算法

查看规范11.11 Binary Logical Operators:

计算第二步:
引用
2.Let lval be GetValue(lref).

因为使用了GetValue,所以返回的不是reference类型,this为undefined

看试验5,逗号操作符 查看规范11.14 Comma Operator ( , )

计算第二步:
引用
2.Call GetValue(lref).

因为使用了GetValue,所以返回的不是reference类型,this为undefined

但是注意在非严格模式下,this的值为undefined的时候,其值会被隐式转换为全局对象。

所以最后一个例子的结果是:
var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}

//试验1
console.log(foo.bar()); //2
//试验2
console.log((foo.bar)()); //2
//试验3
console.log((foo.bar = foo.bar)()); //1
//试验4
console.log((false || foo.bar)()); //1
//试验5
console.log((foo.bar, foo.bar)()); //1

注意:严格模式下因为this返回undefined,所以试验3会报错

最后,忘记了一个最最普通的情况:
function foo() {
    console.log(this)
}

foo(); 

MemberExpression是foo,解析标识符 查看规范10.3.1 Identifier Resolution

会返回一个 Reference类型

但是 base value是 Environment Record,所以会调用ImplicitThisValue(ref)

查看规范10.2.1.1.6

始终返回undefined

所以最后this的值是undefined

最后
尽管我们不可能去确定每一个this的指向都从规范的角度去思考,久而久之,我们就会总结各种情形来告诉大家这种情形下this的指向,但是能从规范的角度去看待this的指向,绝对是一个不一样的角度,该文还是有些晦涩难懂,希望大神指正!

更多
JavaScript深入系列的其他文章可以在 https://github.com/mqyqingfeng/Blog 查看
来自: github
1
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 微软面试题(很动脑哦!附答案、解析及英文原版)

    微软的面试题-超变态但是很经典 (附答案、解析及英文原版)

  • 英文面试问答

    1.自我介绍 Good  afternoon. It is really my honor to have this opportunity for an interview. I hope I can make a good performance today. My chinese name is wanghuan,my english name is Bonnie. I graduated

  • 2020.10.20英语前端电话面试总结

    2020.10.20英语前端电话面试总结 人生第二次前端面试,这次面的这个前端岗位是需要有很好的英语口语沟通能力的,我英语很差,面试官让我用英语自我介绍几句就说了解到了我的水平了,嘤嘤嘤···所以英语这方面就没什么好说的了,我也知道自己英语差~ 所以还是记录一下问到的技术方面的题目吧 1. 对函数式编程的理解 【我的回答】:函数式编程是一种编程范式,相同的输入永远得到相同的输出,不会产生副作用。 【面试官】:嗯··· (我答得不是很全面) 参考答案: 阮一峰老师《函数式编程初探》: "函数式编程"是一种"

  • 英语面试常用口语900句

    (一) 高频词汇: 可以拿来形容自己的形容词。除开我们都熟知的一些基本的词汇可以用来形容自己,比如honest, reliable, trustworthy等,我们还可以运用一些“高级词汇”。   用形容词的形式来形容自己 1. committed 投入的,坚决的 例句:I am committed to meeting deadlines. 我致力于在截止期限前完成工作。 ...

  • 常见前端面试题人话翻译版

    通俗理解面试题 秋招冲冲冲

  • 外企必备面试题

    我就读于华东理工大学,并于2019年毕业,获得软件工程学士学位学位,在那里,我学了很多理论课程,如:HTML5, Java,数据结构,计算机网络,软件测试等大部分都是由外教授课,我最喜欢的外教是kyle Cox/凯尔·考克斯/,他来自美国佛罗里达州。我喜欢我参与的项目,也喜欢和我一起工作的同事,但在某些时候,我意识到我不再像以前那样受到挑战了。我想说的是,虽然我不喜欢一个特定的环境,但我对在一个更具协作性的环境中工作感兴趣,在这种环境中,人们致力于把事情做好,对工作充满热情。所以我认为我非常适合这个职位。

  • 前端面试题总结(二)

    前端面试题总结(二)

  • WEB前端面试题-2021 不定期更新

    前端面试题是这样的,我是刚刚培训出来的,最近也是在忙面试,我把遇到的面试题给大家分享一下,希望会用得到(大佬可以直接右上角×掉了)自我介绍VUE生命周期元素 ,垂直水平居中如何改变文本的样式 是这样的,我是刚刚培训出来的,最近也是在忙面试,我把遇到的面试题给大家分享一下,希望会用得到(大佬可以直接右上角×掉了) 自我介绍 其实这部分我自己刚开始遇到的就很尴尬,英文刚开始的面试的时候,发现自我介绍就是介绍完自己什么姓名,年龄啊之类的就尬住了,不知道还可以说什么,所以以下,总结了一部分 自我介绍,姓名,年龄,

  • 老外的前端面试题

    用Twitter吗? 如果用,你在Twitter上面关注谁了? 用Github吗? 如果用,请列举几个你在上面关注的存储库(repos)。 你关注了什么博客? 你用过什么版本控制系统? 你偏爱的开发环境是什么?(操作系统,编辑器,浏览器,工具等等) 你能描述一下当你创建一张网页时的流程吗? 你能描述一下渐进增强和平...

  • 英文面试常见问题及回答汇总

    今天给大家奉上英文面试常见问题及回答的合集,英语面试主要就是学习如何用英语回答面试常见问题,英文面试常见问题说来说去其实重要的就那么几个,所以今天分享的英文面试常见问题及回答大家只要知道其套路,然后灵活运用就行了。 常见问题一:Why did you leave your last job?上一份工作为什么离职? 面试的时候最怕问这个问题,因为离职嘛,无非就是心委屈了,钱没给到位,可是这两...

  • 外企英文面试可能会用到的东西(我自己整理的)

    这个东西是自己整理的,在IBM和NNIT面试的时候都用到过,现在分享出来供参考!会存在语法等问题吧,口语说的东西别太介意问题1:关于你以后的工作打算,你有什么想法? It costs me some time to get familiar with the job, and on the basis of that, then I will try to improve my work. Sec

  • 英:英语面试常用口语900句

    英语面试常用口语900句 英语面试常用口语900句 2018年09月18日 12:13:11 Nathan_Sun 阅读数:9514 (一) 高频词汇: 可以拿来形容自己的形容词。除开我们都熟知的一些基本的词汇可以用来形容自己,比如honest, reliable, trustworthy等,我们还可以运用一些“高级词汇”。 用形容词的形式来形容自己 1. com...

  • 面试中可能用到的英语对话

     Q: Can you sell yourself in two minutes? Go for it. (你能在两分钟內自我推荐吗?大胆试试吧!)   A: With my qualifications and experience, I feel I am hardworking, responsible and diligent in any project I undertake. Y

  • 前端面试问题

    1.Ajax的get请求跟post请求有什么区别 1 GET请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息的实体内容发送给WEB服务器。 2.首先是”GET方式提交的数据最多只能是1024字节”,Post传输的数据量大,可以达到2M。 3.get方式请求数据会被浏览器缓存起来,因此其他人就可以从浏览器的历史记录中读取到这些数据,例如账号密码等。在某种情况下,get方式会...

  • 前端面试总结一

    1.页面的重构和回流 重构: 改变每个元素的外观时所触发的浏览器的行为,如颜色、背景等样式发生改变而进行的重新构造新外观的行为。重构不会引发页面的重新布局,不一定伴随着回流。 重构时需要注意: 页面的健壮性:在页面排版时需要考虑数据极多或者极少的情况。 页面的扩展性:要考虑未来添加子模块或兄弟模块的状态,为将来留好css、html扩展的出入口。在将来添加模块的时候,尽可能少的去动原来的html结构...

  • 面试常用中英文对照表

    1 Can you sell yourself in two minutes? Go for it. 你能在两分钟内自我推荐吗?大胆试试吧! With my qualifications and experience, I feel I am hardworking, responsible and diligent in any project I undertake. Your org

  • 外企面试中常用到的英语问答

    想找一份满意的工作吗?外企面试中面对外国老板连珠炮似的提问,有没有觉得心慌意乱、无所适从?求职过程中外企面试尤为重要,回答问题,如果能简明扼要,真诚中肯,合乎老外口味,那么录取机会必定大大增加。当然,有的问题,也要轻描淡写,以免言多必失。本篇摘录了若干外企面试中出现频率较高的问题及精彩回答,希望能在您求职路上助您一臂之力。   Q:Can you sell yourself in two mi...

  • 6个经典的英语面试问题

    6个经典的英语面试问题 作者: 【taylor】 发帖日期:2008-01-21 14:56 浏览: 15 鲜花: 0 鸡蛋: 0 "work experience" is the type of work you’ve done in the past. If you haven’t started working yet you can say "Right now I’m still a

  • 【前端面试指南】简历上的前端常用单词,你拼写对了吗?

    前端交流经常会用到很多专业的英文单词和术语,但有些同学并不注意其拼写格式,甚至有同学在简历上的拼写也不规范,以下是错误的书写方式和正确的书写,供大家参考: 错误拼写 正确拼写 Javascript JavaScript css CSS html HTML php PHP python Python vue.js Vue.js react.js React.j...

  • 前端面试题Front-end Job Interview Questions

    Front-end Job Interview Questions This file contains a number of front-end interview questions that can be used when vetting potential candidates. It is by no means recommended to use every single qu

Global site tag (gtag.js) - Google Analytics