-
虽然现在前端技术很多很复杂,但是归根到底,学好js才是重中之重,只有扎实的基础才能往上面盖大楼。
-
为什么学习这个是必要的呢,首先可以在边界数据类型条件判断问题中体现出来
-
比如一个函数检测参数类型当中
-
-
接下来围绕概念、检测方法、转换方法进行讲解
一、数据类型的基本概念
-
下面用一张图来说明
-
其中基础数据类型为7中
-
undefined、Null、Boolean、String、Number、Symbol、BigInt
-
-
复杂数据类型为object,且包括其之下的类型
1.1 存储方式
-
① 基础类型存储在栈内存,被引用或拷贝,会创建一个完全相等的变量
-
② 引用类型存储在堆内存,存储的是地址,其变量名存储在栈中,多个引用指向桐一个地址,这里会涉及一个“共享的概念”
1.2 题目理解
-
用题目来讲解一下共享的概念
-
① 初出茅庐
-
let a = {
name: ‘lee‘,
age: 18
}
let b = a;
console.log(a.name); // lee
b.name = ‘son‘
console.log(a.name); // son
console.log(b.name); // son -
这里面通过b的改动使a的值也产生了变化,足够说明共享这个概念
-
-
② 渐入佳境
-
let a = {
name: ‘Julia‘,
age: 20
}
function change(o) {
o.age = 24
o = {
name: ‘Kath‘,
age: 30
}
return o;
}
?
let b = change(a);
console.log(b.age); // 这里面b的值是change函数return的o, 所以是30
console.log(a.age); // 这里不像上面那题那样也改变了, 而是为24 -
函数传参进来的只是一个内存地址, 在函数里面改变参数的地址指向不会影响实参的地址指向
-
二、数据类型的判断方法
-
也就是数据类型的检测方法
-
其中判断方法有很多种
-
① typeof
-
② instanceof
-
③ Object.toString.call()
-
2.1 typeof
-
其中null检测出object可以说是js的一个bug
-
可以用 === 判断是否为null
-
-
复杂数据类型中,除了function,其他都是无法直接使用typeof判断出来的
2.2 instanceof
-
返回一个布尔值,表示是否属于该原型对象
-
其中基本数据类型要用new出来的实例对象才能判断,不然为false
-
自己实现一个instanceof
function myInstanceof(left, right) {
if (typeof left !== object || left === null) return false;
let proto = Object.getPrototypeOf(left);
while(true) {
if (proto === null) return false;
if (proto === right.prototype) return true; // 比较right的原型对象
proto = Object.getPrototypeOf(proto); // 如果还没有结束循环则继续往下判断
}
}
-
getPrototypeOf是Object对象自带的API,能拿到参数的原型对象
2.3 判断以上两种方法的差异
-
① instanceof可以准确地判断复杂引用类型,但是不能正确判断基础数据类型
-
② typeof虽然可以判断基础数据类型,但是除了function之外其他的复杂数据类型无法判断
2.4 Object.prototype.toString.call**
-
要注意返回的都是[object Xxx]的格式,其中第一个X是大写
-
自行封装的一个getType
-
这里要注意的是,typeof无法识别出new 基础类型的数据类型,比如new String(‘a‘),会让最后的返回结果为String而不是string,所以可以针对大小写进行转换
三、数据类型的转换方式
-
接下来用一张图片来开启这个内容
3.1 强制类型转换
-
Number()、parseInt()、parseFloat()、toString()、String()、Boolean()
-
如这样的话会强制进行数据类型的转换
-
① Number
-
接下来展示一些Number转换之后的结果
-
② Boolean
-
除了undefined、null、false、0(包括-0跟+0)、‘’、NaN转换出来的是false。其他都是true
-
比如
-
Boolean(0) //false
Boolean(null) //false
Boolean(undefined) //false
Boolean(NaN) //false
Boolean(1) //true
Boolean(13) //true
Boolean(‘12‘) //true
-
3.2 隐式类型转换
-
如遇到以下 符号 / 关键字 都会触发隐式转换
-
接下来主要说==和+这两个符号触发的隐式转换
-
① ==
-
如果类型相同,则不用进行转换
-
如果其中一个操作值是null或undefined,则另一方必须是null或者undefined才会返回true
-
undefined == null // true
-
undefined == undefined // true
-
null == null // true
-
-
如果其中一方是Symbol,则返回false
-
如果两个操作符是number和string,则转换为number
-
如果其中一个是boolean,则转换成number
-
如果一个操作之为object,且另一方为string、nukber、symbol,则把object转换为原始数据类型再判断(valueOf、toString)
比如以下结果
null == undefined // true 规则2
null == 0 // false 规则2
‘‘ == null // false 规则2
‘‘ == 0 // true 规则4 字符串转隐式转换成Number之后再对比
‘123‘ == 123 // true 规则4 字符串转隐式转换成Number之后再对比
0 == false // true e规则 布尔型隐式转换成Number之后再对比
1 == true // true e规则 布尔型隐式转换成Number之后再对比
var a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
// 注意这里a又可以等于1、2、3
console.log(a == 1 && a == 2 && a ==3); //true f规则 Object隐式转换
// 注:但是执行过3遍之后,再重新执行a==3或之前的数字就是false,因为value已经加上去了,这里需要注意一下
-
-
② +
-
如果其中一个是字符串,另外一个是undefined、null、Boolean则调用toString()进行字符串拼接
-
如果是纯对象、数组、正则,则默认调用转换方法再拼接
-
-
如果其中一个是Numebr,另外一个是undefined、null、Boolean或Number,则调用Number()并进行加法运算,对象、数组、正则跟上一样
-
如果其中一个是字符串、一个是数字,则按照字符串拼接规则
比如以下结果
1 + 2 // 3 常规情况
‘1‘ + ‘2‘ // ‘12‘ 常规情况
// 下面看一下特殊情况
‘1‘ + undefined // "1undefined" 规则1,undefined转换字符串
‘1‘ + null // "1null" 规则1,null转换字符串
‘1‘ + true // "1true" 规则1,true转换字符串
‘1‘ + 1n // ‘11‘ 比较特殊字符串和BigInt相加,BigInt转换为字符串
1 + undefined // NaN 规则2,undefined转换数字相加NaN
1 + null // 1 规则2,null转换为0
1 + true // 2 规则2,true转换为1,二者相加为2
1 + 1n // 错误 不能把BigInt和Number类型直接混合相加
‘1‘ + 3 // ‘13‘ 规则3,字符串拼接-
注意undefined转换为数字是NaN
-
-
3.3 对象转换规则
-
① 如果部署了[Symbol.toPrimitive]()方法则优先调用再返回
-
② 调用valueOf(),如果转换为基础类型则返回
-
③ 调用toString(),如果转换为基础类型则返回
-
④ 如果都没有返回基础类型,则报错
比如以下代码
var obj = {
value: 1,
valueOf() {
return 2;
},
toString() {
return ‘3‘
},
[Symbol.toPrimitive]() {
return 4
}
}
console.log(obj + 1); // 输出5
// 因为有Symbol.toPrimitive,就优先执行这个;如果Symbol.toPrimitive这段代码删掉,则执行valueOf打印结果为3;如果valueOf也去掉,则调用toString返回‘31‘(字符串拼接)
// 再看两个特殊的case:
10 + {}
// "10[object Object]",注意:{}会默认调用valueOf是{},不是基础类型继续转换,调用toString,返回结果"[object Object]",于是和10进行‘+‘运算,按照字符串拼接规则来,参考‘+‘的规则C
[1,2,undefined,4,5] + 10
// "1,2,,4,510",注意[1,2,undefined,4,5]会默认先调用valueOf结果还是这个数组,不是基础数据类型继续转换,也还是调用toString,返回"1,2,,4,5",然后再和10进行运算,还是按照字符串拼接规则,参考‘+‘的第3条规则