JavaScript
七种数据类型
Undefined、null
*undefined表示此处应该有值,但是还没有被定义。由于在旧版本浏览器中undefined可以被重新赋值,因此做判断时推荐使用void(0)来表示undefined。然而,这一行为在2009年的ECMAScript 5被修复了。
15.1.1.3 undefined
The value of undefined is undefined (see 8.1). This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
undefined 的值是 undefined。这个属性有不可写,不可枚举,不可配置的特性。
- null在js中是一个关键字,表示定义了但是为空,即此处不应该有值。
Boolean
有两个值,true和false
String
- 用于表示文本数据,有最大长度2^53-1,但是这个长度并不是标准意义中的字符数。因为String的意义并非字符串,而是字符的UTF16编码。字符串的操作 charAt、charCodeAt、length 等方法针对的都是 UTF16 编码。所以,字符串的最大长 度,实际上是受字符串的编码长度影响的。
note: 编码:计算机中储存的信息都是用二进制数表示的;而我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果。通俗的说,按照何种规则将字符存储在计算机中,如'a'用什么表示,称为"编码";反之,将存储在计算机中的二进制数解析显示出来,称为"解码",如同密码学中的加密和解密。在解码过程中,如果使用了错误的解码规则,则导致'a'解析成'b'或者乱码。
note:
- 常见字符集名称:ASCII字符集、GBXXXX字符集、Unicode字符集等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。
ASCII字符集:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。 - Unicode编码系统为表达任意语言的任意字符而设计。它使用4字节的数字来表达每个字母、符号,或者表意文字(ideograph)。在计算机科学领域中,Unicode(统一码、万国码、单一码、标准万国码)是业界的一种标准,它可以使电脑得以体现世界上数十种文字的系统。
- 尽管有Unicode字符非常多,但是实际上大多数人不会用到超过前65535个以外的字符。因此,就有了另外一种Unicode编码方式,叫做UTF-16(因为16位 = 2字节)。UTF-16将0–65535范围内的字符编码成2个字节。JavaScript 字符串把每个 UTF16 单元当作一个字符来处理,所以处理超出范围的字符时,你应该格外小心。
Number
- 数字大致对应数学中的无理数。JS为了表示几个额外的情况(比如除数是0)引入了几个特殊的概念:NaN:占用了 9007199254740990,这原本是符合 IEEE 规则的数字、Infinity,无穷大、-Infinity,负无穷大。
note:
- JavaScript 中有 +0 和 -0,区分 +0 和 -0 的方式,正是检测 1/x 是 Infinity 还是 -Infinity。
- 精度丢失:计算机在做运算的时候,会分三个步骤:第一步:转换成二进制、第二步:用二进制科学计算法表示、第三步:表示成 IEEE 754 形式,但第一步和第三步都有可能丢失精度。其中,将十进制转为二进制,再将二进制转为十进制的时候,都会产生精度丢失。要是浮点数的运算精度不丢失,可以使用库,是最简单粗暴的解决方案。但如果使用不频繁,还是要根据需求,手动解决。在使用内置方法 toFixed() 的时候,要特别注意它的返回类型,不要直接拿来做运算。
- 检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法。如console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON) 即可正确表示0.1+0.2是否等于0.3,Number.EPSILON 属性表示 1 与Number中可表示的大于 1 的最小的浮点数之间的差值,即js中提供的最小精度值。
Symbol
Symbol 是 ES6 中引入的新类型,它是一切非字符串的对象 key 的集合。Symbol 可以具有字符串类型的描述,但是即使描述相同,Symbol 也不相等。
Object
类型转换
- 装箱转换:在JavaScript里面有个引用类型叫做基本包装类型,它包括String、Number和Boolean。那么它和基本的类型String、Number和Boolean是啥关系呢?
- 装箱操作:所谓的装箱,是指将基本数据类型转换为对应的引用类型的操作。而装箱又分为隐式装箱和显式装箱。
- 隐式装箱:对于隐式装箱,我们看下下面的代码:
var s1 = 'call_me_R'; // 隐式装箱
var s2 = s1.substring(2);
上面代码的执行步骤其实是这样的:
创建String类型的一个实例;
在实例中调用制定的方法;
销毁这个实例。
上面的三个步骤转换为代码,如下:
# 1
var s1 = new String('call_me_R');
# 2
var s2 = s1.substring(2);
# 3
s1 = null;
隐式装箱当读取一个基本数据类型的值时,后台会创建一个该基本数据类型所对应的基本包装类型对象。在这个基本数据类型上调用方法,其实就是在这个基本包装类型对象上调用方法。这个基本包装类型对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后立即被销毁。这也是在基本数据类型上添加属性和方法后再获取时会不识别或报错的原因了,如下:
var s1 = 'call_me_R';
s1.job = 'frontend engineer';
s1.sayHello = function(){
console.log('hello kitty');
}
console.log(s1.job); // undefined
s1.sayHello(); // Uncaught TypeError: s1.sayHello is not a function
- 显式装箱
装箱的另一种方式是显示装箱,这个就比较好理解了,这是通过基本包装类型对象对基本类型进行显示装箱,如下:
var name = new String('call_me_R');
显示装箱的操纵可以对new出来的对象进行属性和方法的添加啦,因为通过通过new操作符创建的引用类型的实例,在执行流离开当前作用域之前一直保留在内存中。
var objStr = new String('call_me_R');
objStr.job = 'frontend engineer';
objStr.sayHi = function(){
console.log('hello kitty');
}
console.log(objStr.job); // frontend engineer
objStr.sayHi(); // hello kitty
- 拆箱操作
拆箱就和装箱相反了。拆箱是指把引用类型转换成基本的数据类型。通常通过引用类型的valueOf()和toString()方法来实现。
在下面的代码中,留意下valueOf()和toString()返回值的区别:
var objNum = new Number(64);
var objStr = new String('64');
console.log(typeof objNum); // object
console.log(typeof objStr); // object
# 拆箱
console.log(typeof objNum.valueOf()); // number 基本的数字类型,想要的
console.log(typeof objNum.toString()); // string 基本的字符类型,不想要的
console.log(typeof objStr.valueOf()); // string 基本的数据类型,不想要的
console.log(typeof objStr.toString()); // string 基本的数据类型,想要的
所以,在进行拆箱操作的过程中,还得结合下实际的情况进行拆箱
note:
- 装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免对基本
类型做装箱转换。 - 使用内置的 Object 函数,也可以在 JavaScript 代码中显式调用装箱能力:
var symbolObject = Object(Symbol("a")); 2
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true