概述
js基本类型: Undefined、Null、Boolean、Number、String、Symbol
js引用类型: Object (Object类型,Array类型,Date类型,Function类型,RegExp类型)
存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var num1 = 5; var num2 = num1; |______ |___ | | | | |______ |___ | | num1 | 5 | |______ |___ | |______ |___ | | num2 | 5 | |______ |___ | | num1 | 5 | |______ |___ |
引用类型变量只保存对对象,数组,和函数等引用类型的值得引用(即指针或者说内存地址),而引用类型的值保存在堆
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var obj1 = new Object var obj2 = obj1 obj1.name = 'Jack' console.log(obj2.name) // 'Jack' 栈 堆 _______________ |______ |_______ | | |--------— | | | | | _____\ | | Obj1 | | |______ |_______ | / / | |name:jack | | | obj1 | x0c01 |/ | --------- | |______ |_______ | |_______________ | ________________ |______ |_______ | | |--------— | | | obj2 | x03c1 |--\ _____\ | | Obj1 | | |______ |_______ | / / | |name:jack | | | obj1 | x0c01 |/ | --------- | |______ |_______ | |_______________ |
JavaScript 中的数字类型是基于“二进制浮点数”实现的, 64 bit , 8 字节 使用的是“双精度”格式。
浮点数值最高精度是17位小数,但在进行计算时其精确度远远不如整数。会有舍入误差
1 2 3 4 5 6 7 8 9 10 11 var a = 0.1 + 0.2 ; var b = 0.3 ;console .log(a === b); console .log(Number .isInteger(a*10 )); console .log(Number .isInteger(b*10 )); console .log(a); var f = 1.1 f--
数据类型的判别
NaN
属于Number
类型,并且 NaN
不等于任何值,包括它自己
typeof: (判断不了引用类型)除了null
都可以正确判断类型
1 2 3 4 5 typeof null typeof [1 , 2 , 3 ] typeof b typeof NaN
1 2 3 4 5 6 (function ( ) {}) instanceof Function [1 , 2 , 3 , 4 ] instanceof Array new Number (1 ) instanceof Number new Boolean (true ) instanceof Boolean new String ('123' ) instanceof String
当然,
因此,instanceof 只能用来判断对象类型,原始类型不可以,所有的对象类型instaceof Object 都是true
1 2 3 4 5 6 7 Object.prototype .toString .call (123 ) Object.prototype .toString .call ('string' ) Object.prototype .toString .call (true) Object.prototype .toString .call (Symbol(12 )) Object.prototype .toString .call ({}) Object.prototype .toString .call ([1 ,2 ,3 ]) Object.prototype .toString .call (function(){})
通过 Object.getPrototypeOf(a) === Array.prototype
判断引用类型
Array.prototype.isPrototypeOf(a)
判断引用类型
1 2 3 4 var a = [] ; Array . prototype.isPrototypeOf(a ) ; Object . getPrototypeOf(a ) === Array . prototype;
数组还有一个方法,是es5新增的方法:
类型转换
JavaScript是动态类型,变量是没有类型的,可以随时赋予任意值
js类型转换有显式和隐式转换,都会遵循一定的原理。
隐式转换
先来看下面的代码:😲
1 2 3 4 5 6 7 8 9 {}+[] [ ] + {} var a = {}+[]console.log(a) console.log(typeof a) [ ] == false {} == true
条件判断: 在条件判断时,除了 undefined
, null
, false
,NaN
, ''
, 0
, -0
,其他所有值都转为 true
,包括所有对象。
1 2 3 if (undefined) {console.log("false, 不执行" )}if ([] || {}) {console.log("true, 执行" )}
隐式转换原理判断
若想从源头出发,理解一些怪异的类型转换,就需要看 ECMA-262详细的规范,从而探究其内部原理,我们从这段内部原理示意代码开始:
原理代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 function ToPrimitive(x , hint ) { if (IS_STRING(x ) ) return x; if (!IS_SPEC_OBJECT(x ) ) return x; if (IS_SYMBOL_WRAPPER(x ) ) throw MakeTypeError(kSymbolToPrimitive ) ; if (hint == NO_HINT) hint = (IS_DATE(x ) ) ? STRING_HINT : NUMBER_HINT; return (hint == NUMBER_HINT) ? DefaultNumber(x ) : DefaultString(x ) ; } function DefaultNumber(x ) { if (!IS_SYMBOL_WRAPPER(x ) ) { var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf ) ) { var v = %_CallFunction(x , valueOf ) ; if (IsPrimitive(v ) ) return v; } var toString = x.toString; if (IS_SPEC_FUNCTION(toString ) ) { var s = %_CallFunction(x , toString ) ; if (IsPrimitive(s ) ) return s; } } throw MakeTypeError(kCannotConvertToPrimitive ) ; } function DefaultString(x ) { if (!IS_SYMBOL_WRAPPER(x ) ) { var toString = x.toString; if (IS_SPEC_FUNCTION(toString ) ) { var s = %_CallFunction(x , toString ) ; if (IsPrimitive(s ) ) return s; } var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf ) ) { var v = %_CallFunction(x , valueOf ) ; if (IsPrimitive(v ) ) return v; } } throw MakeTypeError(kCannotConvertToPrimitive ) ; }
上面代码的逻辑是这样的:
如果变量为字符串,直接返回.
如果!IS_SPEC_OBJECT(x),直接返回.
如果IS_SYMBOL_WRAPPER(x),则抛出异常
否则会根据传入的hint来调用DefaultNumber和DefaultString,比如如果为Date对象,会调用DefaultString
DefaultNumber:首先x.valueOf,如果为primitive,则返回valueOf后的值,否则继续调用x.toString,如果为primitive,则返回toString后的值,否则抛出异常
DefaultString:和DefaultNumber正好相反,先调用toString,如果不是primitive再调用valueOf.
比如:
1 2 3 1 + {} // "1[object Object]" 1 + [] // "1"
{}和1首先会调用ToPrimitive {}会走到DefaultNumber,首先会调用valueOf
,返回的是Object
{},不是primitive
类型,从而继续走到toString
,返回[object Object]
,是String
类型 最后加操作,结果为[object Object]1
再比如有人问你[] + 1输出啥时,你可能知道应该怎么去计算了,先对[]调用ToPrimitive,返回空字符串,最后结果为"1"。
四则运算符
+
有字符串,另一方也转换为字符串。如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
+ - * /
: 只要其中一方是数字,那么另一方就会被转为数字
比较运算符
x == y,其中x和y是值
若Type(x)与Type(y)相同,则
若Type(x)为Udefined,返回true
若Type(x)为Null, 返回true
若Type(x)为Number,则
若x
为NaN
, 返回false
若y
为NaN
, 返回false
若x
与y
为相等数值,返回true
若x
为+0
且y
为-0
,或x
为-0
且y
为+0
,返回true
若x为null且y为Undefined,返回true,反之同理,即null == undefined
若Type(x)为Number且Type(y)为String, 比较 x == ToNumber(y)的结果,反之同理
若 Type(x) 为Boolean,返回比较ToNumber(x) == y 的结果,反之同理
若Type(x) 为String或 Number且Type(y)为 Object,返回比较x == ToNumber(y)的结果,反之同理
返回 false
验证:
1 2 3 4 5 6 7 8 9 '' == false // true '0' == false // ture [] == false // true '' == 0 // true '0' == 0 // true 0 == [] // true '' == [] // true null == undefined // true {} == true // true
显示转换
类型转换表
value
string
number
boolean
object
undefined
“undefined”
NaN
false
throws TypeError
null
“null”
0
false
throws TypeError
true
“true”
1
true
new Boolean(false)
false
“false”
0
false
new Boolean(false)
“”
0
false
new String(’’)
0 (-0)
“0”
false
new Number(0)
NaN
‘NaN’
false
new Number(NaN)
Infinity
“Infinity”
true
new Number(Infinity)
‘’
0
true
[9]
‘9’
9
true
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Object(undefined ) Object(null ) var n = 123456.789 n.to Fixed(0) n.to Exponential(1) n.to Precision(5) n = 1000000000000000000000000000000 parseInt(n ) n.to String() [1 ,2 ,3 ] .to String() var a = new Boolean(false ) var b = new Number(0) var c = new String("" ) a && b && c a === false a == false var c = { valueOf() { return 123 }, to String() { return 456 } } Number(c )
在 TypeScript中的应用
TypeScript 是 JavaScript 的一个超集(如果一个集合S2中的每一个元素都在集合S1中,且集合S1中可能包含S2中没有的元素,则集合S1就是S2的一个超集),主要提供了类型系统和对 ES6 的支持
可以在编译阶段就发现大部分错误,比在运行时候出错好
熟悉javascript
的类型相关后,可以基于其学习 ts对其变量类型的定义规则
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let isDone: boolean = false ;let createdByNewBoolean: boolean = new Boolean (1 ); let notANumber: number = NaN ;let infinityNumber: number = Infinity ;let binaryLiteral: number = 0b1010 ; => var binaryLiteral = 10 ; let octalLiteral: number = 0o744 ; => var octalLiteral = 484 ; let u: undefined = undefined ;let n: null = null ;
1 2 3 4 5 6 7 8 9 10 11 12 let b: Boolean = new Boolean (1 );let e: Error = new Error ('Error occurred' );let d: Date = new Date ();let r: RegExp = /[a-z]/ ;let body: HTMLElement = document .body;let allDiv: NodeList = document .querySelectorAll('div' );document .addEventListener('click' , function (e: MouseEvent ) { });
不同点:
ts 新增了很多对类型的控制,它更像是一种代码类型规范约束手段或者说代码检查的工具。
比如 void
, 接口(Interfaces)
类型断言
联合类型
字符串字面量类型
,元组
,枚举(Enum)类型
类的public、private 和 protected的定义
等
PS: 初识typescript: 个人对 typescript
的思考: 首先代码类型检查是一个很好的习惯, 平时在开发中应当多注意类型的判断,否则很有可能在一些公共组件中会有因为类型错误而无法运行出错的情况。但是因为只是静态类型的检测,因此还是避免不了一些类型错误,比如运行时。因此,个人感觉单靠typescript是无法解决所有类型变量出问题而出现Bug的情况。另外,代码的易读性会有所降低,重构成本和维护成本也会比较大。但是代码质量,特别是在团队合作时更能有保证。