一、基本类型 vs 引用类型
1. 基本类型都有哪些
JavaScript 一共有5+2种基本数据类型,分别是:
- undefined (未定义)
- null (空)
- Boolean (布尔)
- Number (数字)
- String (字符串)
- Symbol (符号:独一无二的值) ES6新增
- BigInt(任意精度格式整数)ES11新增
知识点扩充
- null 表示空指针对象。
- undefined表示为未定义,当使用var或let声明了变量但是没有初始化时,就相当于给变量赋予了undefined。JS是一门动态类型语言,成员除了表示存在的空值外,还有可能根本就不存在(因为存不存在只在运行时才知道),这就是undefined的意义所在。
- Boolean表示布尔值,true和false。
- Number表示整数或浮点数,还有一些特殊值(-Infinity负无穷、+Infinity正无穷、NaN没有数值)。
- String表示字符串。
- Symbol代表创建后独一无二且不可变的数据类型,它的出现主要是为了解决可能出现的全局变量冲突的问题。
- BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
2. 引用类型都有哪些
引用类型包括:
-
对象Object(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function)
-
基本类型的包装类型(Boolean、Number、String)
3. 基本类型和引用类型的区别
// 值类型
let a = 100;
let b = a;
a = 200;
console.log(b); // 100
let a = null;
let b = a; // b 复制了 a 的值(null)
a = { name: "obj" }; // 修改 a 不影响 b
console.log(b); // null(值类型独立)
// 引用类型
let a = {
age: 200,
name: 'nini',
};
let b = a;
b.age = 21;
console.log(a.age); // 21
-
值类型存在栈中,引用类型存在堆中。当把a赋值给b时:
- 值类型的赋值操作:a与b各自的值都存在于栈中,互不影响,互相独立。修改a不影响b。
- 引用类型的赋值操作:a与b存入栈中的都是引用内存地址,相当于是同一个东西,而数据则存在堆中,通过内存地址指向堆,因此修改a也会修改b。
-
为什么要这么设计?性能的原因。值类型占用的空间比较小,引用类型一般较大。内存的空间和CPU计算的耗时等方面考虑,将值类型与引用类型做了区分。
4, 如何判断类型
(1)typeof 操作符
作用有三个:
- 识别所有值类型
- 能判断函数
- 能识别引用类型(不能再继续识别)
// 识别所有值类型 (这里不包括null)
let a; typeof a // 'undefined'
const str = 'abc'; typeof str // 'string'
const n = 100; typeof n // 'number'
const b = true; typeof b // 'boolean'
const s = Symbol('s'); typeof s // 'symbol'
// 能识别函数
typeof function () {} // 'function'
typeof console.log // 'function'
// 能识别引用类型,但是不能继续识别下去了
typeof null // 'object' JS初版就流传下来的bug,后面由于修改会造成大量的兼容问题就一直没有被修复
typeof ['a','b'] // 'object'
typeof { x: 100 } // 'object'
(2)instanceof 操作符
- instanceof 操作符可以帮助我们判断引用类型具体是什么类型的对象
- 使用instanceof不能检测基本数据类型,因为不是对象
[] instanceof Array // true
new Date() instanceof Date // true
new RegExp() instanceof RegExp // true
(3)toString
- 通过Object原型中的toString方法来判断数据类型
var obj = {}; obj.toString() // "[object Object]"
- 上面代码调用空对象的toString方法,结果返回一个字符串[object Object],其中第二个Object表示该值的构造函数。这是一个十分有用的判断数据类型的方法。
- 由于实例对象可能会自定义toString方法,覆盖掉Object.prototype.toString方法,所以为了得到类型字符串,最好直接使用Object.prototype.toString方法。通过函数的call方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。
Object.prototype.toString.call(value)表示对value这个值调用Object.prototype.toString方法。
Object.prototype.toString.call(2) // "[object Number]"
Object.prototype.toString.call('') // "[object String]"
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(Math) // "[object Math]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
(4)手写一个很准确的类型判断函数
const typeJudge = () => {
const s = Object.prototype.toString.call(o);
return s.match(/[object (.*?)]/)[1].toLowerCase();
}
typeJudge({}); // "object"
typeJudge([]); // "array"
typeJudge(5); // "number"
typeJudge(null); // "null"
typeJudge(); // "undefined"
typeJudge(/abcd/); // "regex"
typeJudge(new Date()); // "date"
// 在上面这个typeJudge函数的基础上,还可以加上专门判断某种类型数据的方法。
['Null',
'Undefined',
'Object',
'Array',
'String',
'Number',
'Boolean',
'Function',
'RegExp'
].forEach(function (t) {
typeJudge['is' + t] = function (o) {
return typeJudge(o) === t.toLowerCase();
};
});
typeJudge.isObject({}) // true
typeJudge.isNumber(NaN) // true
typeJudge.isRegExp(/abc/) // true