基本类型
JavaScript 中基本数据类型
本部分主要是针对于 JavaScript 中常用的数据结构类型进行分析说明。JavaScript 中内置了Object
, Array
, Date
, String
, Number
, or Boolean
这几个数据类型。
- Six data types that are primitives:BooleanNullUndefinedNumberStringSymbol (new in ECMAScript 6)
- and Object
typeof Object === "function"
typeof {} ==== "object"
typeof Boolean === "function"
const boolean = new Boolean()
typeof boolean === "object"
typeof undefined ==== 'undefined'
typeof null ==== 'object'
JavaScript 中数据类型可以分为两类:
- 原始数据类型(Primitive Type ):Undefined、Null、Boolean、Number、String
- 引用类型(Reference Type ):Object、Array、Function、Date
我们在声明变量时会有不同的内存分配:
- 原始值:存储在栈(stack )中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。
- 引用值:存储在堆(heap )中的对象,也就是说,存储在变量处的值是一个指针(point ),指向存储对象的内存地址。这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。
不同的内存分配机制也带来了不同的访问机制在 javascript 中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的按引用访问。而原始类型的值则是可以直接访问到的。复制变量时的不同原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的 value 而已。引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。(这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)参数传递的不同首先我们应该明确一点:ECMAScript 中所有函数的参数都是按值来传递的。但是为什么涉及到原始类型与引用类型的值时仍然有区别呢,还不就是因为内存分配时的差别。(我对比了一下,这里和复制变量时遵循的机制完全一样的嘛,你可以简单地理解为传递参数的时候,就是把实参复制给形参的过程)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象呀。
数值类型
科学计算
取整
JavaScript 中 Math 对象提供了三种取整的方法:
- round,四舍五入取与最接近的整数
- floor, 对一个数进行下舍入,执行向下取整计算,返回小于或等于函数调用参数的并且与之最接近的值。
- ceil, 对一个数进行上舍入,执行的是向上取整计算,返回大于或者等于函数参数,并且与之最接近的整数。
幂
pow(x, y) 返回 x 的 y 次幂的值。x,y 都是必须的且必须是数字。如果由于指数过大而引起浮点溢出,则该方法将返回 Infinity。
console.log(Math.pow(2, 10)); //1024
console.log(Math.pow(99, 9999)); //Infinity
console.log(Math.pow(1024, 0.1)); //2
console.log(Math.pow(0, 0)); //1
console.log(Math.pow(0, 1)); //0
console.log(Math.pow(2, -3)); //0.125
console.log(Math.pow(-2, 3)); //-8
随机数
random() 方法可返回介于 0 ~ 1 之间的一个随机数。
<script type="text/javascript">
document.write(Math.random());
</script>
类型转换
JavaScript 中将字符串或者其他类型转化为数字,主要利用 parseInt、parseFloat、Number 与 JavaScript 变量弱类型转换这几种。
parseInt & parseFloat
JavaScript 提供了 parseInt() 和 parseFloat() 两个转换函数。前者把值转换成整数,后者把值转换成浮点数。只有对 String 类型调用这些方法,这两个函数才能正确运行;对其他类型返回的都是 NaN(Not a Number)。parseInt() 方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。基是由 parseInt() 方法的第二个参数指定的。parseFloat() 方法与 parseInt() 方法的处理方式相似。使用 parseFloat() 方法的另一不同之处在于,字符串必须以十进制形式表示浮点数,parseFloat() 没有基模式。
console.log(parseInt("1245red")); //1245
console.log(parseFloat("1245red")); //1245
console.log(parseInt("1245.5red")); //1245
console.log(parseFloat("1245.555red")); //1245.555
console.log(parseInt("red")); //NaN
console.log(parseInt("0xA")); //10
console.log(parseInt("AF", 16)); //175 10*16+15
console.log(parseInt("10", 2)); //2
console.log(parseInt("10", 8)); //8
console.log(parseInt("10", 10)); //10
//如果十进制数包含前导0,那么最好采用基数10,这样才不会意外地得到八进制的值
console.log(parseInt("010"));
console.log(parseInt("010", 8)); //8
console.log(parseInt("010", 10)); //10
console.log(parseFloat("1234red")); //1234
console.log(parseFloat("0xA")); //0
console.log(parseFloat("22.5")); //22.5
console.log(parseFloat("22.35.5")); //22.35
console.log(parseFloat("0908")); //908
console.log(parseFloat("blue")); //NaN
Number
使用 Number 函数,可以将任意类型的值转化成数字。
- 数值:转换后还是原来的值。
- 字符串:如果可以被解析为数值,则转换为相应的数值,否则得到 NaN。空字符串转为 0。
- 布尔值:true 转成 1,false 转成 0。
- undefined:转成 NaN。
- null:转成 0。
Number 函数将字符串转为数值,要比 parseInt 函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为 NaN。
parseInt("011"); // 9
parseInt("42 cats"); // 42
parseInt("0xcafebabe"); // 3405691582
Number("011"); // 11
Number("42 cats"); // NaN
Number("0xcafebabe"); // 3405691582
如果 Number 传入的参数是一个对象,那么转换规则会相对复杂一点,具体而言描述如下:
- 先调用对象自身的 valueOf 方法,如果该方法返回原始类型的值(数值、字符串和布尔值),则直接对该值使用 Number 方法,不再进行后续步骤。
- 如果 valueOf 方法返回复合类型的值,再调用对象自身的 toString 方法,如果 toString 方法返回原始类型的值,则对该值使用 Number 方法,不再进行后续步骤。
- 如果 toString 方法返回的是复合类型的值,则报错。
//Number()的强制类型转换与parseInt()和parseFloat()方法的处理方式相似,只是它转换的是整个值,而不是部分值。
console.log(Number(false)); //0
console.log(Number(true)); //1
console.log(Number(undefined)); //NaN
console.log(Number(null)); //0
console.log(Number("5.5")); //5.5
console.log(Number("5.6.7")); //NaN
console.log(Number("5red")); //NaN
console.log(Number(new Object())); //NaN
console.log(Number(100)); //100
console.log(Number(a)); //NaN
JavaScript 弱类型转换
const str="012.345";
const x = str-1;
console.log(x); //11.345
const str2 = "012.3456red";
const x = str2-2;
console.log(x); //NaN
格式化显示
这里用的是numeraljs,格式化显示的效果如下所示:
- Number
Number | Format | String |
---|---|---|
10000 | ‘0,0.0000’ | 10,000.0000 |
10000.23 | ‘0,0’ | 10,000 |
10000.23 | ‘+0,0’ | +10,000 |
- Currency
Number | Format | String |
---|---|---|
1000.234 | ‘$0,0.00’ | $1,000.23 |
1000.2 | ‘0,0[.]00 $’ | 1,000.20 $ |
- Bytes
Number | Format | String |
---|---|---|
100 | ‘0b’ | 100B |
2048 | ‘0 b’ | 2 KB |
- Percentages
Number | Format | String |
---|---|---|
1 | ‘0%’ | 100% |
0.974878234 | ‘0.000%’ | 97.488% |
- Time
Number | Format | String |
---|---|---|
25 | ‘00:00:00’ | 0:00:25 |
238 | ‘00:00:00’ | 0:03:58 |
布尔类型
布尔值代表 “ 真 ” 和 “ 假 ” 两个状态。“ 真 ” 用关键字 true 表示,“ 假 ” 用关键字 false 表示。布尔值只有这两个值。如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为 false,其他值都视为 true。
- undefined
- null
- false
- 0
- NaN
- "" (空字符串)
类型转换
所有对象的布尔值都是 true,甚至连 false 对应的布尔对象也是 true。
Boolean(new Boolean(false))
// true
空类型
JavaScript 中常见的空类型为 undefined 与 null,不过typeof undefined === ‘undefined’
而 typeof null === ‘object’
。
null 表示 " 没有对象 “,即该处不应该有值。典型用法是:
- 作为函数的参数,表示该函数的参数是对象。
- 作为对象原型链的终点。
undefined 表示 " 缺少值 “,就是此处应该有一个值,但是还未定义。典型用法是:
- 变量被声明了,但没有赋值时,就等于 undefined。
- 调用函数时,应该提供的参数没有提供,该参数等于 undefined。
- 对象没有赋值的属性,该属性的值为 undefined。
- 函数没有返回值时,默认返回 undefined。
Symbols
Symbols 是 JavaScript 的第七种原始类型,它代指一个全局唯一的不可变对象。如果需要创建一个 Symbol 对象,则需要调用 Symbol 函数:
const sym1 = Symbol();
const sym2 = Symbol("foo");
const sym3 = Symbol("foo");
如上的代码会创建三个新的符号,注意,虽然 Symbol 使用了 ”foo” 这个字符串作为输入对象,但是每次会创建一个新的符号:
Symbol("foo") === Symbol("foo"); // false
确切地说,symbol 与其它类型并不完全相像。symbol 被创建后就不可变更,你不能为它设置属性(在严格模式下尝试设置属性会得到 TypeError 的错误)。他们可以用作属性名称,这些性质与字符串类似。
另一方面,每一个 symbol 都独一无二,不与其它 symbol 等同,即使二者有相同的描述也不相等;你可以轻松地创建一个新的 symbol。这些性质与对象类似。
ES6 中的 symbol 与 Lisp 和 Ruby 这些语言中更传统的 symbol类似,但不像它们集成得那么紧密。在 Lisp 中,所有的标识符都是 symbol;在 JS 中,标识符和大多数的属性键仍然是字符串,symbol 只是一个额外的选项。
关于 symbol 的忠告:symbol 不能被自动转换为字符串,这和语言中的其它类型不同。尝试拼接 symbol 与字符串将得到 TypeError 错误。
> const sym = Symbol("<3");
> "your symbol is " + sym
// TypeError: can't convert symbol to string
> `your symbol is ${sym}`
// TypeError: can't convert symbol to string
有三种获取 symbol 的方法。
- **调用 Symbol()。**正如我们上文中所讨论的,这种方式每次调用都会返回一个新的唯一 symbol。
- **调用 Symbol.for(string)。**这种方式会访问 symbol 注册表,其中存储了已经存在的一系列 symbol。这种方式与通过
Symbol()
定义的独立 symbol 不同,symbol 注册表中的 symbol 是共享的。如果你连续三十次调用Symbol.for("cat")
,每次都会返回相同的 symbol。注册表非常有用,在多个 web 页面或同一个 web 页面的多个模块中经常需要共享一个 symbol。 - **使用标准定义的 symbol,例如:Symbol.iterator。**标准根据一些特殊用途定义了少许的几个 symbol。
Symbol
const obj = Object.create(null);
obj[Symbol.toPrimitive] = function (hint) {
console.log(hint);
return "";
};
console.log(String(obj));
console.log(obj + "");
console.log(+obj);
const smb =Symbol('hello');
console.log(smb);
console.log(''+smb);
console.log(1+smb);
console.log(String(smb));
// Uncaught TypeError: Cannot convert a Symbol value to a string