开辟了一个关于javascript的基础系列,更加深刻、细致的了解这门语言。今天分享的是js的数据类型。
javascript的数据类型能够分为两类:原始类型(基础数据类型)
和对象类型(引用数据类型)
原始类型包括:数字
、字符串
、布尔值
、以及特殊的undefined
和null
除了以上的数据类型,其余就都是对象类型了
具备表明性的对象类型有:对象(object)
、数组(array)
、函数(function)
javascript
本次咱们着重介绍原始数据类型java
两个小注意点:
1.js语言是弱类型语言(并非没有数据类型)
2.在js语言中所声明的变量是没有数据类型的,所以能够被赋予任何类型的值es6
和其余变成语言不一样,js不区分正整数值和浮点数值,js中全部数字都是用浮点数值表示的。数组
数字的算术运算符方法有+
,-
,*
,/
,%
(加,减,乘,除,余)。
除此以外,js还支持更复杂的算术运算,这些复杂运算经过做为Math对象的属性定义和常量来实现:安全
// 2的53次幂 Math.pow(2, 53) // 0.6的四舍五入值 Math.round(0.6) // 向上取整 Math.ceil(0.6) // 向下取整 Math.floor(0.6) // 取绝对值 Math.abs(-5) // 求出x,y,z的最大值 Math.max(x, y, z) // 求出x,y,z的最小值 Math.min(x, y, z) // 生成一下大于等于0小于1的随机数 Math.random() // 圆周率 Math.PI // e天然对数的底数 Math.E // 3的开平方根 Math.sqrt(3) // 3的开立方根 Math.pow(3, 1/3) // 三角函数 Math.sin(0) // 求10的天然对数 Math.log(10) // 以10为底数的100的对数 Math.log(100)/Math.LN10 // 以2为底数的512的对数 Math.log(512)/Math.LN2 // e的3次方幂 Math.exp(3)
js的数字表示范围是有限制的(可否表示的限制、可否知足精度到个位的限制以及可否做为数组索引的限制)
具体的状况以下图:
(图片来自网络,侵删)网络
所以javascript在进行数学运算时,会出现溢出和下溢两种状况,
溢出的状况为:
当运算结果超出了js语言所能表示的上线(即图中1.8e308
—正无穷
的区域),结果会返回Infinity
(表示无穷大)
一样的,当计算的负数的值超过了能表示的负数范围(即图中-1.8e308
—负无穷
的区域),结果会返回-Infinity
(表示负无穷大)dom
下溢的状况为:
当运算的结果无限接近于0,并比js能表示的最小值还小的状况(即图中0
—5e-324
的区域)。这样结果会返回0
。
一样的,当一个负数发生下溢(即图中0
—-5e-324
的区域),这时结果会返回一个-0
。函数
上文,咱们介绍数字中预约义的全局变量Infinity
,此外还有一个预约义的全局变量NaN
(表示非数字,not-a-number,当运算的结果并非一个数字值的时候,会返回NaN
)测试
在js中,NaN有特殊的一点,就是它和任何值都不相等(包括自身),所以想要判断一个值是否为NaN
,可使用x != x
判断编码
var x = 1 - 'a' x != x //true
除此以外,咱们还能够调用全局预约好的函数isNaN
// 当传入的参数只要不是一个数字,就返回true isNaN(5 - 'a') // true isNaN('1') // true isNaN('a') // true isNaN({a: 2}) // true isNaN(1) //false isNaN(Infinity) //false
另外,全局还有一个预约好的函数isFinite
// 当传入的参数只要不是NaN, Infinity, -Infinity就返回true isFinite(5 - 'a') // false isFinite('1') // false isFinite('a') // false isFinite({a: 2}) // false isFinite(1) // true isFinite(Infinity) //false
数学中实数有无限多个,而在javascript语言中能经过浮点数的形式只能表现其中的有限个,所以在js中使用实数的时候,咱们每每都是使用的一个近似值。
javscript所采用的浮点数表示发,是一种二进制表示法,所以咱们能够精确的表示1/2
、1/8
、1/1024
。可是在数学中,咱们经常使用的都是十进制分数1/10
。因此js中并不能精确的表示像0.1
这样简单的数字。
var x = 0.3-0.2 var y = 0.2-0.1 x == y // false
所以要避免在js中用浮点数进行计算(尽可能使用整数)
javascript中的字符串采用的是UTF-16编码的Unicode字符集
,字符串的长度是其含有16位值的个数,以下:
var a = 'z' var b = '?' // 注意,这个字不是“吉祥”的吉 a.length // => 1: a包含的一个16位值 \u007A b.length // => 2: b包含两个16位值 \uD842\uDFB7
在js语言中,字符串是由单引号或双引号括起来的字符序列,定义的由单引号定界的字符串中能够包含双引号,一样,定义的由双引号定界的字符串中也能够包含单引号。
字符串能够拆分为数行,每行必须以\
结束,若是但愿在字符串中再起一行可使用转义字符\n
所有的转义字符以下:
测试输出结果以下:
可是,在ES6中,新增了模板字符串,模板字符串是用反勾号`
将字符括起
在模板字符串中换行就简单不少:
` hello world ` // 等价于 'hello\nworld'
除此以外,模板字符串还支持元素注入
var str = 'world' `hello ${world}` // 等价于 'hello ' + str
除了字符串的length属性以外,字符串还有不少能够调用的方法
var str = 'Hello, World' str.charAt(0) // H, 返回第一个位置的字符 str.charAt(s.length - 1) // t, 返回最后一个位置的字符 str.substring(1,4) // ell, 返回位置2-4的字符 str.slice(1,4) // ell, 同上 str.slice(-3) // rld, 返回最后三个字符 str.indexOf('l') // 2, 返回首次出现l的位置 str.lastIndexOf('l') // 10,返回最后一次出现l的位置 str.split(", ") // ['Hello', 'World'], 分割为数组 str.replace('H', 'h') // 'hello, World', 将h替换为H str.toUpperCase() // 'HELLLO, WORLD', 将字符串全部字母变为大写 str.toLowerCase() // 'hello, world', 将字符串全部字母变为小写 // es6新增方法 let s = 'Hello world!'; s.startsWith('Hello') // true s.endsWith('!') // true s.includes('o') // true // includes():返回布尔值,表示是否找到了参数字符串。 // startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。 // endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。 'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello" 'na'.repeat(0) // "" // repeat方法返回一个新字符串,表示将原字符串重复n次
须要注意的是,对于字符串的任何方法都会返回一个新的字符串,而不会在原字符串上修改。
javascript中布尔值有两个true
和false
null和undefined都表示”值的空缺“,但事从背后更深远的角度考虑,他们的仍是有差异的。
对null进行typeof
检测,返回值是object
对undefined进行typeof
检测,返回值是undefined
undefined表示,对这个值还未定义,尚未进行初始化。好比,当咱们声明一个变量,可是却未赋值,此时会返回undefined,当咱们获取一个对象未定义的属性,此时会返回undefined,当咱们调用一个函数,却未传参,参数会返回undefined。
null表示,没有对象,此处没有值,此处不该该有值。好比,原型链的重点就是null。
后面会从栈
和堆
的角度进行另外一番解释。
咱们能够这么理解,undefined是系统级的、出乎意料的、相似错误的空缺。而null是程序级的、正常的、在乎料之中的值的空缺。在某些场景下,好比想赋值给一个变量,想表示变量为空,或做为参数传入一个函数,这是,最佳的选择是null。
了解包装对象以前,咱们首先思考这么一个问题。
var a = 'test' a.length //4
咱们知道上面代码中的a
是一个字符串,字符串不是一个对象,不能进行.
关键字的操做。可是,为何咱们能够获得a.length
呢?
由于只要存在包装对象的概念,在上述代码执行的过程当中,js会将字符串经过new String
的方式生成一个包装对象,这个对象继承了String
的方法,由于能够经过.
的方式访问到。一旦属性的引用结束,这个包装对象就会被销毁(其实在js语言内部的实现上不必定建立或销毁这个对象,可是整个过程在执行层面看起来是这样的,咱们也能够这么进行理解)
想要深刻理解原始类型和引用类型的变与不变,相等比较等问题的时候,咱们须要借助栈
和堆
的思想来理解,咱们能够这么思考:
(图片来自网络,侵删)
这张图阐述了原始类型和引用类型的关系:
原始类型保存在栈内存中,原始类型(包括字符串、数字、布尔型、undefined)是保存在栈内存中,是不能够修改的(咱们所看到的修改,其实都是删除后从新赋值),当复制一个原始类型的时候,其实就是在内存中复制这么值。其中,undefined表明的就是未被赋值的一个栈内存的区域。
引用类型保存在堆内存中,可是在栈内存中存了一个引用类型的地址,栈内存中的地址有一个指针指向堆内存的引用类型。这个引用类型是能够进行修改的,好比咱们能够向数组中push一个新值。若是咱们只是简单的复制一个引用类型(浅拷贝),那么其实复制的是这个在栈内存中的地址,复制后的值发生修改,那么以前被复制的值也一样会被修改,所以在复制引用类型的时候,最好要进行深拷贝。其中,null很特殊,表示的是在栈内存中,有一个指针指向堆内存中的引用类型,一旦这个指针掉了,就是null。
jacascript中的类型转换很是常见,也是javascript语言中很是重要的一点。
首先咱们来看一下类型转化表:
任意JavaScript的值均可以转换为布尔值,只有undefine、null、0、NaN、""会被转换为false,其余全部值都会被转换成true。
当字符串转化为数字数字时,那些数字表示的字符串能够转化为数字,也容许在开始和结尾处有空格,可是其余含有非空非数字字符都不会完成到数字的转化,他们会转化为NaN。
原始值到对象的转换也很是简单,原始值经过调用构造函数,转化为包装对象。
null和undefined除外,他们太特殊了,他们不会到对象进行正常的转化。
其余类型的原始值会按照上表的方式进行转换。
下面咱们介绍一下,由对象转化为原始值的过程:
JavaScript中对象到字符串的转换通过以下步骤:
1.若是对象具备toString(),则调用这个方法,若是该方法返回一个原始值,则最后转换成字符串。
2.若是对象没有toString()方法,或者这个方法并不返回一个原始值,那么JavaScript会调用valueOf()方法,若是返回的是原始值,最后就转换为字符串。
若是JavaScript没法从toString()和valueOf()中得到一个原始值,就会抛出类型错误的异常。
对象到数字的转换过程当中:
JavaScript优先调用valueof()方法,再调用toString()。
咱们能够知道,利用!
,+
,==
进行隐式类型转换
在这里,咱们有必要了解==
的类型转换机制,以下:
1.若是两个操做数的类型相同,则和上文所述的严格相等的比较规则同样。若是严格相等,那么比较结果为相等。若是它们不严格相等,则比较结果为不相等。
2.若是两个操做数类型不一样,“==”相等操做符也可能会认为它们相等。检测相等将会遵照以下规则和类型转换:
说完了,隐式的类型转换,咱们再看一下js语言提供的显式类型转换:
首先就是最简单的Boolean()
,Number()
,String()
和Object()
,此外咱们知道的toString()
方法和String()
返回的结果是同样的。
Number('3') // => 3 String({}) // => '[object Object]' String([]) // => '' Boolean([]) // => true Boolean('0') // => true Boolean(0) // => false Object(3) // => new Number(3)
其次就是咱们知道的一些全局函数:toFixed
,toExponential
,toPrecision
,parseInt
,parseFloat
var n = 123.45 n.toFixed(0) // => '123' n.toFixed(2) // => '123.45' // 根据指定小数点后的位数,返回字符串 n.toExponential(1) // => '1.2e+5' // 将数字进行科学计数法,传入参数为小数点后数字个数,返回一个字符串 n.toPrecision(4) // => '123.4' // 传入参数为保留数字的个数,返回一个字符串
首先介绍一下typeof
typeof
运算符返回的不是该变量的类型,而是该变量持有值的类型。在js中直接访问一个未声明的变量,会抛出异常,可是在typeof a
中,不会抛出异常,而且返回undefined
。这样就能经过判断是否存在该变量而安全使用该变量。typeof
运算符适合于检测原始类型和函数。
typeof undefined === 'undefined' typeof true === 'boolean' typeof 42 === 'number' typeof 'str' === 'string' typeof Symbol() === 'symbol' typeof null === 'object' typeof function () {} === 'function'
其次介绍一下instanceof
{a: 1} instanceof Object
:右操做符是一个函数构造器,其原理是判断左边对象的原型链上是否有右边构造器的prototype
属性。不一样window或iframe间的对象不能使用instanceof。
[1,2] instanceof Array // => true [1,2] instanceof Object // => true '3' instanceof String // => false new String('3') instanceof String // => true new String('3') instanceof Object // => true
所以咱们看出instanceof
的问题,他对于原始数据类型根本没法检测,对引用数据类型也不能很清楚的断定类别。并且,一旦修改了原型链环节上的prototype
,检测就没法使用。
而后咱们再来看一下constructor
咱们首先明确一下这个概念:
Object.prototype.constructor === Object // => true String.prototype.constructor === String // => true
构造函数的prototype
中的constructor属性指向的是这个构造函数自己,所以咱们能够利用这个特色。
'1'.constructor === String // => true (1).constructor === Number // => true [1,2,3].constructor === Array // => true
除了undefined
和null
,其余类型的变量均能使用constructor
判断出类型。
可是constructor
能够被靠前的原型链覆盖。
var a = [1,2,3] a.constructor = Object a.constructor === Array // => false
因此这个也不是很靠谱
最后咱们来看一下Object.prototype.toString.call
这个方法百试百灵,是目前公认的最靠谱检测数据类型的方法
var toString = Object.prototype.toString; console.log(toString.call(new Date) === '[object Date]') //true console.log(toString.call(new String) ==='[object String]') //true console.log(toString.call(new Function) ==='[object Function]') //true console.log(toString.call(Type) ==='[object Function]') //true console.log(toString.call('str') ==='[object String]') //true console.log(toString.call(Math) === '[object Math]') //true console.log(toString.call(true) ==='[object Boolean]') //true console.log(toString.call(/^[a-zA-Z]{5,20}$/) ==='[object RegExp]') //true console.log(toString.call({name:'wenzi', age:25}) ==='[object Object]') //true console.log(toString.call([1, 2, 3, 4]) ==='[object Array]') //true console.log(toString.call(undefined) === '[object Undefined]') //true console.log(toString.call(null) === '[object Null]') //true
建议使用这个方法!
最后,最近一段时间个人博客会保持长时间更新,针对文章有什么问题,你们能够在下方留言,感谢!