- 参考1: JAVASCRIPT高级程序设计 第三版 p25
- 参考2: 知乎 为什么JavaScript里面typeof(null)的值是”object”?
- 参考3: ECMAScript 6 入门 阮一峰 symbol
一、 什么是数据类型
数据:未经过处理的原始记录,在计算机系统中,数据以二进制信息单元0,1的形式表示。
数据类型: 是用来约束数据的解释规约。
二、基础数据类型、引用数据类型
1. typeof操作符
(1) typeof的所有返回值都是字符串
(2) 六种返回值类型
undefined
number
string
boolean
Object
Function
(3) 建议使用()
1 | (type of a); |
原因:原因操作符优先级,js中有15种优先级,为避免混乱建议使用()包裹。
(4) typrof null
- 返回值为”Obejct”
原因:
(1)从逻辑角度来看,null值表示一个空对象指针,而这正是使用typeof操作符检测null值时会返回“object”的原因。摘自《JAVASCRIPT高级程序设计 第三版 p25》
(2)null不是一个空引用, 而是一个原始值, 参考ECMAScript5.1中文版 4.3.11节; 它只是期望此处将引用一个对象, 注意是”期望”, 参考 null - JavaScript.
(3)typeof null结果是object, 这是个历史遗留bug, 参考 typeof - JavaScript
(4)在ECMA6中, 曾经有提案为历史平凡, 将type null的值纠正为null, 但最后提案被拒了. 理由是历史遗留代码太多, 不想得罪人, 不如继续将错就错当和事老, 参考 harmony:typeof_null [ES Wiki]
以上内容作者:克荷林
链接:https://www.zhihu.com/question/21691758/answer/98782260
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2. undefined
- undefined类型只有一个值,即特殊的undefined
- 在使用var关键字声明变量,但未对其初始化时,这个变量就是undefined
3. null
- null类型只有一个值,这个特殊值是null,从逻辑角度来看,null表示一个空对象指针,而这也是typeof操作符检测null时会返回“obeject”的原因
- 在使用var关键字声明变量,但未对其初始化时,这个变量就是undefined
4. null 和 undefined
(1)相同点
(1)参与判断 返回false
1 | // if判断为false的情况 |
(2)没有方法
(3)都是falsy,不加!比较都是false
(2)不同点
(1) 语义上不同,null是有值,但值为空,undefined是值未定义
(2) typeof
null typeof 是obejct undefined typeof undefined
(3) 数字转化
undefined =>NAN null => 0
(4) null可以json化
*(5)通常情况下,undefined代表未初始化,null已经初始化
*(6)在浏览器环境中,undefined是windows的一个属性,只不过属性对应的值是undefined
*(7) null不可以作为变量名 undefined可以作为变量名
原因:null不能做变量名的原因应该是因为null是字面量
(3)为什么不同
JavaScript的最初版本是这样区分的:null是一个表示”无”的对象,转为数值时为0;undefined是一个表示”无”的原始值,转为数值时为NaN。
原因有如下两个:
null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示”无”的值最好不是对象。
null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示”无”的值最好不是对象。
因此,Brendan Eich又设计了一个undefined。
如果不深究,简单理解可以归纳为历史原因
(4)使用场景
[1] null
null表示”没有对象”,即该处不应该有值
典型用法:
- null表示”没有对象”,即该处不应该有值
1 | var test = null; |
- 作为对象原型链的终点
1 | Object.getPrototypeOf(Object.prototype) |
[2] undefined
undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义
变量被声明了,但没有赋值时,就等于undefined
调用函数时,应该提供的参数没有提供,该参数等于undefined
对象没有赋值的属性,该属性的值为undefined
函数没有返回值时,默认返回undefined
1 | var i; |
有些框架会定义
1 | var undefined |
建立undefined的引用是为了提高性能。
[3] == && ===
- undeined是派生自null的值,因此ECMA-262规定他们的相等性测试要返回true
- 原因:由于 == 会进行转换操作
1 | console.log(null == undeinded) // true |
5.boolean
名称源自数学家乔治布尔。
(1) 特点
[1] 该类型只有2个字面值,true和false
这两个值与数字不是一回事,true不一定等于1, false也不一定等于0
[2] 所有数据类型都可以转换为 boolean , js提供了Boolean方法转换
js中只有6个数据会被转化为fasle(fasly)
null
undefined
0
-0
NaN
空字符串
(2) 转换boolean
Boolean()
!!转换为boolean
5.Number
(1) 标准
ECMAScript使用的是IEEE 754标准(浮点数算术标准)来定义Number类型,ECMAScript中所有的数字,无论是整数还是小数,其类型均为Number。
在程序内部,Number类型的实质是一个64位的浮点数,这与Java中double类型的浮点数是一致的。
(2)数字表示方式:
- 十进制
1 | var intNum = 12; |
- 八进制
建议不使用(严格模式、ES5下无效,会导致Javascript引擎抛出错误)
1 | var octalNum1 = 070 |
- 十六制
1 | var hexNum = 0xA |
(3)数值范围:
[1] JavaScript所能表示的数值范围为:
正负1.7976931348623157乘以10的308次方,其最小所能表示的小数为正负5乘以10的负324次方
[2] JavaScript能表示并进行精确算术运算的整数范围为:
正负2的53次方,也即从最小值-9007199254740992到最大值+9007199254740992之间的范围
对于超过这个范围的整数,JavaScript依旧可以进行运算,但却不保证运算结果的精度。值得注意的是,对于整数的位运算(比如移位等操作),JavaScript仅支持32位整型数,也即从-2147483648到+2147483647之间的整数。
获取边界值:
两个边界值可以分别通过访问Number对象的MAX_VALUE属性和MIN_VALUE属性来获取。
1 | Number.MAX_VALUE // 1.7976931348623157e+308 |
[2] 虽然小数点前面可以没有整数,但不推荐:
1 | var floatNum2 = .1; |
[3] 由于保存浮点数值需要的内存是保存整数的两倍,因此ECMAscript会不失时机地将浮点数值转换为整数
- 小数点后没有任何数字,那么数值就可以作为为整数值来保存
1 | var floatNum1 = 1.; // 解析为1 |
- 浮点数本身表示就是整数,那么该值会被转换为整数
1 | var floatNum2 = 10.0; // 解析为10 |
(5)科学计数法:
当数字极大或极小,可以用e表示法(即科学计数法)表示。
1
var floatNum = 3.125e7; // 等于31250000 3.125乘以10的7次方
ECMAscript会将那些小数点后面带有6个0以上的浮点数值转化为科学计数表示法。
1
var floatNum = 3e-7 // 等于0.0000003
浮点数值的最高精度是17位小数,但在进行算术计算时其精度远远不如整数。建议永远不要测试某个特定的浮点数值。
(6) Infinity
1 | 11 / 0 // Infinity |
(7) NaN
Not a Number
1 | 0 / 0 // NaN |
1 | Nan === NaN // false |
isNaN()
返回结果只有 true false
- 使用isNaN()判断传入值是不是NaN
- 但实际使用中会判断传入值是否能被转化为数字
1 | alert(isNaN(NaN)); // true alert(isNaN(10)); // false alert(isNaN("10")); // false alert(isNaN("blue")); // true alert(isNaN(true)); // false |
(7) 转换方法
目的:将数据转换成数字、整数、浮点数。
[1] Number()
作用:任何数据类型转Number
- 数字
- NaN
[2] parseFloat
作用:处理字符串
建议:传第二个参数10
[3] parseInt
作用:处理字符串
建议:传第二个参数10 (表示第一个参数是什么进制,返回值结果始终十进制)
(8) 精度损失 (0.1 + 0.2 != 0.3)
1 | var a = 0.1; |
[1] 原因
关于浮点数值计算误差的问题,是因为基于IEEE754标准。
[2] 解决方案
(1) 基数
1 | var a = 0.1; |
(2) toFixed
1 |
|
6.String
String类型用于表示由零或多个16位unicode字符组成的字符序列,即字符串。
(1) 字符串特性
ECMAScript中字符串是不可变的
1 | 'abcd' |
(2) 转义符
(3) unicode 编码
\uxxxx
(4) 字符串类型转换
toString
null undefined 不可用
String()
可以转换传进去所有类型
6.基础数据类型转换归纳
相同点:
(1) 都有String Boolean
(2) 首字母大写
(3) 都可以穿入任何类型
(4) 得到的结果都是和自己对应的
(5) 传入对象[Object,Object]
8. Object
定义:属性构成对象的无序的集合
对象的比较是引用的比较
(1) Object对象的分类
[1] 内部对象 (17个)
a. 内置对象 (3)
- Math
- Global
- JSON
b. 常用对象 (8)
Boolean
String
Number
Object
Array
Function
Date
RegExp
c. 错误对象 (6)
Error
EvalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError
[2] 宿主对象
- window (浏览器)
- global (node)
- document
[3] 自定义对象
(2)基础数据类型转对象
[1] boolean => Object
1 | Object(false); |
[2] null => Object
1 | Object(null); |
[3] undefined => Object
1 | Object(undefined); |
[4] string => Object
1 | Object('abc'); |
[5] number => Object
1 | Object(123); |
(3) 对象转基础数据类型
[1] Object => boolean
任何对象转换boolean类型,都是true
1 | Boolean(Object); // true |
[2] Object => number
先调用valueOf(),后调用toString()
调用valueOf(),返回原始值,若无原始值则返回对象本身
[1] date对象特殊,当前系统时间与1970年1月1日差额毫秒
1 |
[2] 对象
[3] Object => string
先调用toString(),后调用valueOf()
调用toString(),当前对象通过字符串表示
- 数组
1 |
|
- 函数
1 | function a() { |
- 日期
1 | var a = new Date(); |
- 正则
1 | var a = new RegExp(); |
(4). 创建Object
[1] 对象字面量
1 | var o1 = {test: 1}; |
[2] new Object()
1 | var o1 = new Object({test: 1111}); |
[3] Object.create()
- 语法
Object.create(proto, [propertiesObject])
- 参数
proto:
新创建对象的原型对象。
propertiesObject:
可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数
- 返回值
一个新对象,带着指定的原型对象和属性
1 | var a = Object.create(Object.prototype,{foo: {value: '111'}}); |
[4] new Object() 和 Object.create()区别
new操作符——完成四件事
创建一个新的空对象;
绑定this到这个空对象,即切换上下文;
给这个新对象添加proto,并指向构造函数的原型;
如果构造函数未返回对象(如返回对象,则替换return this),则将该新对象返回,即return this(第2条:this指向新对象);
1 |
|
Object.create
Object.create传入对象将作为新建对象的原型。
Object.create传入构造函数原型时,无论构造函数是否返回对象,Object.create只会return 以构造函数原型来新建的对象(参见下面的polyfill),而new则参见上节粗体描述。
[5] Object.assign vs new
Object.assign 只拷贝对象的属性和函数,丢失原型链,proto指向Object.prototype
1 | function Dog(name){ |
(5). 对象属性查找
[1] .
解释器解析步骤:
- 判断.之前是否为null 或 undefined
- 判断.前时候为对象,若不是转化为对象
- .后名字匹配
- 返回值或undefined
[2] []
解释器解析步骤
- 判断[]之前是否为null 或 undefined
- 判断[]前时候为对象,若不是转化为对象
- 计算[],转换为字符串,匹配值
- 返回值或undefined
7.symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。
[1] Symbol值通过Symbol函数生成的
Symbol 值通过Symbol函数生成,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。基本上,它是一种类似于字符串的数据类型。
- 创建
1 | let s = Symbol(); |
- 不能使用new操作符
1 | let s = new Symbol(); //报错 |
- 不能添加属性
1 | let s = Symbol(); // s.name 等 '123' 不可行 |
[2] typeof
typeof运算符的结果,表明变量s是 Symbol 数据类型
1 | typeof s // "symbol" |
[3] 参数
Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
1 | let s1 = Symbol('foo'); |
如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol值
1 | const obj = { |
[4] 比较
Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的
1 | // 没有参数的情况 |
[5] 运算
- Symbol 值不能与其他类型的值进行运算,会报错
1 | let sym = Symbol('My symbol'); |
- Symbol 值可以显式转为字符串
1 | let sym = Symbol('My symbol'); |
- Symbol 值也可以转为布尔值,但是不能转为数值
1 | let sym = Symbol(); |
[6] 作为属性名的 Symbol
- 定义
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
1 | let mySymbol = Symbol(); |
- Symbol 值作为对象属性名时,不能用点运算符
1 | const mySymbol = Symbol(); |
上面代码中,因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值。
同理,在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
1 | let s = Symbol(); |
上面代码中,如果s不放在方括号中,该属性的键名就是字符串s,而不是s所代表的那个 Symbol 值。
- 增强的对象写法
1 | let obj = { |
- 增强的对象写法
Symbol 类型还可以用于定义一组常量,保证这组常量的值都是不相等的。
1 | const log = {}; |
下面是另外一个例子。
1 | const COLOR_RED = Symbol(); |
常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。
还有一点需要注意,Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。
8.可变类型与不可类型
注:数据类型动态性 定义变量只是赋值,但过程中没有确定数据类型,在使用时在确定数据类型
(1) 基础数据类型是不可变:
[1] 值比较
- 基础数据类型,是值的比较
1 | var a = 1; |
[2] 动态的属性
- 不可以添加属性和方法,也不能改变和删除属性和方法
1 | var a = 'test'; |
[3] 复制变量值
从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制为新变量分配的位置上。
1 | var num1 = 1; |
[4] 传递参数
传递参数的方式为值传递
向参数传递基本类型,被传递的值会复制给一个局部变量,因此这个局部变量的变化会反应在函数外部。
1 | function addTen(num) { |
(2) 引用数据类型可变
[1] 值比较
- 引用比较,独立对象不相等
1 | var obj1 = {a: 1}; |
[2] 动态的属性
- 可以添加属性和方法,也不能改变和删除属性和方法
1 | var a = {}; |
[3] 复制变量值
从一个变量向另一个变量复制引用类型的值,同样会将存储在变量对象中的值复制一份放到新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,复制结束后,两个变量实际上将引用同一个对象。
因此,改变其中一个,会影响另一个。
1 | var obj1 = {a: 1}; |
[4] 传递参数
传递参数的方式为值传递
向参数传递引用类型,被传递的对象的引用会复制给一个局部变量,因此外部和函数内部的变量指向同一引用,因此内部会影响外部:
1 | function setName(obj) { |