js数据类型

625 阅读10分钟

JS的数据类型

一、思考:浏览器/计算机是如何管理各种数据类型值的?

所有的数据类型值,在计算机底层,都是以2进制进行存储的

常用的进制

  • 2进制 : 0 1
  • 8进制 : 0~7
  • 10进制 : 0~9 {我们平时写代码,写出来的值都是10进制的}
  • 16进制 : 0~9 A-F

如果我们写值是以“0x”开头的,浏览器认为其是16进制,默认帮我们转换为10进制进行处理,如果写的值是以“0”开始的,浏览器认为其实8进制,也帮助我们默认转换为10进制,剩余写的值,都是按照10进制算的;但是不论咋样,计算机最后都是按照2进制进行存储!!

JS使用number类型表示数字(整数和浮点数)

  • 遵循 IEEE-754 标准 通过64位二进制值来表示一个数字
    • 第0位:符号位,0表示正数,1表示负数 S
    • 第1位到第11位「11位指数」:储存指数部分 E
    • 第12位到第63位「52位尾数」:储存小数部分(即有效数字)F 注:尾数部分在规约形式下第一位默认为1(省略不写)

把其它进制转换为10进制

'10101' 2机制 -> 10进制 : 1 * 2^0 + 0 * 2^1 + 1 * 2^2 + 0 * 2^3 + 1 * 2^4

 parseInt('12px',10)
      在’12px‘字符串中从左查找,找到所有符合10进制的内容「0~9」   => '12'
      把找到的'12'当做10进制,转换为10进制 => 12

   parseInt('12px',2)
      在`12px`中查找,找到所有符合2进制的「0~1」 => '1'
      把`1`看做2进制转化为10进制 => 


   其它进制转换为10进制练习题
      101  这是一个二进制的
      1*2^2 + 0*2^1 + 1*2^0  ->   4+0+1  => 5

      241 这是一个八进制
      2*8^2 + 4*8^1 + 1*8^0 = 128+32+1 = 161

十进制 (decimal) 转 二进制 (binary)

[number].toString([radix]) : 把一个十进制数字转换为[radix]进制的字符串,如果不写[radix],默认是10

整数

用十进制的值一直除以2,直到商为0结束,把每一次取到的余数,从末尾到开始串起来即可

转二进制.jpg

var n = 42;
console.log(n.toString(2)); //->'101010'  二进制字符串
console.log(n.toString(8)); //->'52'  八进制字符串

浮点数

用十进制浮点数乘以2,每一次去整数部分,把剩下的小数部分继续乘以2...直到乘积是1,没有小数为止... {很多时候会出现无限循环下去} -> 但是计算机存储最长64位,超出的部分干掉了,也就是 计算机底层存储的浮点数的二进制值,不一定是准确的,有可能是省略后的结果...

转十进制.png

计算精准度问题:0.1+0.2=?

// var n = 0.1;
// console.log(n.toString(2)); //'0.0001100110011001100110011001100110011001100110011001101'
// console.log(0.1 + 0.2 == 0.3); //false
// console.log(0.1 + 0.2); //0.30000000000000004
// console.log(((0.1 * 10) + (0.2 * 10)) / 10); //0.3 乘以相关系数

二、原始值数据类型

number

  • 分类:正数 负数 0 小数 NaN(Not a Number) Infinity(无穷大)负无穷大(-Infinity) JS中最大安全数字「16位」:9007199254740991
  • 获取最大值的两种方式
    • Number.MAX_SAFE_INTEGER
    • Math.pow(2,53)-1

string 字符串类型

在JS中但凡用单引号或者双引号 反引号 包起来的都是字符串

  • 反引号是ES6新提供的模板字符串,好处是方便字符串拼接 ${变量/运算程序} 可以换行
  • 每一种引号里面不能包自己
  • 单双引号里可以加\作为转义字符''这样就会让带有特殊含义的字符变成普通字符,\n把普通字符转化为特殊含义的字符,单引号要加转义字符才能随意换行
    • \:换行 编写字符串的时候看起来换行,实际上字符串并没有换行
    • \n:换行符 字符串真的会换行
    • \t:制表符
    • +:字符串拼接
代表字符串的1 :var num="1";
代表数字1:var num2=1;
  • 获取字符串的长度:变量.length
var a="hello";
console.log(a.length)==>5

索引

每个字符都对应一个下标,叫做索引,从0开始逐渐递增:0,1,2,3...

  • 第一个字符索引:0
  • 最后一个字符索引:【变量】.length-1
  • 获取当前索引位置对应的字符:【变量】【索引值】
var a="hello"
a[0]===>"h"   获取到第一个字符
a.length-1    获取最后一个字符的索引值 
a[a.length-1] 获取最后一个字符

布尔

  • 有两个值:一般用于判断
    • true:真
    • false:假

null空对象指针{没有}

null已知的没有,一般都是先手动赋值为null,后期再给赋予其他值

1.声明变量的时候,不知道这个数据类型是什么,可以先赋值为null,后面可以再给具体的值
var dom=document.getElementById("box");
console.log(dom);
null
2.获取页面中不存在的元素
typeof null;
"object"
3.如果想要清除对象的空间地址时候,我们可以赋值为null
var obj={"name":"li"};
obj=null;

undefined未定义{没有}

undefined未知的没有,一般都是浏览器默认赋予的空

1.只声明,没定义 undefined
var name;
console.log(name);
undefined
2.想要获取对象中的属性值,里面没有对应的属性, undefined
var obj={"name":"lili"};
console.log(obj.name)
lili
console.log(obj.age)
undefined
3.函数里面设置了形参,在调用的时候,没有传实参,在函数中打印形参,undefined
function fn(x,y){console.log(x,y)}
fn();
undefined
4.函数没有return返回值,函数的返回结果就是undefined
function fn(x,y){var total=x+y}
var res=fn(1,2);
console.log(res)
undefined

symbol(唯一值)

console.log(Symbol() == Symbol()); //false
console.log(Symbol('AA') == Symbol('AA')); //false
let n = Symbol('AA');
let m = n;
console.log(n == m); //true 

BigInt

  • 最大安全数字Number.MAX_SAFE_INTEGER =9007199254740991
  • 必须是整数,不能是小数

超过最大安全数字,不能进行准确的计算,所以出现了BigInt这个新的类型

var obj=456n;此时obj就是最大数数据类型,而不是数字数据类型了
console.log(Symbol() == Symbol()); //false
console.log(Symbol('AA') == Symbol('AA')); //false
let n = Symbol('AA');
let m = n;
console.log(n == m); //true 

三、对象数据类型

  • 对象:把描述同一件事物特征的属性和方法放在一起,实现分组和分类的作用,避免相互冲突
  • 零到多组 键值对(属性名和属性值)组成的
  • 注意:属性名是不能冲突的,而且属性名的类型是字符串和Symbol,属性值可以是任何类型的
//如果想要获取唯一值类型
var seb=Symbol(123);
var obj = {
    // 'name'
    name: '东方淼淼',
    // 'age'
    age: 18,
    // 0会默认转换为字符串 '0' 作为属性名
    0: 100,
    1: true,
    // Uncaught SyntaxError: Unexpected token ':' 语法错误
    // Symbol(): 100
    // 想要设置Symbol类型的属性名,直接写会报语法错误,需要拿[]包起来
    [Symbol()]: 100,
    [seb]:101//变量承接
};

var name = '1';
console.log(obj['name']); //获取成员是"name"的值  '东方淼淼'
console.log(obj[name]); //当name没有加引号时,就在全局变量里面查找,此时在var name = '1';中,把'1'这个属性值,作为属性名,给obj里面的name,相当于obj['1'] 获取name变量存储的那个值,所对应的成员属性值  true */
// 'name'
// name 变量 代表的是 '1'
console.log(obj.name);//获取成员是"name"的值  '东方淼淼'
console.log(obj['name']);//获取成员是"name"的值  '东方淼淼'
// console.log(obj.0); //Uncaught SyntaxError: missing ) after argument list
console.log(obj['0']);//100
console.log(obj[0]); //浏览器会默认把0转换为'0' 100
console.log(obj[Symbol()]); //undefined 此处是创建了一个新的唯一值,不是获取之前那个唯一值的属性
如果对象中没有这个成员,而我们去获取了,结果不会报错,但是值是undefined */
console.log(obj[seb]);//会找参数seb 这个参数存放的是属性名 101

标准普通对象

标准特殊对象

数组:[1,23]
正则:var reg = /^[+-]?(\d|([0-9]\d+))(\.\d+)?$/;
日期对象:var time = new Date();
错误对象
// var err = new SyntaxError();
// SyntaxError 语法错误
// ReferenceError 引用错误
// TypeError 类型错误
// Error 错误
Math()//数学方法

非标准特殊对象

可执行/调用对象 function 实现了call或者能调用call方法」函数 function

function sum(x, y) {
    return x + y;
}
sum(10, 20);

四、数据间的相互转换

小总结.png

把其它类型转换为数字

转数字.png

Number([value]) 按照计算机底层存储的二进制值进行转换的

  • 一般用于隐式转换「数学运算、isNaN、==比较...」
  • 字符串->数字 空字符串变为0 & 字符串中只要出现非有效数字字符结果就是NaN
  • 布尔->数字 true变为1 false变为0
  • null->0
  • undefined->NaN
  • Symbol->报错
  • BigInt->正常转换
  • 对象遵循 Symbol.toPrimitive/valueOf/toString/Number
Number("123")=>123
Number("")=>0
Number("12px")=>NaN
Number(true)=>1
Number(false)=>0
Number(null)=>0
Number(undefined)=>NaN
Number(99999n)=>99999
Number({"name":"lili"})=>NaN
Number([1,2])=>NaN
Number([1])=>1
Number([])=>0
//唯一值类型会报错
Number(Symbol("你是最帅的"))

parseInt/parseFloat([value])

  • 首先会把[value]变为字符串,从字符串左侧第一个字符开始查找,直到找到一个非有效数字字符为止,把找到的结果转换为数字,一个都没找到,结果就是NaN「parseFloat多识别一个小数点」
parseInt("123")=>123
parseInt("")=>NaN
parseInt("12px")=>12
parseInt(true)=>1
parseInt(false)=>0
parseInt(null)=>NaN
parseInt(undefined)=>NaN
parseInt(99999n)=>99999
parseInt({"name":"lili"})=>NaN
parseInt([1,2])=>1
parseInt([1])=>1
parseInt([])=>NaN
//唯一值类型会报错
parseInt(Symbol("你是最帅的"))

parseFloat("12.5")=>12.5
parseInt("12.5px")=>12  遇到非有效数字停止
parseInt("wid12.5px")=>NaN

parsetInt([val],[radix])处理机制

  • [val] 必须是一个字符串,如果不是,则也要默认转换为字符串
  • [radix]不设置(或者写的是零):正常都是按照10处理的,如果字符串是以”0x“开始的,默认值是16...
  • 先在[val]中,找到所有符合[radix]进制的内容(从左到右查找,直到遇到不符合的为止「不论后面是否还有符合进制的,都不在查找了」),然后再把找到的内容看做[radix]进制,转换为十进制
  • [radix]范围 2~36,除了0以外(0->10/16),不在这个范围内,结果都是NaN

isNaN()

console.log(NaN == NaN); //false
let n=?;
if(n==NaN){ //这个条件永远不会成立,所以不要这样判断是否为有效数字
    console.log('n不是有效数字');
}
// isNaN([value]):验证这个值不是有效数字的命题是否成立,如果真的不是有效数字,则返回true,反之是false
//   + isNaN('12px') 如果检测的值不是number类型的,首先需要转换成数字类型「隐式转换:Number」
if(isNaN(n)){
    console.log('n不是有效数字');
} 

其他数据类型转字符串

转字符串.png 规则:原始值转换是直接用引号包起来「bigint会去除n」;除对象转换为字符串是比较特殊的;

toString「排除Object.prototype.toString{检测数据类型}」

字符串/模板字符串拼接「“+”在JS中除了数学运算还有字符串拼接{但是其它运算符一般都是数学运算}」

  • “+”左右两边,有一边出现了 字符串或者部分对象 则都是按照字符串拼接处理的
  • Symbol.toPrimitive/valueOf/toString
  • “+”有一边出现对象:{}+10 / 10+{}
  • “+”只有一边:+n / ++n / n++

把原始值其它类型转换为字符串:[value].toString / String([value])

[value].toString 浏览器隐式转换都按照这个来

对象类型值转换为字符串/转换为数字 {V8「webkit」底层渲染机制}

  • 第一步:先检测是否存在 Symbol.toPrimitive 这个属性,存在则执行这个方法获取
  • 第二步:如果不存在,则继续基于 valueOf 方法获取原始值
  • 第三步:如果valueOf获取的不是原始值,则再调用toString转换为字符串
  • 第四步:如果是转换为数字,则在Number一下即可
var arr = [10, 20, 30];
console.log(arr + 10); //"10,20,3010"
 先看 arr[Symbol.toPrimitive] ->undefined
 再看 arr.valueOf() ->[10,20,30] 不是原始值
 再看 arr.toString() -> "10,20,30"
 字符串拼接处理了
 
 // 需求:我就想变为20数字
arr[Symbol.toPrimitive] = function () {
    return 10;
};
console.log(arr + 10); */
var n = new Number(10);
//  n[Symbol.toPrimitive]  undefined
//  n.valueOf()  -> 10
console.log(n + 10); //20

把其它类型转换为布尔

  • 规则:只有“0、NaN、null、undefined、空字符串”会变为false,其余都是转换为true
  • 场景:Boolean([value]) \ !![value] \ ![value] 转换为布尔类型取反
  • 条件判断 例如:if(1){} \ A||B A&&B ...

五、补充

“+”:在JS中除了数学运算,还有字符串拼接

“+”有左右两边,其中有一边是一个字符串「或者是个对象」,这样是字符串拼接

  • 排除:{}+1 认为左边{}是个代码块,和“+1”是分开的,其实我们运算的“+1”
  • ({}+1) 这就是字符串拼接了,因为这是一个整体,或者 var n={}+1,这样也是字符串拼接...
  • 1+{} 这一定是字符串拼接了
  • 如果出现的是对象,按照 Symbol.toPrimitiv/valueOf/toString/Number 这个顺序来处理,能走到哪一步,就按照哪一步的规则处理

“+”只有一边

  • +[value] 把[value]变为数字
  • [value]+ 报错
  • ++i 先把i累加1「数学运算」,然后再输出或者运算
  • i++ 先拿i原来的值进行输出或者运算,完了之后再自己累加1
var i = 3;
console.log(5 + (++i)); // ++i i=4   5+4 9
i = 3;
console.log(5 + (i++)); // 5+3 8  i++ i=4
console.log(i); //4 

 var i = 2;
console.log(2 + (++i) - (i++) + (i--) - (--i)); //4
// 2+(++i)  ++i i=3  2+3 5
// 5-(i++)  5-i 2  i++ i=4
// 2+(i--)  2+i 6  i-- i=3
// 6-(--i)  --i i=2  6-2 4
console.log(i); //2 

/* // 阿里
// i++ i+=1 i=i+1  是否一样?
var i = '2';
i++; //数学运算中的累加1
console.log(i); //3
i = '2';
i += 1; //=> i=i+1  '2'+1
console.log(i); //'21' */
// console.log(+'10'); //10
// console.log(1 + 1); //2
// console.log(1 + '2'); //'12'

  • ![value] 先把[value]转换为布尔类型,然后取反
  • !![value] 转换为布尔类型 等价于 Boolean([value])
console.log(!1); //1->true 取反 false
console.log(![]); //false
console.log(!0); //true
console.log(!![]);//true
if (1) {
    // 隐式转换:先把1转换为布尔类型,判断真假,决定条件是否成立
}
// 大厂面试题
let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr);
// ---
parseInt(27.2, 0)//27
parseInt(0, 1)//NaN
parseInt('0013', 2)//1
parseInt('14px', 3)//1
parseInt(123, 4)//27 
*/

比较

不同数据间的相互转化.png

  • = 赋值
  • ==相等 !=不相等
  • ===绝对相等 !==绝对不相等

1.A===B 绝对相等 「推荐」

  • A/B类型一样,则直接比较值即可
  • A/B类型不一样,结果是false,不会进行数据类型转换
null===undefined  =>false
null==undefined  =>true

2. A==B 值比较

  • A/B类型一样,则直接比较值即可
console.log(1 == 1); //true
console.log('zhufeng' == 'peixun'); //false

3. A/B类型不一样,则需要先转换为相同的数据类型,然后再比较

规则:

  • 对象==字符串 对象转换为字符串
  • null==undefined 结果是true,但是null或者undefined和其它任何值都不相等
  • NaN!=NaN NaN和自己本身也不相等,和谁都不相等
  • 剩下所有两边类型不一致的情况下,都是转换为数字,然后再比较
console.log(null == undefined); //true
console.log(null == 0); //false
console.log(undefined == ""); //false
console.log([12] == "12"); //[12]->'12'  => true
console.log("10" == 10); //Number("10") -> 10   10==10 => true
console.log(false == ""); //0 == 0  => true
console.log(true == 0); //1==0 => false
console.log(true == 2); //1==2 => false

原始值类型和对象类型的区别

原始值类型,结构比较简单,直接存储到栈内存中即可,后续变量都是直接关联和操作这个值的“按值操作”「所以它也叫:值类型/基本数据类型」

var a = 12;
var b = a;
b = 13;
console.log(a); //12

栈内存.png 对象类型,结构比较复杂,不能直接存储到栈内存中,需要在堆内存中单独开辟一个空间,来存储对象的键值对,而变量关联和操作的都是:堆内存空间的引用地址{16进制}“按引用地址操作”「所以它也叫:引用数据类型」

var a = {
    n: 12
};
var b = a;
b['n'] = 13;
console.log(a.n);

堆内存.png