赞
踩
上篇文章聊了js两种数据类型的区别以及基本数据类型,这篇文章主要聊一下js的引用数据类型、包装对象、js的强转换、隐性数据类型转换。
在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。
除了对象,引用数据类型还有很多:
- Array 数组
- Date 日期
- RegExp 正则
- Function 函数
除了上面的数据类型,还有三个比较特殊的(基本数据类型的包装对象):
- Boolean
- Number
- String
大家可能遇到过一道面试题:
- let num1 = 3
- let num2 = new Number(3)
- console.log(num === num2) // false
这个Number()就是基本数据类型的number的包装对象:
- typeof(num1) // number
- typeof(num2) // object
这两个不是一个东西,当然也不相等了,那么我们就来搞清楚,这个包装对象到底是干什么的?
- let str = 'wanghuahua'
- let str1 = str.substr(0, 4)
- console.log(str1) // wang
- console.log(String.substr) // undefined
刚才调用substr方法截取了一下str这个字符串,str是基本数据类型,打印String的substr方法是undefined,这说明String是没有substr方法的,但是为什么能够截取呢?
这中间有一个计算机的操作,我们把它称作‘加工厂’,那么这个加工厂进行了哪些操作呢?(重点)
- 1、原材料进厂:把基本数据类型转成包装对象
-
- 因为基本数据类型是没有方法的,如果你想操作基本数据类型,那就必须转成包装对象
-
- 2、加工:调用substr的相关方法
-
- 包装对象是可以加方法和属性,所以这个时候包装对象会调用方法进行操作
-
- 3、成品出厂:包装对象转成基本数据类型
-
- ECMAScript规范中提供了toPrimitive原则,所以这个地方会遵从toPrimitive原则进行转换。
上篇文章说到了,这个js弱类型语言把相当多的操作都交给了计算机,这个地方就是一个明显的栗子,这些操作就都交给了计算机,相对来说增加了计算机的负担。
包装对象的由来,其实也没有那么复杂,就是为了处理基本数据类型的数据,加了中间的一层。
刚才在说成品出厂的时候,聊到了toPrimitive,toPrimitive就是我们常说的js强制转换:
所谓强制转换,就是人为的进行转换:
- let num2 = new String('hhh')
- console.log(num2.toString()) // hhh
每个对象都有一个 toString()方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。
有时候会有一些非常恶心的面试题,这儿也列举几个不常见的:
- console.log(null.toString()) // 报错
- console.log(undefined.toString()) // 报错
- console.log(true.toString()) // 'true'
- let a = 4
- console.log(a.toString()) // 4
- console.log('hhh'.toString()) // 'hhh'
- console.log(Symbol().toString()) // 报错
除此之外,toString()还接收数字参数,来代表转换的进制:
- 二进制:.toString(2);
-
- 八进制:.toString(8);
-
- 十进制:.toString(10);
-
- 十六进制:.toString(16);
- let num2 = new String('hhh')
- console.log(num2.valueOf()) // hhh
JavaScript 调用 valueOf()方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf 方法一般都会被 JavaScript 自动调用。
- String => 返回字符串
- Number => 返回数字
- Date => 返回时间戳
- Boolean => 返回Boolean的this值
- Object => 返回this
上面两种方法都是object的方法,除了这两个方法之外,还有三个运算符:
- null 转换为 0
- undefined 转换为 NaN
- true 转换为 1,false 转换为 0
- 字符串转换时遵循数字常量规则,转换失败返回NaN
刚才看toString的时候,null和undefined都会报错,但是String就不会报错,万物皆可转,(也不是那么绝对)但是String运算符没办法接收参数。
- String(null) // null
- string(undefined) // undefined
Boolean也很好理解,就是转成布尔值,只需要记住,一下几种转成false,其余的全部是true就行了。
- undefined
- null
- -0
- 0或+0
- NaN
- ''(空字符串)
除了这六种,其余全部都是true,什么空对象,空数组全部都是true,甚至这种:
Boolean(new Boolean(false)) // true
除了上面五种方法,js还有一个相对综合了一下的ToPrimitive,它对基础数据类型是没用的,只有引用数据类型可以。
ToPrimitive(obj,type)
ToPrimitive运算符接受一个需要转换的对象值,以及一个可选择的type值,根据这个可选的type值,来进行转换:
- type为string:
-
- 先调用obj的toString方法,如果为原始值,则return,否则进行第2步
- 调用obj的valueOf方法,如果为原始值,则return,否则进行第3步
- 抛出TypeError 异常
-
- type为number:
-
- 先调用obj的valueOf方法,如果为原始值,则return,否则进行第2步
- 调用obj的toString方法,如果为原始值,则return,否则第3步
- 抛出TypeError 异常
-
- type参数为空
-
- 该对象为Date,则type被设置为String
- 否则,type被设置为Number
因为js是所类型语言,所以相对语法非常宽松,字符串可以和数字运算,数字还能和布尔值运算,在运算过程中就产生了隐式数据类型转换,首先,隐式转换有下面三个原则:
- ++/--(自增自减运算符)
- + - * / %(算术运算符)
- > < >= <= == != === !=== (关系运算符)
但是,这里面还有很多常见的坑,比如:
- 常见的面试题:
-
- console.log(1 + 'true') // '1true'
- console.log(1 + true) // 2
- console.log(1 + null) // 1
- console.log(1 + undefined) // NaN
- console.log(null + undefined) //NaN
关于字符串拼接+和算数运算符+,有个原则,只要有一边是字符串,那这个+就是字符串拼接符,其余的所有的情况都是算数运算符。
- console.log(1 + 'true') // '1true'
-
- 'true'是字符串,所以是字符串拼接,只要是字符串拼接,两边都会调用String运算符
-
- console.log(1 + true) // 2 Number(1)+Number(true) 1+1 2
- console.log(1 + null) // 1 Number(1)+Number(null) 1+0 1
- console.log(1 + undefined) // Number(1)+Number(undefined) 0+undefined NaN
- console.log(null + undefined) //Number(null)+Number(undefined) 0+undefined NaN
-
- 上面的没有一边是字符串,所以是运算符,如果被当做运算符,那么两边都会调用Number运算符
- 常见的面试题:
-
- console.log(2>'10') // false
- console.log('2'>'10') // true
- console.log(2>'10') // Number(2)>Number(10) 2>10 false
- console.log('2'>'10') //2.charCodeAt()>10.charCodeAt() 50>49 true
'2'>'10'进行charCodeAt()的时候,会先比第一个字节,如果不一样,直接就返回结果,如果第一个一样会接着比第二个字节。就是2.charCodeAt()会先跟1.charCodeAt()进行比较。
- console.log([1,2] == '1,2') //true
-
- 在这个过程中[1,2]是引用数据类型,会先调用valueOf()方法
- 如果返回的还是引用数据类型,再调用toString()方法
- console.log([] == 0) // true
- console.log(![] == 0) // true
为啥[]等于0,![]也等于0,这不是毁三观吗?
- 1、在讲引用数据类型阴性转换的时候,我们说了引用数据类型进行隐形类型转换必须先转成string
- [].valueOf().toString() // ''
-
- 2、==数据关系运算符,两边必须都转成Number
- Number('') // 0
-
- 所以true
- 1、在讲Boolean运算符的时候说了
- 除了undefined、null、-0、0或+0、NaN、''(空字符串),其余全部都是true
- 所以[]是true,那么![]就是false
-
- 2、两边在转Number的时候:
- Number(false) // 0
-
- 所以![] == 0也是true
- [] == ![] //true
- [] == [] // false
[]等于非[]竟然是true,[]等于他自己反倒是false?
- 1、[]还是会转成string,就是'''
- 2、![]还是false
- 3、‘’和false都进行Number操作都是0
-
- 所以相等
这个在上一篇讲栈内存的时候说了,引用数据类型在栈中存的都是地址,所以这两个肯定不一样
- {} == !{} // false
- {} == {} // false
{}和[]都是引用数据类型,为啥他是false呢?
- 1、{}还是会转成string,但是{}转成字符串是""[object Object]"
- 2、![]还是false
- 3、Number(""[object Object]")和Number(false)
-
- 所以不相等
同理,也是比较栈内存的地址,所以false
关于js的数据类型,主要的就是隐性数据类型转换,会经常出错,这个系列的两篇文章主要是聊了一下js的赋值和赋址的区别、强转换以及隐性转换我们经常出现的问题。
个人的微信公众号:小Jerry有话说,平时会发一些技术文章和读书笔记,欢迎交流。
后面会持续更新一些js基础的文章,有兴趣的可以点个关注。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。