JS学习篇(一)—— 数据类型篇

JS学习篇(一)—— 数据类型篇

JS的有八种数据类型

七种基本类型

  • undefined
  • null
  • Boolean
  • number
  • string
  • symbol
  • bigint

一种引用类型

  • object

七种基本类型

1):undefined

定义:通俗的讲就是未定义,当数据被声明但是没有被初始化的时候就会默认为undefined
:只有一个值,就是undefined
检测: typeof 返回undefined,也就是undefined类型

eg:

var person;
console.log(person); //undefined
typeof(person) //undefined

但是要注意的是,undefined一般是变量被声明但是没有被初始化的时候默认赋的值。但是如果某个变量没有被声明就直接调用的话就会报错

//var age;
console.log(age);  //会报错,因为age没有被声明。
但是这个时候用typeof检测还是会返回undefined
typeof(age) //undefined

2):null

定义:表示一个空对象指针
:只有一个值,就是null
检测typeof 返回object,但是它是null类型!!!
返回object的原因:
历史遗留BUG,曾经的计算机是32位的计算机,存储性能各个方面和现在64位的都有差距,处于这个考虑,使用低位存储变量,然后对象的存储是000开头的。而null是全0,所以就误被解析成为object。
作用:经常用于做判断
if ( 变量!==null ){ 如果这个变量为空执行什么操作 } else { 否则执行另一个操作 }

eg:

var box=null
typeof(box)  //object

因为undefined是null派生的,所以undefined==null,但是不全等也就是undefined !== null ,因为类型不一样。

区别:

1:定义不一样:声明一个变量但是没有初始化的时候默认为undefined,但是null一般表示空指针,也就是尚未存在的对象。
2:null转化为数值的时候是0,但是undefined转化为数值的时候是NAN

3):Boolean

定义:布尔值
:两个字面量值,分别是true和false
检测: typeof 返回Boolean
注意true和false是区分大小写的,也就是说这样写True,或者是False是无用的,都不是布尔值

类型转化

数据类型
string空字符串转化为false,剩余非空字符串转化为true
number0和NaN转化为false,其余任何非0数字值转化为true
undefinedundefined转化为false
nullnull转化为false
object任何对象都被转化为true(注意null值)

4):number

定义:number类型,用来表示整数和浮点类型。
:数字值和NaN
检测: typeof 返回Namber
注意:NaN是一个特殊的值,not a Namber不是一个数值,也就是非数值。任何涉及NaN的操作返回的都是NaN,并且NaN不等于任何值,包括他自己,一般使用isNaN()函数判断是否是“不是一个数值”。就是它接收一个数值就会尝试将这个值转化成为数值,如果不能转化的话就会返回true,否则返回false。

浮点类型:

  • 数值中必须包含一个小数点,小数点后面至少有一个数字。
  • 保存浮点数需要占用的内存空间是保存整数类型的二倍,所以JS运行机制会不失时机的将其转化为整型。比如1.0就会被转化成为1
  • 对于极大值以及极小值可以使用e的表示法进行表示。
  • 0.1加0.2为什么不等于0.3???这是一个很经典的问题
    然后怀揣着好奇心,我去控制台试验了一波,然后打印出如下效果
    在这里插入图片描述
    参考完https://segmentfault.com/a/1190000012175422才得以解释这个问题。

在JS中浮点数值的最高精度是17位小数,所以当达到最高精度的时候后面的位数都将被直接舍弃。在进行运算的时候,计算机会先将他们转化成为对应的二进制,然后再进行计算。也就意味着0.1和0.2要先被转化为二进制,所以转化后在计算机内部他们并不是完整的0.1和0.2,而是有舍入误差的数,这个时候已经出现了精度缺失,再进行相加转化为对应的十进制,也就是我们看到的0.30000000000000004

看懂了0.1加0.2之后,我又有疑问了:按照这个思路来讲的话,那么0.1加0.3在转化的过程中也被舍弃掉了,为什么0.1+0.3所得的结果还是0.4呢?

是因为0.4和相加之后最后的那个结果是最接近的,他比任何其他的浮点数值都接近,所以在有的语言中直接显示0.4,而非最真实的结果

所以在编码的过程中,我们要注意不能做如下的操作:

if(a+b == 0.3){
	//如果a,b是0.1或者是0.2的话,就会导致这个的测试不通过
}

那么如何进行浮点数值的运算呢?
使用bignumber.js,他是用于任意精度十进制和非十进制算术的JavaScript库
安装npm install bignumber.js
仓库https://github.com/MikeMcl/bignumber.js
eg:
0.1+0.2
x = BigNumber(0.1) //
y = x.plus(0.2) //这个时候就会输出0.3

数值范围:

表示
最大值Number.MAX_VALUE(1.7976931348623157e+308),如果超过这个值,就会自动被定义为正无穷infinity
最小值Number.MIN_VALUE(5e-324),如果小于这个值,会被转化为负无穷-infinity

使用isFinite()函数检验某个数字值是否在这最大值和最小值之间,如果在之间返回true。否则返回false
数值转换:
JS提供了三个函数,可以将非数值转化为数值:number(),parseInt(),parseFloat()

number():转型函数,可以用于任何数据类型转换为字符类型

类型转换规则
Booleantrue转换为1,false转换为0
数字类型简单的传入和传出,NaN返回NaN
undefined返回NaN
null返回0
字符串若只是包含数字,准换为相应的十进制数字即可;如果出现十六进制格式,将其转化为对应的十进制;如果里面有有效的浮点格式,将其转化为对应的浮点数值;如果字符串为空,则返回0,;其余全部转化成为0
对象会先调用对象自身的valueOf()的方法,然后再调用上面相应的转换规则

如果字符串中含有非数字的情况,就不能使用Namber()方法来进行转换,使用下面两种方法

parseInt()
转换规则:
1:会忽略字符串前面的空格,直到找到第一个非空格的字符
2:如果第一个字符不是数字字符或者是负号,就会返回NaN
3:若是数字字符,那么会继续向下解析,直到解析完成或者碰到非数字字符
提供第二个参数,也就是转换时候的进制数

parseInt("12hahaha")     //12
parseInt(" ")  //NaN
parseInt("22.5") //22   因为小数点不是数字字符,所以遇到解析就停止了
parseInt("0xAF",16) //175
parseInt("0xAF",16) //175    这样写告诉计算机将16进制的0xAF转化为十进制,此时可以省略0x,其他进制也同样可以省略
//省略写法:
parseInt("AF",16) //175

典型题目:
为什么 [“1”, “2”, “3”].map(parseInt) 返回 [1,NaN,NaN]?
对于这段代码实际上执行的是

['1','2','3'].map((item,index)=>{
    return parseInt(item,index)
})
所以查分来解释就是
['1','2','3'].map(parseInt)parseInt('1',0);index为 0,parseInt() 会根据十进制来解析,所以结果为 1parseInt('2',1);index为 1,超出区间范围,所以结果为 NaNparseInt('3',2);index 为 2,用2进制来解析,应以 01 开头,所以结果为 NaN

parseFloat()
转换规则:
1:从最开始解析,直到解析完成或者是碰到第一个无效的浮点数字字符为止
2:十六进制始终会被解析为0

parseFloat("12hahaha")  //12
parseFloat("0xAF")     //0
parseFloat("22.5")     //22.5
parseFloat("22.5.5")     //22.5  只有第一个小数点是有效的
parseFloat("022.5.5")     //22.5  忽略前导的0

5):string

定义:字符序列,可以由双引号表示,也可以由单引号表示
检测: typeof 返回string

转换为字符串:

toString()方法:可以将其他类型的值转换为字符串
注意:1:可以转换数值,布尔值,对象,字符串值,但是无法转换undefined和null
2:转换数值的时候可以提供一个参数,表示对应的进制

10.toString() //"10"
10.toString(2) //"1010"
10.toString(8) //"12"

String()方法:可以将其他类型的值转换为字符串
转换规则:
1:如果这个值有toString()方法,那么调用该方法并返回相应结果
2:如果是null,返回 “null”
3:如果是undefined,返回“undefined”

除此之外,还有一种方式可以转换为字符串,那就是加法运算

加法规则:
1:如果有一个操作数是NaN,则返回NaN
2:infinity+infinity = infinity ,-infinity+(-infinity)=-infinity,-infinity+infinity = NaN
3:0 + 0 =0; -0 + -0 =-0 ; +0 + -0 =+0(正负0是不相等的,但是 -0 == +0 会返回true,如果要判断的话可以这样判断1/0 //infinity ;1/-0 //-infinity )
4:如果有一个是字符串:两个都是,则拼接;只有一个,则另一个转换字符串之后再拼接
5:对象,数值,布尔值调用tostring方法转字符串,然后再用字符串加法
undefined和null调用string之后在拼接

6):symbol

ES6引进的原始数据类型

定义:表示独一无二的值,最大的用法就是用来表示对象独一无二的值
检测: typeof 返回symbol

不能使用new命令,会报错,因为symbol是原始类型,不是对象,不能添加属性

let sy = Symbol("KK");  //可以接受一个字符串作为参数,也可以不加
console.log(sy);   // Symbol(KK)
typeof(sy);        // "symbol"
 
// 相同参数 Symbol() 返回的值不相等
let sy1 = Symbol("kk"); 
sy === sy1;       // false

作为对象的属性名,由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名
注意:Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 mySymbol属性,而不是 Symbol 值 mySymbol属性

var mySymbol = Symbol(); 
// 第一种写法 
var a = {}; 
a[mySymbol] = 'Hello!'; 
// 第二种写法 
var a = { [mySymbol]: 123 };
// 第三种写法 
var a = {}; 
Object.defineProperty(a, mySymbol, { value: 'Hello!' });  
//通过方括号结构和Object.defineProperty,将对象的属性名指定为一个Symbol值

// 以上写法都得到同样结果 
a[mySymbol] // "Hello!"

Symbol值作为属性名时,该属性还是公开属性,不是私有属性。Symbol作为属性名,该属性不会出现在for...in、for...of循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols方法,可以获取指定对象的所有Symbol属性名。 Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的Symbol值。

var obj = {}; 
var a = Symbol('a');
 var b = Symbol.for('b'); 
 obj[a] = 'Hello'; 
 obj[b] = 'World'; 
 var objectSymbols = Object.getOwnPropertySymbols(obj); 
 objectSymbols // [Symbol(a), Symbol(b)]

方法:
Symbol.for()

Symbol.for() 类似单例模式,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。

let yellow = Symbol("Yellow");
let yellow1 = Symbol.for("Yellow");
yellow === yellow1;      // false
 
let yellow2 = Symbol.for("Yellow");
yellow1 === yellow2;     // true

Symbol.keyFor()
Symbol.keyFor() 返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。

let yellow1 = Symbol.for("Yellow");
Symbol.keyFor(yellow1);    // "Yellow"

7):bigint

定义: BigInt是一种新的数据类型,用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库

一种引用类型

object

定义:通过执行new操作符后跟要创建的对象类型名称来创建。
检测: typeof 返回object,一般检测对象类型,但是对象他有(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function)这么多类型,如果想要正确返回是那种对象类型,一般使用instanceof检测对象

Object 的每个实例都具有下列属性和方法:

  • constructor:保存着用于创建当前对象的函数
  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例
    如:o.hasOwnProperty("name"))
  • isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型
  • propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须以字符 串形式指定。
  • toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。 toString():返回对象的字符串表示。
  • valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值 相同。

详细见之后的对象篇

基本类型和引用类型的区别

基本数据类型:按值访问,可以操作保存在变量中的值。靠传值赋值的,每赋值一次,内存中就新开出一块地方来储存这个数据,保存在栈中。

引用数据类型:内存中只有唯一的一块地方来储存这个数据,以后的所有赋值都是传址赋值。也就是说,它只是发放一些“廉价的内存指针”,来指向内存中的这个数据。对象是指内存中的可以被 标识符引用的一块区域

区别:
1:基本类型的值没有属性和方法,而引用类型的值有
2:在复制值的时候,当一个变量的值复制给另一个变量,然后改变其中一个变量,基本类型的值没有影响,而引用类型的值则会因为另一个的变化而改变,因为引用类型的值,当一个赋值给另一个的时候,他们的指针指向的是同一个对象,所以改变其中一个另一个也会改变

基本类型的值                       引用类型的值
var num1=5var obj1=new object()
var num2=num1;                  var obj2=obj1;
num1=3;                               obj1.name="zhang";
num2//5                                obj2//zhang

图解:基本类型的值(保存在栈中):在这里插入图片描述
对于基本类型值,复制变量值,会生成一个新的值,这两个是完全独立的。你可以对它们进行任何操作而不会互相影响

引用类型的值(保存在堆中)
在这里插入图片描述
复制引用类型值,复制的其实是引用,变量obj1和obj2引用的都是同一个对象,所以你对任一变量进行操作,另一个变量的值也会变化

3:检测,基本类型的值一般用typeof检测出来,而引用类型的值一般用instanceof判断
4:基本类型的赋值是简单赋值,赋值的时候会在变量对象上面创建一个新的值,然后把该值复制到为新变量分配的位置上去,引用类型的赋值是对象引用

如何判断数据类型

typeof :在判断除Object类型的对象时比较方便
typeof返回的类型都是字符串形式,能判断的类型有number,string,object,function,undefiend

alert(typeof "helloworld")    ------------------>"string"     
alert(typeof 123)             ------------------>"number"
alert(typeof [1,2,3])         ------------------>"object"
alert(typeof new Function())  ------------------>"function"
alert(typeof new Date())      ------------------>"object"
alert(typeof new RegExp())    ------------------>"object"
alert(typeof Symbol())        ------------------>"symbol"
alert(typeof true)            ------------------>"true"
alert(typeof null)            ------------------>"object"
alert(typeof undefined)       ------------------>"undefined"
alert(typeof 'undefined')     ------------------>"string"

2.instanceof:判断已知对象的引用类型
如果已经知道一个数据是对象类型,那么可以用instanceof来判断它是Array,Date,Function,String,Number,Object。
instanceof不能判断null和undefined.

[1,2,3] instanceof Array                -------->true
new Date() instanceof Date              -------->true
new Function() instanceof Function      -------->true
new Function() instanceof function      -------->false
null instanceof Object                  -------->false

这里还会经常问道能不能手动实现一下instanceof

function instanceof(left, right) {
    // 获得类型的原型
    let prototype = right.prototype
    // 获得对象的原型
    left = left.__proto__
    // 判断对象的类型是否等于类型的原型
    while (true) {
    	if (left === null)
    		return false
    	if (prototype === left)
    		return true
    	left = left.__proto__
    }
}

3.constructor

construstor能判断number,string,boolean,array,object,date,function,不能判断null和undefined

console.log(d.constructor === Array)    //true
console.log(e.constructor === Date)        //true
console.log(f.constructor === Function)    //true 

注意constructor在类继承时会出错

例如:

  function A(){};

    function B(){};

    var aObj = new A();

    console.log(aObj.constructor === A);    //true;

    console.log(aObj.constructor === B);    //false; 
     
    function C(){};

    function D(){};

    C.prototype = new D(); //C继承自D

    var cObj = new C();

    console.log(cObj.constructor === C);    //false;

    console.log(cObj.constructor === D);    //true; 

而instanceof方法不会出现该问题,对象直接继承和间接继承的都会报true:

 console.log(cObj instanceof C);            //true
 console.log(cObj instanceof D);            //true 
     

解决construtor的问题通常是让对象的constructor手动指向自己:

cObj.constructor = C;//将自己的类赋值给对象的constructor属性
console.log(cObj.constructor === C);    //true;
console.log(cObj.constructor === D);    //false; 基类不会报true了; 

4.Object.prototype.toString.call:能判断所有类型
注意:Object.prototype.toString这种方法来查找,不能直接使用toString,从原型链的角度讲,所有对象的原型链最终都指向了Object,按照JS变量查找规则,其他对象也是可以直接访问到Object的toString方法的,但是事实上,大部分对象都已经实现了自身的toString方法,这样就可能导致Object的toString被终止查找,所以我们使用call方法来强制执行Object的toString方法。

Object.prototype.toString.call(’’) ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object Window] window是全局对象global的引用

类型转换常见题

1. [] == ![]结果是什么?为什么?

在两个==的操作符中 左右两边都需要转换为数字然后进行比较。[]转换为数字为0。 ![]首先是转换为布尔值,由于[]作为一个引用类型转换为布尔值为true, 因此![]为false,进而在转换成数字,变为0。 0 == 0 ,结果为true

2.=====、以及Object.is的区别?

1:===叫做严格相等,是指:左右两边不仅值要相等,类型也要相等,例如'1'===1的结果是false,因为一边是string,另一边是number。
2:==的判断规则
在这里插入图片描述

Object.is在严格等于的基础上修复了一些特殊情况下的失误,具体来说就是+0和-0,NaN和NaN

+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

源码:

function is(x, y) {
  if (x === y) {
    //运行到1/x === 1/y的时候x和y都为0,但是1/+0 = +Infinity, 1/-0 = -Infinity, 是不一样的
    return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
    //NaN===NaN是false,这是不对的,我们在这里做一个拦截,x !== x,那么一定是 NaN, y 同理
    //两个都是NaN的时候返回true
    return x !== x && y !== y;
  }
 

3:对象转基本类型的过程

转化流程:

如果Symbol.toPrimitive()方法,优先调用再返回
调用valueOf(),如果转换为原始类型,则返回
调用toString(),如果转换为原始类型,则返回
如果都没有返回原始类型,会报错

let a = {
  valueOf() {
    return 0;
  },
  toString() {
    return '1';
  },
  [Symbol.toPrimitive]() {
    return 2;
  }
}
1 + a // => 3
'1' + a // => '12'

4. null === null? NaN == null?NaN === NaN?

null === null true
NaN == null false
NaN === NaN false
//NaN 和任何类型都不相等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值