文章目录
关于类型的细节
关于Javascript的类型,各种文档介绍的已经很全面了,也是前端开发人员最熟悉的概念之一,但如果对下面的几个问题还有犹豫,说明对于类型这部分知识点,还是有遗漏部分的。
为什么有的编程规范要求用 void 0 代替 undefined?
字符串有最大长度吗?
String有最大长度2^53-1
,但是这个所谓的最大长度并不是字符数。
String的意义并非“字符串”,而是字符串的UTF16编码,字符串操作charAt
, charCodeAt
, length
等方法针对的都是UTF16编码,所以字符串的最大长度,实际上是受字符串的编码长度影响的。
JavaScript 中的字符串是永远无法变更的,一旦字符串构造出来,无法用任何方式改变字符串的内容,所以字符串具有值类型的特征。
0.1 + 0.2 ≠ 0.3 ?
Javascript的Number类型表示通常意义上的“数字”。大致对应数学中的有理数,但是计算机有精度限制。
avaScript 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则。(Javascript并未定义不同类型的数字数据类型,而是始终将数字存储为双精度浮点值)
根据双精度浮点数的定义,Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff,所以 Number 无法精确表示此范围外的整数。
同样根据浮点数的定义,非整数的 Number 类型无法用 ==
(===
也不行) 来比较。
console.log( 0.1 + 0.2 == 0.3); //false
对于计算机而言,两个数字在相加时是以二进制形式进行的,在呈现结果时才转换成十进制。所以在计算0.1+0.2
时,会先把两个数字转换为二进制数字,转二进制的时候如果尾数的52位不能完全满足,只会达到要求的精度。
// 0.1 转化为二进制
0.0 0011 0011 0011 0011...(0011无限循环)
// 0.2 转化为二进制
0.0011 0011 0011 0011 0011...(0011无限循环)
//由于双精度表示法的尾数只有52位,0.1和0.2转换后的值为
e = -4; m =1.1001100110011001100110011001100110011001100110011010 (52位)
e = -3; m =1.1001100110011001100110011001100110011001100110011010 (52位)
两个二进制相加的结果为:
//相加时如果指数不一致,需要对齐,一般情况下是向右移,因为最右边的即使溢出了,损失的精度远远小于左边溢出。
(e = -3; m = 0.1100110011001100110011001100110011001100110011001101)
+ (e = -3; m = 1.1001100110011001100110011001100110011001100110011010)
= (e = -3; m = 10.0110011001100110011001100110011001100110011001100111)
= (e = -2; m = 1.0011001100110011001100110011001100110011001100110100)
= 0.010011001100110011001100110011001100110011001100110100
= 0.30000000000000004 //(十进制)
浮点数运算的精度问题导致等式左右的结果并不是严格相等,而是相差了个微小的值0.30000000000000004
与0.3
。
用Javascript提供的最小精度值比较可得到结果为true
:
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);
ES6 新加入的 Symbol 是个什么东西?
Symbol 是 ES6 中引入的新类型,它是一切非字符串的对象 key 的集合,在 ES6 规范中,整个对象系统被用 Symbol 重塑。Symbol 可以创建一个独一无二的值(但并不是字符串)。
Symbol 可以具有字符串类型的描述,但是即使描述相同,Symbol 也不相等。
创建Symbol的方式是使用全局的Symbol函数。
var mySymbol = Symbol("my symbol");
为什么给对象添加的方法能用在基本类型上?
在Javascript中,对象的定义是“属性的集合”,属性分为数据属性和访问器属性,二者都是key-value结构,key可以是字符串或Symbol类型。
Javascript的基本类型,都在对象类型中有一个“亲戚”,分别是Number,String,Boolean,Symbol,所以3
与new Number(3)
是完全不同的值,一个是Number类型,一个时对象类型。
Javascript的.
运算符提供了装箱操作,会根据基础类型构造一个临时的对象,使得我们能在基础类型上调用对应对象的方法。
所谓装箱转换,就是把基本类型转换为对应的对象,它是类型转换中相当重要的种类。
Javascript对象
对 JavaScript 来说,属性并非只是简单的名称和值,JavaScript 用一组特征(attribute)来描述属性(property)。
数据属性:
它比较接近于其它语言的属性概念。数据属性具有四个特征。
- value:就是属性的值。
- writable:决定属性能否被赋值。
- enumerable:决定 for in 能否枚举该属性
- configurable:决定该属性能否被删除或者改变特征值。在大多数情况下,我们只关心数据属性的值即可。第
访问器(getter/setter)属性,它也有四个特征: - getter:函数或 undefined,在取属性值时被调用
- setter:函数或 undefined,在设置属性值时被调用
- enumerable:决定 for in 能否枚举该属性
- configurable:决定该属性能否被删除或者改变特征值。
Javascript的对象分为几类: - 宿主对象(host Objects):由 JavaScript 宿主环境提供的对象,它们的行为完全由宿主环境决定。(如window对象)
- 内置对象(Built-in Objects):由 JavaScript 语言提供的对象。
- 固有对象(Intrinsic Objects ):由标准规定,随着 JavaScript 运行时创建而自动创建的对象实例。
- 原生对象(Native Objects):可以由用户通过 Array、RegExp 等内置构造器或者特殊语法创建的对象。
- 普通对象(Ordinary Objects):由{}语法、Object 构造器或者 class 关键字定义类创建的对象,它能够被原型继承。
Javascript执行
异步执行的顺序:
- 首先我们分析有多少个宏任务;
- 在每个宏任务中,分析有多少个微任务;
- 根据调用次序,确定宏任务中的微任务执行次序;
- 根据宏任务的触发规则和调用次序,确定宏任务的执行次序;
- 确定整个顺序。