从规范看ECMAScript(二):数据类型
栏目: JavaScript · 发布时间: 5年前
内容简介:ES里的数据类型分两种。一种是一种是
ES里的数据类型分两种。
一种是 语言类型
,就是我们平常写的 12,'xccurate',undefined
等等类型。
一种是 规范类型
,是用来描述ECMAScript语言结构和ECMAScript语言类型的值,例如引用 Reference
,列表 List
等等。
先来看看基本的语言类型吧。
语言类型
ES的语言类型有七种:
-
Undefined
类型:只有一个值:undefined
。 -
Null
类型:只有一个值:null
。 -
Boolean
类型:有两个值:true
和false
。 -
String
类型:是零个或多个16位无符号整数值的所有有序序列的集合,最大长度为2^53 -1个元素。它的严格定义就不深究了,想了解的看:link:。 -
Symbol
类型:每个值都是独一无二的,例如Symbol('s1mple')
。规范中定义了一些内置的Symbol值:link:。 -
Number
类型:一共18437736874454810627个值,包括NaN
、+Infinity
、-Infinity
。详细定义看:link:。 -
Object
类型:例如:let a = {}
。这就得好好说说了,见下方。
Object类型
对象是属性 property
的集合。
属性使用键值对来标识,属性的键的值可以为 String
类型(可以为空 ''
)或 Symbol
类型。
属性分为两种:
-
数据属性
data property
:将键值与一个 ES语言值 和一组 布尔属性 相关联。 -
访问器属性
accessor property
:将键值与一个或两个 访问器函数 和一组 布尔属性 相关联。访问器函数用于存储或检索与属性关联的ECMAScript语言值。
所有对象都是属性的逻辑集合。但是有很多种对象在访问和操作其属性的语义上有所不同。
普通对象(Ordinary objects)是最常见的对象形式,具有默认对象语义。
怪异对象(exotic object)是其属性语义与默认语义不同的任何形式的对象。
属性的特性
特性 attribute
用来定义和解释对象属性的状态。
其中数据属性的特性有: [[Value]]
、 [[Writable]]
、 [[Enumerable]]
、 [[Configurable]]
。
访问器属性的特性有: [[Get]]
、 [[Set]]
、 [[Enumerable]]
、 [[Configurable]]
。
它们的意义也不多说了,随便一查都有,不是重点。
对象的内部方法与内部插槽
内部方法 Object Internal
:其算法制定了对象的实际语义。不是ES语言的一部分,由规范定义,仅用于说明。内部方法是多态的,不同的对象可能会执行不同的算法。
内部插槽 Internal Slots
:是给算法使用的内部状态。不是对象属性,不继承。其值可能为规范类型或者语言类型。(为了方便理解,其实可以把它看作内部属性,不对开发者可见。而且内部插槽这个名字实在太别扭了:joy:,下文就叫做内部属性了。)
这两个都是规范定义的用来描述ES对象行为的东西,可以简单的理解为对象的函数和属性。
**内部方法和内部属性均用双方括号标识: [[xxxxx]]**
这个表格:link:总结了对象的必要的内部方法,每个对象都必须有其算法,然而却不一定相同。
简略说一下有什么:
[[GetPrototypeOf]]
、 [[SetPrototypeOf]]
:获取和设置对象的原型 prototype
。
[[IsExtensible]]
:决定是否可以向此对象添加其他属性。
[[PreventExtensions]]
:决定是否可以将新属性添加到此对象。// 这个跟上面那个有什么区别还不太清楚
[[GetOwnProperty]]
、 [[DefineOwnProperty]]
:获取和设置对象(名称为参数的)属性的属性描述符。
[[HasProperty]]
:判断对象是否有自己的或继承的(名称为参数的)属性。
[[Get]]
、 [[Set]]
:获取和设置(名称为参数的)属性的值。
[[Delete]]
:删除对象(名称为参数的)属性。
[[OwnPropertyKeys]]
:返回对象自身的属性的列表。
上篇说过,函数是可调用对象。所以其具有额外的内部方法: [[Call]]
。而构造函数具有额外的内部方法: [[Construct]]
。
也就是说函数执行时最终调用的就是函数对象的 [[Call]]
方法,构造函数构造对象时,执行的是构造函数的 [[Construct]]
方法。
它们的的实际语义到时候再仔细分析。
接下来一小节:link:讲的是这些基本内部方法不变的行为。例如返回类型的规定,等等。这些规则是所有对象必须遵守的,尽管其算法有可能不同。这些边边角角不用开发者操心,就不详述了。
内在对象
内在对象 Intrinsic Objects
是规范中的算法要引用到的内置对象。像 Array
、 String
、 Object
及其原型等等。
详细见表:link:,就先不列举了,等到用到的时候再说。
多说一句,这组内置对象是每个领域 Realm
中有一组。什么是领域呢,例如一个页面中的 iframe
中的 Array
对象与该页面中的 Array
对象就不一样,它们就是两个领域的。
规范类型
规范类型是在规范中使用的,用来描述ES语义的、无法在规范外使用类型。
List和Record类型
List类型用来解释函数参数列表的执行。List类型的值是包含单个值的列表元素的简单有序序列。
其实可以简单理解为ES的数组,但是写规范的时候还不存在ES的数组类型,只好用List来描述像数组这样的类型。例如,«1,2»定义了一个List值,它具有两个元素,每个元素被初始化为一个特定的值。 一个新的空列表可以表示为«»。
(在上篇提到的ES解释器engine262中List实现就是用数组来实现的。)
Record类型用于描述规范算法中的数据聚合。Record类型值由一个或多个命名字段组成。 每个字段的值都是ECMAScript值或由与Record类型相关联的名称表示的抽象值。 字段名称始终用双括号括起来,例如 [[value]]
。
这个就像是ES里的对象了,使用也很像: R.[[Field2]]
是名为 [[Field2]]
的R的字段的缩写。
(在engine262里Record就是用对象来实现的。)
Set 和 Relation类型
Set类型用于解释内存模型中使用的无序元素集合,其中没有元素出现超过一次。元素可以添加到集合中,也可以从集合中移除。集合可以合并、交叉或从彼此中减去。
Relation类型用于解释Set上的约束。
// 这两个我还没遇到过,不太懂就不说了:joy:。
Completion Record 类型
Completion Record类型用来解释值和控制流的运行时传播。
ECMAScript规范中的每个运行时语义都显式或隐式返回一个报告其结果的完成Completion Record。
(这个定义读起来就很拗口,不过不是很重要,简单理解一下就行。)
Completion Record是一个Record,所以就可以用对象来描述它,它有三个字段:
Completion Record = { [[type]] // Completion的类型,有 normal, break, continue, return, throw 5种类型 [[value]] // 返回的值为ES语言值或空,仅当当[[type]] 为 normal,return, throw时有值 [[target]] // 定向控制转移的目标label,为string或空,仅当[[type]] 为break, continue时有值 } 复制代码
[[type]]
为 normal
时返回的Completion Record称作 normal completion
。其余的都称为 abrupt completion
。
在规范的各种抽象操作(也叫算法)中会有这样的返回 Return Infinity
,这就是隐式返回了一个Completion Record,相当于 Return NormalCompletion("Infinity")
。
规范在算法中经常有 ? Call(exoticToPrim, input, « hint »)
这样以 ?
开头的语句,它的意思如下:
让 res
为执行 Call(exoticToPrim, input, « hint »)
抽象操作的结果。
上面说过,每个运行时语义都显示或隐式的返回一个Completion Record,所以
如果 res
为 abrupt completion
,返回 res
。
如果 res
为 Completion Record
, res = res.[[value]]
。
你还会看到 ! ToString(key)
这样前缀为 !
的语句,它的意思如下:
让res为ToString(key)
断言res不是 abrupt completion
如果 res
为 Completion Record
, res.[[value]] = res
。
这两个不一样哦,注意看最后的赋值语句。
在阅读规范时,可暂时忽略其实际意义。
Reference类型
Reference类型用来解释诸如 delete
, typeof
,赋值运算符, super
关键字和其他语言特征等运算符的行为。
(在我看来,就是LHS,为了解决谁在哪里这样的问题)
一个Reference是解析的名称或属性绑定。Reference由三个组件组成:
{ BaseValue // 值为:undefined,Object,Boolean,String,Symbol,Number, Environment Record. ReferencedName // String或Symbol值。 StrictReference // 布尔值,标识是否为严格模式 } 复制代码
base
值为 undefined
时表示无法解析该引用。
还有Super Reference, 用于表示使用super关键字表达的名称绑定。有个额外的 thisValue
组件,其 base
值永远不会为 Environment Record
。
举个栗子:
let a = { b: 'test' } 复制代码
其中,查找b时就会得到一个Reference类型,其值为:
{ BaseValue: a, ReferencedName: b, StrictReference: false } 复制代码
规范还规定了对于它的一些抽象操作,例如 GetBase
、 GetReferencedName
、 IsStrictReference
等等。
简单说下吧:
-
GetBase ( V )
:V是一个Reference。获取V的base
组件。 -
GetReferencedName ( V )
:V是一个Reference。获取V的name
组件。 -
IsStrictReference ( V )
:V是一个Reference。获取V的strict
组件。 -
HasPrimitiveBase ( V )
:V是一个Reference。用来判断V的base组件的值是否是这几个原始值Boolean,String,Symbol,Number
。 -
IsPropertyReference ( V )
:V是一个Reference。用来判断V是否是属性Reference,即其base值为Object或者HasPrimitiveBase ( V )
为true。 -
IsUnresolvableReference ( V )
:V是一个Reference。用来判断是否是无法解析的Reference,即base
值为undefined
。 -
IsSuperReference ( V )
:V是一个Reference。就是判断V是不是Super Reference,即有没有thisValue
组件。 -
GetValue ( V )
:如果V不是Reference,就直接返回V
是Reference,
是无法解析的Reference,就抛出ReferenceError 错误。(就是平常遇到的变量未定义错误啦
Uncaught ReferenceError: a is not defined
) 是属性Reference,
如果是base原始值
Boolean,String,Symbol,Number
, 就让base = ToObject(base)。:o:1(以后有红圈的地方就是有批注的地方,圈起来,重点:joy:)
返回
base.[[Get]](GetReferencedName(V), GetThisValue(V))
。 那么Reference一定为
Environment Record
, 返回
base.GetBindingValue(GetReferencedName(V), IsStrictReference(V))
。:o:1:这个地方就是为什么
string、number
不是对象却能够使用String、Number
对象上的方法的原因了。比如使用
'SOMEBODY'.toLocaleLowerCase()
时要先进行LHS查询找到'SOMEBODY'.toLocaleLowerCase
是什么,才能执行函数。此时的Reference就是:{ BaseValue: 'SOMEBODY', ReferencedName: toLocaleLowerCase, StrictReference: false } 复制代码
判断
'SOMEBODY'
是原始值,就进行ToObject
,这个操作会将其变成一个String对象,再获取toLocaleLowerCase
方法。当Reference为
Environment Record
时,由于Environment Record
还没讲,不说太多,大概就是获取Environment Record绑定值。 -
PutValue ( V, W )
:如果V不是Reference,抛出ReferenceError 。
如果无法解析Reference,
如果是严格模式,抛出ReferenceError 。
否则,:o:2
获取全局对象
GlobalObject
, 执行
Set(globalObj, GetReferencedName(V), W, false)
操作,返回其结果。如果是属性Reference,
如果是
base
原始值Boolean,String,Symbol,Number
,就ToObjet
。 执行
base.[[Set]](GetReferencedName(V), W, GetThisValue(V))
操作, 如果返回值为
false
,且为严格模式,抛出ReferenceError 。 那么一定是Environment Record,
执行
base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V))
。返回其结果。:o:2:这个地方就是平常我们没声明变量直接进行赋值操作
a = 1
时的算法,会在全局对象上新建一个属性,并赋给其值。当Reference为 Environment Record时,大概进行的操作就是在类型为Environment Record 的
base
上创建一个可变绑定,并赋值。 -
GetThisValue ( V )
:当为SuperReference时返回thisValue
组件的值。 -
InitializeReferencedBinding ( V, W )
:此时base
为Environment Record,执行InitializeBinding(GetReferencedName(V), W)
操作,就是初始化绑定。
总算讲完了,Reference类型算是比较重要的一种规范类型了,理解ES运行时语义绕不开它,所以讲了很多,不像Completion Record类型,没讲太多,一方面对于理解语义无关紧要,一方面是我也不太懂:joy:。
Property Descriptor类型
属性描述符类型,用来解释对象属性的特性的操作,其值为Record类型,分为数据属性描述符和访问器属性描述符。
它的抽象操作我就不多说了,自己了解下就行了:link:。
Lexical Environment 类型 Environment Record类型
这两个是大部头,按下不表,下面有一节专门介绍,主要是用来描述ES里作用域,标识符绑定等等。
额外说一句,我看到现在很多教程里都还在用活动对象(Activation Object)来解释作用域、闭包时,还纳闷怎么没在规范里看到,后来一搜才发现,AO、VO是ES3规范里的东西,打ES5后就再也没这俩词了。。
Data Blocks
用来描述字节大小(8位)数值的不同且可变的序列。暂时我也不懂,就先不说了:joy:。
这篇就先结束到这吧,本来打算把下章抽象操作也讲完的,一写下来发现有点长,就下篇讲吧。下篇主要是类型测试、类型转换,还有对象上的基本操作。
最后如果对大家有用的话,求关注、star,github 仓库 :link: 。:pray::pray:
以上所述就是小编给大家介绍的《从规范看ECMAScript(二):数据类型》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【数据类型】js的数据类型
- Python中不可变数据类型和可变数据类型
- 重学JS: 数据类型及类型检测
- JS专题之数据类型和类型检测
- 6. Go 语言数据类型:字典与布尔类型
- Redis 入门-数据类型:3 种特殊类型详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
"笨办法"学Python
肖 (Zed A.Shaw) / 王巍巍 / 人民邮电出版社 / 2014-11-1 / CNY 49.00
本书是一本Python入门书籍,适合对计算机了解不多,没有学过编程,但对编程感兴趣的读者学习使用。这本书以习题的方式引导读者一步一步学习编程,从简单的打印一直讲到完整项目的实现,让初学者从基础的编程技术入手,最终体验到软件开发的基本过程。 本书结构非常简单,共包括52个习题,其中26个覆盖了输入/输出、变量和函数三个主题,另外26个覆盖了一些比较高级的话题,如条件判断、循环、类和对象、代码测......一起来看看 《"笨办法"学Python》 这本书的介绍吧!