目录
一、JS的数据类型有8种
1. Number
2. String
3. Boolean
4. Object
5. null
6. undefined
7. Symbol
8. bigInt
二、强制类型转换
1. 其它类型转换为String
2. 其它类型转换为Number
3. 其它类型转换为Boolean:Boolean()
三、如何判断js数据类型(三种方法)
1. 方法一:typeof
2. 方法二:instanceof(instanceof 只能用来判断两个对象是否属于原型链的关系)
3. 方法三:constructor属性
4. 方法四:Object.prototype.toString.call()
5. 方法五:全等===
四、暂时性死区
1. 定义
2. ES6 暂时性死区的目的
Number、String、Boolean、Object、Null、undefined、Symbol、bigInt
在ES5中,有6种数据类型:Number、String、Boolean、Object、Null、undefined;
在ES6中,又增加了一种数据类型:Symbol;
在谷歌67版本中还出现了一种 :bigInt。指安全存储、操作大整数(目的是比Number
数据类型支持的范围更大的整数值)。
其中,
基本类型:Number、String、Boolean、Null、undefined、Symbol
引用类型:Object (包含function、Array、Date、Math)
使用最多的一个类型,有两个字面值,分别是true(非0)、false(0)
是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型名称来创建
null 是js的关键字,常用来描述空值,是一个对象
null是JavaScript语言的关键字,它表示一个特殊值,常用来描述“空值”。
undefined不是关键字,是预定义的全局变量,它的值就是“未定义”,表示值的空缺(表明变量没有初始化)。
在查询对象的属性或数组元素的值时返回undefined,则说明这个属性或元素不存在;
Symbol 类型的对象永远不相等,即便创建的时候传入相同的值。因此,可以用解决属性名冲突的问题(适用于多少编码),做为标记。
Symbol 数据类型在 ES6 中起什么作用? - 简书
ES6 入门教程(介绍了symbol类型)
Symbol
函数的返回值是不相等的。Symbol
函数前不能使用new
命令,这是因为生成的 Symbol 是一个原始类型的值,不是对象。Symbol
函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。for...in
、for...of,
Object.keys()都不可遍历到;但
使用Object.getOwnPropertySymbols()
方法可访问)。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。Symbol.for()
与Symbol()
都会生成新的 Symbol。它们的区别是,Symbol.for()
会被登记在全局环境中供搜索,后者不会。Symbol.for()
不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key
是否已经存在,如果不存在才会新建一个值。for...of
循环时,会调用Symbol.iterator
方法。对象的Symbol.iterator
属性,指向该对象的默认遍历器方法。s
不放在方括号中,该属性的键名就是字符串s
,而不是s
所代表的那个 Symbol 值。特性:唯一性
(1)创建Symbol类型的变量(其中()内的参数用来描述Symbol)
注意:<1> 使用 Symbol() 函数创建,不会在你的整个代码库中创建一个可用的全局 symbol类型
<2>使用Symbol.for() 方法,可创建全局symbol类型
(2)使用[],进行赋值和取值
(3)好处(作用):
<1> Symbol类型的属性名不能通过Object.属性名和for...in来访问(避免一些)。但是对象具有Symbol.iterator
属性,就是可遍历的
<2>使用Symbol来代替常量
<3> 在对象内,可以模拟私有属性,无法被轻易覆盖。
BigInt
数据类型是为了让JavaScript程序能表示超出Number
类型支持的数值范围。
在对大整数进行数学运算时,以任意精度表示整数的能力尤为重要。有了BigInt
,整数溢出将不再是一个问题,是 chrome 67中的新功能。
(1)创建:BigInt() 或 通过在一个整数字面量后面加 n 的方式定义一个 BigInt
typeof undefined // undefined
typeof null // object
null === undefined // false
null == undefined // true
主要是指将其它数据类型转换为String、Number、Boolean
(1)方法一:toString()方法 (前面需要加.)
注意:<1> 该方法不会影响到原变量,会将转换的结果返回
<2> null 和 undefined这两个值没有toString()方法
toString的具体用法:Javascript toString 方法原理_josavion的博客-CSDN博客
(2)方法二:String()函数
(1)方法一:Number()函数,不会改变本身
<1> 字符串转换为数字:
<2> 布尔转换为数字:
<3> Null 转换为数字: 0
<4> undefined转换为数字: NaN
(2)方法二:parseInt(将字符串转化为整数) 和 parseFloat(将字符串转化为浮点数)
如果对非Strings使用上述方法进行转换,它会先将其转换为String类型,再转为数字
parseInt方法接收两个参数,parseInt(要被解析的值string,被解析的值的进制);
2
到 36
,表示被解析的值的进制。例如说指定 10
就等于指定十进位。1.基本用法,只接受一个参数,可以当做第二个参数默认是10。parseInt的返回值只有两种可能,不是一个十进制整数,就是NaN。
a.将字符串转为整数。
parseInt('123'); // 123
b.如果字符串头部有空格,空格会被自动去除。
parseInt(' 81'); // 81
c.如果parseInt的参数不是字符串,则会先转为字符串再转换。这个很重要
d.字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分。
parseInt('99aa'); // 99
e.如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN。
parseInt('aa99'); // NaN
parseInt('-99'); // -99
f.如果字符串以0x或0X开头,parseInt会将其按照十六进制数解析。
parseInt('0x10'); // 16
g.如果字符串以0开头,将其按照10进制解析。
parseInt('011') // 11
h.如果参数以0开头,但不是字符串,则会先将数值转成字符串,然后解析,见规则c;
parseInt(011); // 9
// 解释一下,(011).toString()得到的结果是‘9’,具体原因请查看我的博文中有介绍toString方法
i.对于那些会自动转为科学计数法的数字,parseInt会将科学计数法的表示方法视为字符串,因此导致一些奇怪的结果。
parseInt(1000000000000000000000.5); // 1
// 等同于
parseInt('1e+21'); // 1
parseInt(0.0000008); // 8
// 等同于
parseInt('8e-7'); // 8
2.进制转换(接收两个参数),parseInt方法还可以接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数。默认情况下,parseInt的第二个参数为10,即默认是十进制转十进制。
a.第一个参数解析规则参照第一条基本用法
b.如果第二个参数不是数值,会被自动转为一个整数。这个整数只有在2到36之间,才能得到有意义的结果,超出这个范围,则返回NaN。如果第二个参数是0、undefined和null,则直接忽略。
parseInt(“19”, 10); // 19 (10+9)
parseInt(“11”, 2); // 3 (2+1)
parseInt(“17”, 8); // 15 (8+7)
parseInt(“1f”, 16); // 31 (16+15)
parseInt('-99', null); // -99
parseInt('-99', undefined); // -99
parseInt('-99', 0); // -99
(1)数字转换为布尔:除了0和NaN,其余都是true
(2)字符串转换为布尔:除了空串,其余都是true
(3)对象也会转换为true
补充:只有下面五种情况,转换为Boolean类型,值为false:
1)NaN
2)0
3)‘’
4)undefined
5)null
typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function等6种数据类型。
缺点:
typeof 可以对JS基础数据类型做出准确的判断(除了null),而对于引用类型返回的基本上都是object。其实返回object也没有错,因为所有对象的原型链最终都指向了Object,Object是所有对象的`祖宗`。
但当我们需要知道某个对象的具体类型时,typeof 就显得有些力不从心了。
typeof '' ; // string 有效
typeof 1; // number 有效
typeof true ; //boolean 有效
typeof undefined; //undefined 有效
typeof null ; //object 无效
typeof [] ; //object 无效
typeof new Function(); // function 有效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
为什么 typeof null 时会返回object:
js中不同对象在底层都表示为二进制,而javascript 中会把二进制前三位都为0的判断为object类型,而null的二进制表示全都是0,自然前三位也是0,所以执行typeof时会返回'object。
补充:js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息
用 instanceof 来判断null的话会报错:
null instanceof null // TypeError: Right-hand side of 'instanceof' is not an object
instanceof 是用来判断 A 是否为 B 的实例对,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。
在这里需要特别注意的是:instanceof检测的是原型,我们用一段伪代码来模拟其内部执行过程:
instanceof (A,B) = {
var L = A.__proto__;
var R = B.prototype;
if (L === R) {
//A的内部属性__proto__指向B的原型对象
return true ;
}
return false ;
}
从上述过程可以看出,当 A 的 __proto__ 指向 B 的 prototype 时,就认为A就是B的实例,我们再来看几个例子:
[] instanceof Array; //true
{} instanceof Object; //true
new Date() instanceof Date; //true
function Person(){};
new Person() instanceof Person; //true
// 只能判断 两个对象是否属于原型链的关系
[] instanceof Object; //true
new Date() instanceof Object; //true
new Person instanceof Object; //true
我们发现,虽然 instanceof 能够判断出 [] 是Array的实例,但它认为 [] 也是Object的实例,为什么呢? 我们来分析一下[]、Array、Object 三者之间的关系:
从instanceof 能够判断出 [].__proto__ 指向 Array.prototype, 而 Array.prototype.__proto__ 又指向了Object.prototype,Object.prototype.__proto__ 指向了null,标志着原型链的结束。因此,[]、Array、Object就形成了如下图所示的一条原型链:
从原型链可以看出,[] 的 __proto__ 直接指向Array.prototype, 间接指向Object.prototype, 所以按照 instanceof 的判断规则,[] 就是Object的实例。
当然,类似的new Date()、new Person() 也会形成这样一条原型链,
因此缺点:instanceof 只能用来判断两个对象是否属于原型链的关系, 而不能获取对象的具体类型。
instanceof 的实现原理:
只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
<1> 构造函数F的显式原型prototype含有constructor属性(指向构造函数F的引用),
<2> 创建构造函数F的实例对象f,对象f的隐式原型__proto__ 指向 构造函数F的显式原型prototype,故对象f中含有constructor属性(指向构造函数F)。
<3> 所以我们可以通过判断该对象的constructor属性 是否等于 构造函数F,来确定该对象的数据类型。
当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。如下所示:
当执行 var f = new F() 时,创建构造函数F的实例对象f,此时F原型上的constructor传递到了f上,因此f.constructor == F
可以看出,JS在函数F的原型上定义了constructor,当F被当作构造函数用来创建对象时,创建的新对象就被标记为了“F” 类型,使得新对象有名有姓,可以追溯。
同理,JS中的数据类型也遵守这个规则:
细节问题:
为什么变成了Object?
prototype被重新赋值的是一个{}, {}是new Object()的字面量,因此new Object()会将Object原型上的constructor传递给{},也就是Object本身。
因此,为了规范,在重写对象原型时一般都需要重新给constructor赋值,以保证实例对象的类型不被改写。
一般数据类型都能够判断,最准确最常用的一种
需要通过call方法,绑定toString的this指向。toString方法运行时,会返回this指向的具体类型。
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。
Object.prototype.toString.call( '' ) ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call( true ) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call( null ) ; // [object Null]
Object.prototype.toString.call( new Function()) ; // [object Function]
Object.prototype.toString.call( new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call( new RegExp()) ; // [object RegExp]
Object.prototype.toString.call( new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全局对象global的引用
需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object, 按照JS变量查找规则,其他对象应该也可以直接访问到Object的toString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此要用call来强制执行Object的toString方法。
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。