数据类型是我们学习JavaScript时最先接触的东西,它是JavaScript中最基础的知识,这些知识看似简单,但实则有着许多初学者甚至是部分学习了多年JavaScript的老手所不了解的知识。

数据类型

ECSMAScript标准中规定了7种数据类型,这7种数据类型又分为基本型和引用类型。

ES10(ES2019)中新增了一种基本类型BigInt,可以用来表示超出number安全范围的任意精度整数。

引用类型

  • Object对象:包含对象、数组、函数等。

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

存放位置不同

内存空间被分为两种:栈内存和堆内存。

栈内存

  • 存储的值大小固定
  • 空间较小
  • 可以直接操作,效率高

堆内存

  • 存储的值大小不确定,可以动态调整
  • 空间较大,运行效率低
  • 无法直接操作其内部,使用引用地址读取

基本类型就属于较为简单的数据,且会被频繁使用,因此通常存放在栈中。

var a = 10;
var b = 'hello';
var c = true;

引用类型则是同时保存在栈和堆当中:引用类型的实际值存储在堆当中,同时它会在栈中存储一个指向堆内存中的值的地址。

var a = 10;
var obj1 = { name: 'nihao' };
var obj2 = function () {
// do something
}

基本类型具有不可变性

var name = 'hello';
name.toUpperCase(); // "HELLO"
console.log(name); // "hello"

由以上代码我们会发现,如果不使用name变量本身去接收toUpperCase()的返回值,那么name的值不会被改变。

由于栈中的内存空间大小固定,所以存储在栈中的变量就是不可变的,但在使用JavaScript时我们会发现可以改变基本类型的值,例如:

var c = true;
c = false;
console.log(c); // false

这实际上是相当于在栈中开辟了一片新的空间用来存储false这个值,然后用变量c指向这个值,并非改变原本的true

引用类型就可以很轻易的改变了,它不需要使用变量本身(obj1)去再次接收新的值就可以改变,例如:

var obj1 = { name: 'nihao' };
obj1.name = 'nibuhao';
console.log(obj1); // { name: 'nibuhao' }

值比较和引用比较

对于基本类型,比较时会直接比较它们的值,相等返回true

var str1 = 'Hello';
var str2 = 'Hello';
console.log(str1 === str2); // true

对于引用类型,比较时会比较它们的引用地址,哪怕两个变量具有同名属性,且同名属性的值相同,但是因为存储位置不同,两者仍然不相等

var obj1 = { name: 'obj' };
var obj2 = { name: 'obj' };
console.log(obj1 === obj2); // false

赋值

与上面的两种比较类似,基本类型赋值时是直接将值给另一个变量,而引用类型则是将地址给另一个变量

var str1 = 'Hello';
var str2 = str1;
str2 = 'World';
console.log(str1); // "Hello"
//str1的值没变
var obj1 = { name: 'obj1' };
var obj2 = obj1;
obj2.name = 'obj2';
console.log(obj1.name); // "obj2"
// obj1的值改变

null与undefined

  • null表示空值
  • undefined表示“缺少值”,即此处应该有值,但是还未定义
var a = null;
console.log(typeof a); // object
console.log(typeof b); // undefined

如果学过C#Java之类的静态类型语言就会知道,直接使用未声明的变量会直接报错,而JavaScript是动态类型语言,成员除了存在空值外,还有可能根本就不存在(因为只有在运行时才会知道是否存在)。

Symbol类型

symbol变量需要使用Symbol()创建

var s = Symbol(); // 注意没有new

Symbol()函数接受一个可选参数,用来描述即将创建的symbol变量,无论传入的描述是否相同,最后生成的symbol一定是独一无二的

var name1 = Symbol('Tom');
var name2 = Symbol('Tom');
console.log(name1 === name2); // false

如果一定要创建两个一模一样的symbol,需要使用Symbol.for()

var name1 = Symbol.for('Tom');
var name2 = Symbol.for('Tom');
console.log(name1 === name2); // true

Symbol类型可以用作对象属性,使用该类型可以保证对象不会出现同名属性

var obj = {
[Symbol('name')]: 'Tom'
};

使用Symbol类型作为对象的属性名时,是无法是用for ... inObject.getOwnPropertyNamesObject.keys()获取到该属性的,可以调用用来专门获取Symbol的方法Object.getOwnPropertySymbols()来获取

var obj = {
[Symbol('name')]: 'Tom'
};
for (var key in obj) {
console.log(key); // undefined
}
Object.getOwnPropertySymbols(obj); // [Symbol(name)]

数据类型转换

图片来源:https://juejin.im/post/5cec1bcff265da1b8f1aa08f#heading-23

宽松等号(==)的隐式转换

使用==时,如果等号两侧的数据类型相同,那么比较结果与===相同,否则会发生隐式转换

NaN

NaN和任何类型比较都会返回false,包括他自己

NaN == NaN // false

Boolean类型与其他类型进行比较

只要Boolean类型参与比较,该Boolean类型的值都会被转换为Number类型,1转为true0转为false

false == 0 // true
true == 1 // true
true == 2 // false

如果在使用if判断时,我们使用数字作为判断条件

var x = 10;
if (x) {
// ...
}
if (x == true) {
// ...
}

我们会发现,第一个判断结果为true,而第二个的结果为false,这就是因为true被转换为了1,判断条件变为了x == 1

Number类型和String类型进行比较

这两者进行比较时,String类型会被转为Number类型,除了纯数字字符串正常转换为Number类型外,空字符串''转为0,科学计数法(例如1e11)正常转换,Infinity正常转换,其他全部转换为NaN

'' == 0 // true
'123' == 123 // true
'1e11' == 1e11 // true
Infinity == 'Infinity' // true
true == '1' // true
false == '0' // true

null与undefined

null == undefined结果为true外,其他任何类型和nullundefined比较都为false

基本类型与引用类型比较

ToPrimitive规则

首先我们要先了解ToPrimitive规则,即引用类型转为基本类型

  • 当引用类型需要被转为基本类型时,它会先查找对象的valueOf方法,如果该方法返回基本类型的值,则ToPrimitive的结果就是这个值
  • 如果valueOf不存在或者valueOf方法返回的不是基本类型,就会尝试调用对象的toString方法,然后使用toString的返回值作为ToPrimitive的结果
  • valueOftoString都不存在,或者没有返回基本类型,则抛出TypeError异常

对于不同的引用类型,该过程会有些许不同,比如Date会先调用toString

引用类型转换为不同的基本类型也会有一些不同,比如:

  • 引用类型转换为Number类型,先调用valueOf,再调用toString
  • 引用类型转换为String类型,先调用toString,再调用valueOf

具体请参考ECMA标准

Number([]); // 0
Number([10]); // 10 var obj = {
valueOf: () => { return 10; },
toString: () => { return -10; }
};
Number(obj); // 10
String(obj); // -10

基本类型与引用类型比较

比较时,引用类型会依据ToPrimitive规则转换为基本类型

var a = {};
var b = [1, 2, 3]; a == '[object Object]'; // true
a.toString(); // [object Object]
b == '1,2,3' // true
b.toString(); // "1,2,3"

判断数据类型

typeof

typeof只能用来判断以下几个类型

typeof 'str';  // string
typeof 123; // number
typeof true; // boolean
typeof Symbol(); // symbol
typeof undefined; // undefined
typeof function () {} // function

对于引用类型(数组、对象等)以及nulltypeof的返回值均为object

instanceof

instanceof可以判断引用类型的具体类型

[] instanceof Array; // true
/1/ instanceof RegExp; // true
new Date() instanceof Date; // true

但是,instanceof同样没法判断null

null instanceof null; // Uncaught TypeError: Right-hand side of 'instanceof' is not an object
null instanceof Object; // false

MDN中,instanceof被这样描述:

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

也就是说instanceof设计的初衷并不是用来检测数据类型的,因此很多类型它也无法判断

Object.prototype.toString.call()

Object.prototype.toString.call()利用call来改变this的指向,可以让该方法能够获取到任意变量的[[class]]属性,通过该属性可以判断所有JavaScript的内置类型

Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call(undefined); // [object Undefined]
Object.prototype.toString.call(123); // [object Number]
Object.prototype.toString.call(new Date()); // [object Date]
// ...

但是该方法并不能判断自定义类型,而instanceof可以实现对自定义类型的判断

function Person() {}
Object.prototype.toString.call(new Person()); // [object Object]
new Person() instanceof Person; // true

参考资料

深入学习JavaScript数据类型的更多相关文章

  1. JavaScript学习总结(2)——JavaScript数据类型判断

    最近做项目中遇到了一些关于javascript数据类型的判断处理,上网找了一下资料,并且亲自验证了各种数据类型的判断,在此做一个总结吧! 一.JS中的数据类型  1.数值型(Number):包括整数. ...

  2. JavaScript学习笔记——数据类型强制转换和隐式转换

    javascript数据类型强制转换 一.转换为数值类型 Number(参数) 把任何的类型转换为数值类型 A.如果是布尔值,false为0,true为1 B.如果是数字,转换成为本身.将无意义的后导 ...

  3. 2019-9-24:渗透测试,JavaScript数据类型基础学习

    JavaScript 数据类型 值类型(基本类型):字符串(String).数字(Number).布尔(Boolean).对空(Null).未定义(Undefined).Symbol. 引用数据类型: ...

  4. 《如何正确学习JavaScript》读后小结

    在segmentfault上读的一篇学习JavaScript路线的文章,做个小结. 一.简介.数据类型.表达式和操作符 (1)<JavaScript权威指南>前言1-2章&< ...

  5. 一步步学习javascript基础篇(3):Object、Function等引用类型

    我们在<一步步学习javascript基础篇(1):基本概念>中简单的介绍了五种基本数据类型Undefined.Null.Boolean.Number和String.今天我们主要介绍下复杂 ...

  6. 一步步学习javascript基础篇(1):基本概念

    一.数据类型 数据类型 基本数据类型(五种) Undefined Null Boolean Number String 复杂数据类型(一种) Object Undefined:只有一个值undefin ...

  7. Javascript:Javascript数据类型详解

    要成为一个优秀的前端工程师,系统的学习Javascript,有夯实的Javascript基础,以及对语言本身的深刻的理解,是基本功.从Javascript数据类型开始,我将对Javascript知识体 ...

  8. 如何正确学习JavaScript

    不要这样学习JavaScript 不要一开始就埋头在成堆的JavaScript在线教程里 ,这是最糟糕的学习方法.或许在看过无数个教程后会有点成效,但这样不分层次结构地学习一个东西实在是十分低效,在实 ...

  9. 「译」如何正确学习JavaScript

    原文:How to Learn JavaScript Properly 目录 不要这样学习JavaScript 本课程资源 1-2周(简介,数据类型,表达式和操作符) 3~4周(对象,数组,函数,DO ...

随机推荐

  1. 阻塞队列一——java中的阻塞队列

    目录 阻塞队列简介:介绍阻塞队列的特性与应用场景 java中的阻塞队列:介绍java中实现的供开发者使用的阻塞队列 BlockQueue中方法:介绍阻塞队列的API接口 阻塞队列的实现原理:具体的例子 ...

  2. 字符串相同ID竟然不同!!!

  3. redis的持久化(RDB与AOF)

    1.为什么redis要实现持久化? 避免因宕机.断电等场景导致进程退出后数据丢失,如果redis的数据都只存放于内存,那么进程退出后数据就丢失了.持久化机制可以持久化内存数据到硬盘,重启redis后基 ...

  4. 记录一次更改服务器名称导致mysql 不能正常登录、启动

    由于客户要求更改服务器的名称,以便区分多台服务器:修改前mysql 能正常登录,但是修改后,登录时报错: Enter password: ERROR 1524 (HY000): Plugin '*C6 ...

  5. 第m大的身份证号码(局部排序代全局、结构体排序)

    第m大的身份证号码(点击) 时间限制: 1 Sec  内存限制: 128 MB                                                             ...

  6. 快捷键浏览存储过程的内容(执行文中的User Store Proc,设置快捷方式的指向usp_Name)

    虽然不常用,但使用时还是在用传统方法查看存储过程的内容, sp_helptext usp_Name;下面这段执行后可设置快捷查询usp的内容(但是需要在每个数据库下都存在,不能直接在master,并且 ...

  7. linux网络编程-socket(37)

    在编程的时候需要加上对应pthread开头的头文件,gcc编译的时候需要加了-lpthread选项 第三个参数是线程的入口参数,函数的参数是void*,返回值是void*,第四个参数传递给线程函数的参 ...

  8. Java的前生今世

    Java作为一门编程语言,自诞生以来已经流行了20多年,在学习它之前,我们有必要先了解一下它的历史,了解它是如何一步步发展到今天这个样子. 孕育 上世纪90年代,硬件领域出现了单片式计算机系统,比如电 ...

  9. activiti学习笔记一

    activiti学习笔记 在讲activiti之前我们必须先了解一下什么是工作流,什么是工作流引擎. 在我们的日常工作中,我们会碰到很多流程化的东西,什么是流程化呢,其实通俗来讲就是有一系列固定的步骤 ...

  10. HTML5实现DTMF(电话拨号按键信号)解码、编码,代码简单易于移植

    目录 一.前言 1.1 HTML5实现DTMF的一些动机 1.2 一些有效场景 (1) 10086 (2) 软电话 (3) 小玩具 二.DTMF频率按键对照表 三.DTMF信号解码 得到按键值 3.1 ...