一.深入探讨JS数据类型

一.数据类型分类(7+1)

最新版本的JS标准中,数据类型分了8种,包括:

  • 7种基本数据类型:Undefined、Null、Boolean、Number、String、Symbol(es6新增)和BigInt(es10新增);
  • 1种引用数据类型:Object(Object本质上是由一组无序的名值对组成的)。里面包含 function、Array、Date等。JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述 8 种数据类型之一。

1.1 基本数据类型细述

基本数据类型也是简单数据类型,或者原始类型,值类型;基本类型的变量值和变量标识符标识符一起存放在栈区的,一般不可改变。占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。

1.1.1 typeof操作符

typeof 可以判断所有的值类型数据,返回的都是字符串

  • 常见的值类型数据有:number,string,boolean,undefined,symbol,
  • 可以判断常见的引用类型数据:object,array,null.返回的都是 object
  • 判断函数 function 返回的是 function
1.1.2 undefined类型

声明后变量的没有初始化就是undefined,是唯一值;

//特殊情况是即使是没有声明变量a
typeof a ;//undefined
复制代码
1.1.3 null类型

null值表示一个空对象指针,表现为检验typeof null会返回’Object’;

null的用法:

  • 在定义将来要保存的对象时建议用null初始化;
  • null是个假值;所以,null == undefined ;为true。
  • 一律用===,只有判断是不是等于 null 或 undefined 才用==
  • null==0 ;//false
  • !null==true;//true 常用于if判断
1.1.4 Boolean类型

Boolean类型是JavaScript中使用最多的一种基本数据类型,只有两个值true和false(全为小写)。

JavaScript中所有类型的值都有与这两个Boolean值等价的值,可以调用转型函数Boolean()将其他类型的值转化为Boolean值。

不同类型值与布尔值的转换规则如下:

[] == false; //true
![]==false   //true表示把空数组转化为布尔值在进行取反然后和右边的比较
数据转化为布尔值的规律:只有`0/NaN/null/undefined`五个值是false,其余全为true
复制代码
1.1.5 Number类型
  • Number可以同时表示整数和浮点数值,用浮点值(小数)进行计算会不精确,即0.1+0.2=0.30000000000000004;
  • NaN:是非数字,英文名字not a number;函数isNaN();这句话的意思是是否是非数字?
  • NaN==NaN //false;
1.1.5.1 Number的数值转换函数Number()

Number()可以用于任何数据类型:

    • 布尔转化0,1
    • 数值不转换
    • null转为0
    • undefined转为NaN
    • 字符串:正常数字返回,16进制转化10进制; 空串为0,其他NaN
    • 对象:调用对象的 valueOf()方法,然后依照前面的规则转换返回的值。如果转换 的结果是 NaN,则调用对象的 toString()方法,然后再次依照前面的规则转换返回的字符 串值。
1.1.5.2 Number的数值转换函数parseInt()
    • parseInt()专门用于把字符串转换为数值:
    • 函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。
    • 如果第一个字符不是数字字符或者负号,parseInt()就会返回 NaN;也就是说,用 parseInt()转换空字符串会返回 NaN(Number()对空字符返回 0)。
    • 如果第一个字符是数字字符,parseInt()会继续解析第二个字符,直到解析完所有后续字符或者遇到了一个非数字字符。
var num1 = parseInt("1234blue"); // 1234
var num2 = parseInt(""); // NaN
var num3 = parseInt("0xA"); // 10(十六进制数)
var num4 = parseInt(22.5); // 22
var num5 = parseInt("070"); // 56(八进制数)
var num6 = parseInt("70"); // 70(十进制数)
var num7 = parseInt("0xf"); // 15(十六进制数)
复制代码
  • parseInt()可以引入第二个参数:转换时使用的基数。
var num1 = parseInt("10", 2); //2 (按二进制解析)
var num2 = parseInt("10", 8); //8 (按八进制解析)
var num3 = parseInt("10", 10); //10 (按十进制解析)
var num4 = parseInt("10", 16); //16 (按十六进制解析)
复制代码
1.1.5.3 Number的数值转换函数parseFloat()

parseFloat() 与 parseInt()函数类似,parseFloat()也是从第一个字符(位置 0)开始解析每个字符。而且也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。

1.1.6 String类型

字符串是不可变的,若果要修改的话,必须要销毁原始的字符串,字符串可以用双引号、单引号、反引号标示。

1.1.6.1 转化为字符串
  • 1、toString(): 除了null和undefined值没有tostring()方法,其他值都有这个方法,该方法返回字符串的一个副本。
  • 2.String():如果不确定一个值是null或undefined,可以使用String()转型函数,始终会返回相应类型值的字符串.
  • 3.使用+" ": 即可以通过要转换的值 + 空字符串(” “),也可以实现转换。
1.1.6.2 模板字面量、字符串插值、模板字面量标签函数
模板字面量

ES6新增的模板字面量使用反引号包裹,它的作用是保留换行字符,可以跨行定义字符串

字符串插值
  • 模板字面量是一种特殊的JavaScript句法表达式,只不过求值之后得到的是字符串。即模板字面量在定义时立即求值并转化为字符串实例。任何插入的变量也会从他们最接近的作用域中取值。
  • 字符串插值通过${}使用一个JavaScript表达式实现
      let num = 5;
      let square = "二次方";
      let res1 = num + "的" + square + "是" + num * num;
      let res2 = `${num}${square}${num * num}`;
      console.log("字符串拼接", res1); //字符串拼接 5的二次方是25
      console.log("模板字符串", res2); //模板字符串 5的二次方是25
复制代码
1.1.7 Symbol类型(ES6)

ES6新规定的Symbol(符号)是原始值,且符号实例唯一、不可变的,它的用途是确保对象属性使用唯一标识符。

使用方法

需要使用Symbol()函数初始化

 let name1 = Symbol('liming');
 let name2 = Symbol('liming');
 console.log(name1 == name2);  //false
 
// 希望能够多次使用同一个symbol值
 let name1 = Symbol.for('name'); //检测到未创建后新建
 let name2 = Symbol.for('name'); //检测到已创建后返回
 console.log(name1 === name2); // true
复制代码
  • Symbol的另一特点是隐藏性,Symbol 作为属性名,遍历对象的时候,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。

 let id = Symbol("id");
 let obj = {
  [id]:'symbol'
 };
 for(let option in obj){
     console.log(obj[option]); //空
 }
复制代码
  • 但是也有能够访问的方法:Object.getOwnPropertySymbols.该方法会返回一个数组,成员是当前对象的所有用作属性名的Symbol值。

 let id = Symbol("id");
 let obj = {
  [id]:'symbol'
 };
let array = Object.getOwnPropertySymbols(obj);
 console.log(array); //[Symbol(id)]
 console.log(obj[array[0]]);  //'symbol'
复制代码
1.1.8 BigInt类型(ES10)

BigInt是JavaScript中一种可以用来表示任意精度(arbitrary precision)整数的基本数据类型,使用BigInt可以安全的存储和操作任意大小的整数而不受Number类型的安全值范围的限制。

方法一:生成一个BigInt类型的值只需要在任意整数后加上n做后缀即可。
      例如:123BigInt类型表示123n.
 方法二:通过全局函数BigInt(number)来将Number类型转化为BigInt类型
复制代码

BigInt类型更详细的介绍请参考MDN文档—>传送门

1.2 引用数据类型-Object类型

简介:
1.Object类型是JavaScript中最庞大而复杂的引用数据类型,本文只做简单介绍,后续的文章会做Object的详细介绍。
2.Object,即对象,是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型的名称来创建。而创建 Object 类型的实例并为其添加属性和(或)方法,就可以创建自定义对象。

1.2.1 Object实例属性和方法:

由于在 ECMAScript 中 Object 是所有对象的基础,因此所有对象都具有这些基本的属性和方法。Object 的每个实例都具有下列属性和方法:

constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就是 Object()。


hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如:o.hasOwnProperty("name"))。


isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型。


propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句来枚举。与hasOwnProperty()方法一样,作为参数的属性名必须以字符串形式指定。


toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。


toString():返回对象的字符串表示。


valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值相同。
复制代码

二.数据类型比较

2.1基本数据类型

  • 值是不可变的
  • 一.可变和不可变类型

可变类型:值发生改变时,内存地址不变,即id不变,证明在改变原值
不可变类型:值发生改变时,地址也发生改变,即id也变,证明是没有在改变原值,是产生了新的值

var name = "aaaaaa";
name.toUpperCase();//输出 AAAAAA
console.log(name);// 输出 aaaaaa
复制代码
  • 存放在栈区

基本类型值指的是简单的数据段,按值访问,可操作保存在变量中的实际的值,其占据空间小、大小固定,属于被频繁使用的数据,所以放入栈(stack)中存储。

  • 值的比较
var n = 1;
var m = true;
console.log(n == m);//true
console.log(n === m);// false
复制代码

2.2 引用数据类型

  • 值是可变的
var person = {
  	  name:'jake',
        age:22,
        action:function () {
         console.log("do something!")
        }
      }
person.age = 23;
console.log(person.age)// 23

从上面的代码可看出引用数据 类型可以拥有一个或多个属性和方法,而且是可以动态修改的,是在原值上修改的。
复制代码
  • 同时存放在栈内存和堆内存

引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

  • 值的比较

当从一个变量向另一个变量赋引用类型的值时,同样也会将存储在变量中的对象的值复制一份到位新变量分配的空间中。

var person1 = {
		age:20
          }
var person2 = person1;
person2.age = 23;
console.log(person1.age == person2.age)// true
/*
引用数据类型存储`在堆中的对象`,`在栈中存储了指针`,而这个指针的指向正是堆中实体的起始位置。变量person1初始化时,person1指针指向该对象{age:20}的地址,将person1赋给person2后,person2又指向该对象{age:20}的地址,这两个变量指向了同一个对象。因此改变其中任何一个变量,都会相互影响。
此时,如果取消某一个变量对于原对象的引用,不会影响到另一个变量。
*/
var a = {age:22}
var b = a;
a = 1;
console.log(b);//{age:22}
/*
上面代码中,a和b指向同一个对象,然后a的值变为1,这时不会对b产生影响,b还是指向原来的那个对象。
*/
复制代码

三.JS数据类型隐式转化(你只需要记住常见的几种)

3.1转成String类型

字符串连接符(+)转成字符串
 var a = 10;
 var b = "20";
 var c = a + b;
 console.log(c,typeof c);//1020 String
复制代码

3.2转成Number类型(数学运算中)

  • 自增自减运算符 ++/–

  • 减乘除求余算数运算符 +-*/%

  • 关系运算符 > < >= <= == != === !===

    • 当关系运算符一边有字符串时,会将其数据类型使用Number转换,再做比较;
  • 特殊情况,请记住以下结论(判断相等吗,只有数字才能判断相等吗):

console.log(undefined == undefined) // true 
console.log(undefined == null) // true 
console.log(null == null) // true
console.log(NaN == NaN) // false NaN与任何数据比较都是NaN
复制代码

3.3转成Boolean型(逻辑判断中)

  • 数据在逻辑判断和逻辑运算之中会隐式转换为Boolean类型
  • 逻辑非运算符! 逻辑非运算中,会将数据先做Boolean转换,然后取反
  • 以下这几种数据经过Boolean转换,会转成:false:
    • +0、-0、NaN、undefined、null、””、document.all();
  • 复杂数据类型经过Boolean转换后都是true,如:[]、{}
if( {} ){
console.log('12131231')
}    //12131231

if( null ){
console.log('4444444444')
}  //压根不执行

if( [] ){
console.log('shuzu')
}//shuzu

if( !null ){
console.log('4444444444')
}//444444444
复制代码

3.4 复杂数据类型隐式转换

复杂数据类型隐式转换时会先调用自身的valueOf()和toString()两个函数,如果自身数据原型对象上没有相应的函数则会由原型链__proto__最终调用到Object.prototype对象对应的函数上,所有对象(除Null 和 undefined)都会继承这两个方法。

转换规则如前面所述,使用valueOf()获取原始值,如果原始值不是基本类型,则使用toString方法转成字符串

console.log([1,2] == '1,2') // true 解析如下

console.log([1,2].valueOf()) // [1,2],获取原始值
console.log([1,2].toString()) // '1,2',转成字符串,与右边数据相等

var a = {}
console.log(a == "[object Object]") // true

// 左边转换过程
console.log(a.valueOf()) // {}
console.log({}.toString()) // "[object Object]",再进行比较

复制代码

3.5逻辑非隐式转换与关系运算符隐式转换

if (console.log(1111)||undefined){

    console.log(1111);

} else {
console.log(22222)

}//1111   22222
复制代码

逻辑非优先级高于关系符运算

console.log(![] == 0) // true 
/*
解析:空数组转换布尔型是true,取非后为false;false跟数字0比较,
布尔型被Number后为0,0 == 0
*/

console.log([] == ![]) // true 
/*
[].valueOf().toString()=>''; 
![] => false 
关系运算符将两边转成Number型进行比较,Number('') => 0; Number(false) => 0
*/
console.log({} == !{}) // false 
/*
逻辑非优先级高,其实是{}和!{},这个逻辑表达式的比较,按照复杂类型隐式转换规则,需通过valueOf和toString转换后进行比较

console.log({}.valueOf()) // {}
console.log({}.toString()) // "[object Object]",再进行比较
*/
复制代码

3.6引用数据类型的转化处理

  • 引用数据类型,可称为对象类型,包括Object 、Array 、Function 、Date等;数据存在堆中,变量中存的是堆地址,我们只能操作存在栈内存的引用地址。
  • var声明的一般是栈内存
  • 6种基本数据类型的存储方式是值类型,存在于栈中
console.log([] == []) // false 数组为引用类型,在堆中存放的是两份不同的数据,所以比较结果不相等
console.log({} == {}) // false,同理,{}为引用类型,结果不相等
复制代码

在这里特别感谢快狗打车前端团队提供的优秀的技术博文参考,他们有很多优秀的文章值得阅读,若果你想去看,请点击—>传送门

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享