抛出问题:
● 为什么有的编程规范要求用void 0 代替Undefined?
● 字符串有最大长度吗?
● 0.1+0.2不等于0.3吗?为什么js里面不是这样?
● es6中的Symbol是什么?
● 为什么给对象添加的方法能用在基本类型上
javascript规定了7种语言类型
其中最后一种Symbol是es6新增的
六种基本类型:
Number:数字
String:字符串
Boolean:true&fslse
Null:空,只有一个值,就是null表示空值,是关键字,可以放心使用null关键字来获取null值
Undefined:未定义
Symbol:独一无二的值
访问:基本数据类型的值是按值访问的
存储:基本类型的变量是存在堆内存里(stack)面
图解:
栈内存中包括了变量的标识符和变量的值
一种引用类型:
Object:对象
访问:引用类型的值是按照引用访问的
存储:引用类型的值是保存在堆内存(stack)中的对象(object)
图解:
堆内存中包含了变量的标识符和堆内存中指向该变量的对象
堆内存中包含了对象的内容
undefined、null
Q:为什么有的编程规范要求用void 0 代替Undefined?
javascript对象的未定义过的属性得到的值都是undefined
undefined类型表示未定义,它的类型只有一个值,就是undefined。任何变量在赋值前是undefined类型,值是undefined。一般可以用全局变量undefined来表达这个值,或者用void运算来把任意一个表达式值变为undefined。
但是,javascript里面的undefined是一个变量而不是关键字,这是javascript语言公认的失误之一,所以为了避免无意中被篡改,建议用void 0来获取undefined值。
null值类型也只有一个值,就是null,它的语义表示空值,与undefined不同,null是javascript的关键字,可以放心的使用null来获取null值。
Boolean
Boolean类型具有两个值,true和false,表示逻辑上的真和假,同样就具有两个关键字true和false来表示。
String
Q:字符串有最大长度吗?
String表示文本数据,String有最大长度2^53 - 1,这个所谓的最大长度,并不完全是我们理解中的字符数。因为String的意义并非“字符串”,而是字符串的UTF16编码,我们字符串的操作charAt、charCodeAt、length等方法针对的都是UTF16编码。所以字符串的最大长度,实际是受字符串的编码影响的。
注:现行的字符集国际标准,字符是以Unicode的方式表示的,每一个Unicode的码点表示一个字符,理论上,Unicode的范围是无限的。UTF是Unicode的编码方式,规定了码点在计算机中的表示方法,常见的有UTF16和UTF8。Unicode 的码点通常用 U+??? 来表示,其中 ??? 是十六进制的码点值。 0-65536(U+0000 - U+FFFF)的码点被称为基本字符区域(BMP)
JavaScript 字符串把每个 UTF16 单元当作一个字符来处理,所以处理非 BMP(超出 U+0000 - U+FFFF 范围)的字符时,你应该格外小心。
Number
Number类型表示通常意义上的“数字”。这个数字大致对应数学中的有理数,当然在计算机中,有一定的精度限制。
avaScript 中的 Number 类型有 18437736874454810627(即 264-2+3) 个值。
JavaScript 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则,但是 JavaScript 为了表达几个额外的语言场景(比如不让除以 0 出错,而引入了无穷大的概念),规定了几个例外情况:
● NaN,占用了 9007199254740990,这原本是符合 IEEE 规则的数字;
● Infinity,无穷大;
● -Infinity,负无穷大。
另,javascript中有+0和-0,在加法类的运算中它们没有区别,但是除法的场合则需要特别留意区分,“忘记检测除以 -0,而得到负无穷大”的情况经常会导致错误,而区分 +0 和 -0 的方式,正是检测 1/x 是 Infinity 还是 -Infinity。
根据双精度浮点数的定义,Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff,所以 Number 无法精确表示此范围外的整数。
同样根据浮点数的定义,非整数的 Number 类型无法用 (= 也不行) 来比较,
NaN和+Infinity的规定实际是IEEE 754标准规定的特殊值:
(e为指数的位数,双精度中e=11)
● 指数为2^e – 1且尾数的小数部分全0,这个数字是±∞。(符号位决定正负)
● 指数为2^e – 1且尾数的小数部分非全0,这个数字是NaN,比如单精度浮点数中按位表示:S111 1111 1AXX XXXX XXXX XXXX XXXX XXXX,S为符号位值无所谓,A是小数位的最高位(整数位1省略),其取值表示了NaN的类型:X不能全为0,并被称为NaN的payload
关于NaN规定和参考双精度浮点数的表达方式,尾数共有53位,指数固定为2^e – 1并去掉±∞两个值,那么NaN其实是 2^53-2 个特殊数字的合集(2^53-2 = 9007199254740990 );
并不是 9007199254740990 被占用,而是 9007199254740990 个特殊值被占用来表示 NaN
扩展一下,我们就可以理解为什么NaN !== NaN了,它确实不是一个值,而是一群值
第三题的问题,为什么在 JavaScript 中,0.1+0.2 不能 =0.3:
结果是 false,说明两边不相等的,这是浮点运算的特点,原因是:浮点数运算的精度问题导致等式左右的结果并不是严格相等,而是相差了个微小的值。
console.log(0.1+0.2==0.3); // false
检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法
console.log(Math.abs(0.1+0.2-0.3)<= Number.EPSILON);// true
Symbol
Symbol是es6中引入的新类型,它是一切非字符串的对象key的集合,在ES6规范中,整个对象系统被用Symbol重塑。
Symbol可以具有字符串类型的描述,但是即使描述形同,Symbol也不相等。
创建Symbol的方式是使用全局的Symbol函数。
let t_symbol = Symbol('test symbol');
一些标准中提到的 Symbol,可以在全局的 Symbol 函数的属性中找到。例如,我们可以使用 Symbol.iterator 来自定义 for…of 在对象上的行为:
var o = new Object();
o[Symbol.iterator] = function(){
var v = 0
return {
next: function(){
return {value:v++,done: v>10}
}
}
}
for (var v of o){
console.log(v); // 0 1 2 3 ...9
}
代码中我们定义了 iterator 之后,用 for(var v of o) 就可以调用这个函数,然后我们可以根据函数的行为,产生一个 for…of 的行为。
这里我们给对象 o 添加了 Symbol.iterator 属性,并且按照迭代器的要求定义了一个 0 到 10 的迭代器,之后我们就可以在 for of 中愉快地使用这个 o 对象啦。
这些标准中被称为“众所周知”的 Symbol,也构成了语言的一类接口形式。它们允许编写与语言结合更紧密的 API。
Object
概念:
Object 是 JavaScript 中最复杂的类型,也是 JavaScript 的核心机制之一。Object 表示对象的意思,它是一切有形和无形物体的总称。
问题:为什么给对象添加的方法能用在基本类型上?
在 JavaScript 中,对象的定义是“属性的集合”。属性分为数据属性和访问器属性,二者都是 key-value 结构,key 可以是字符串或者 Symbol 类型。
提到对象,我们必须要提到一个概念:类。
因为 C++ 和 Java 的成功,在这两门语言中,每个类都是一个类型,二者几乎等同,以至于很多人常常会把 JavaScript 的“类”与类型混淆。
事实上,JavaScript 中的“类”仅仅是运行时对象的一个私有属性,而 JavaScript 中是无法自定义类型的。
JavaScript 中的几个基本类型,都在对象类型中有一个“亲戚”。它们是:
● Number;
● String;
● Boolean;
● Symbol。
所以,我们必须认识到 3 与 new Number(3) 是完全不同的值,它们一个是 Number 类型, 一个是对象类型。
Number、String 和 Boolean,三个构造器是两用的,当跟 new 搭配时,它们产生对象,当直接调用时,它们表示强制类型转换。
Symbol 函数比较特殊,直接用 new 调用它会抛出错误,但它仍然是 Symbol 对象的构造器。
JavaScript 语言设计上试图模糊对象和基本类型之间的关系,我们日常代码可以把对象的方法在基本类型上使用,比如:
console.log("abc".charAt(0)); //a
● 1
甚至我们在原型上添加方法,都可以应用于基本类型,比如以下代码,在 Symbol 原型上添加了 hello 方法,在任何 Symbol 类型变量都可以调用。
Symbol.prototype.hello = () => console.log("hello");
var a = Symbol("a");
console.log(typeof a); //symbol,a 并非对象
a.hello(); //hello,有效