时间:2022-10-18 11:53:38 | 栏目:JavaScript代码 | 点击:次
首先介绍一下JavaScript中的八大基本类型
除对象类型(object)以外的其它任何类型定义的不可变的值(值本身无法被改变)。例如(与 C 语言不同),JavaScript 中字符串是不可变的(译注:如,JavaScript 中对字符串的操作一定返回了一个新字符串,原始字符串并没有被改变)。我们称这些类型的值为“原始值”。 —— MDN
(1)布尔类型(Boolean)
布尔表示一个逻辑实体,可以有两个值:true
和false
、
(2)Null类型
null
(3)Undefined类型
undefined
是全局对象的一个属性。也就是说,它是全局作用域的一个变量。undefined
的最初值就是原始数据类型undefined。console.log(undefined in window) // true let a; console.log(a) // undefined
function f() { } console.log(f()) // undefined
(4)数字类型(Number)
Infinity
(或者+Infinity)
-Infinity
// 注意:Number.MIN_VALUE(5e-324)— Number.MAX_VALUE(1.7976931348623157e+308) 是可以表示的正值范围 console.log(Number.MIN_VALUE); // 5e-324 console.log(Number.MAX_VALUE); // 1.7976931348623157e+308 console.log(1e310); // Infinity console.log(-1e310); // -Infinity console.log(1e-330); // 0 console.log(-1e-330); // 0
NaN
(非数值,Not-a-Number)。not-a-number:
NaN
是一个全局对象的属性。NaN
属性的初始值就是 NaN,和Number.NaN
的值一样。
(5)BigInt
// 数字类型安全整数限制 console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991 console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
n
或调用构造函数来创建的。// 构造函数创建 // BigInt() 不与 new 运算符一起使用。 let a = BigInt(1); // 末尾附加字母n创建 let b = 1n; console.log(a) // 1n console.log(b) // 2n
BigInt
不能与数字相互运算。否则,将抛出TypeError
。console.log(1 + 1n); //Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
(6)字符串类型(String)
JavaScript 的字符串类型用于表示文本数据。它是一组 16 位的无符号整数值的“元素”。在字符串中的每个元素占据了字符串的位置。第一个元素的索引为0
,下一个是索引1
,依此类推。字符串的长度是它的元素的数量。
(7)符号类型(Symbols)
for( ... in ...)
” 中作为成员出现,也因为这个属性是匿名的,它同样不会出现在 “Object.getOwnPropertyNames()
” 的返回数组里。这个属性可以通过创建时的原始 symbol 值访问到,或者通过遍历 “Object.getOwnPropertySymbols()
” 返回的数组。在之前的代码示例中,通过保存在变量 myPrivateMethod
的值可以访问到对象属性。对象类型(Object)
在计算机科学中, 对象(object)是指内存中的可以被标识符引用的一块区域。
在 JavaScript 中,对象可以被看作是一组属性的集合。用对象字面量语法来定义一个对象时,会自动初始化一组属性。而后,这些属性还可以被添加和移除。
对象拥有两种属性:
a、数据属性
b、访问器属性
JavaScript中提供了很多内置对象,可参考MDN官方文档。
(1)原始数据类型:
基础类型存储在栈内存,被引用或拷贝时,会创建一个完全相等的变量;占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
(2)引用数据类型:
引用类型存储在堆内存,存储的是地址,多个引用指向同一个地址,这里会涉及一个“共享”的概念;占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
所以,在 JavaScript 中,原始类型的赋值会完整复制变量值,而引用类型的赋值是复制引用地址。
typeof
操作符返回一个字符串,表示未经计算的操作数的类型。
语法:
// operand:一个表示对象或原始值的表达式,其类型将被返回 typeof operand typeof(operand)
返回值:
"undefined" "object" "boolean" "number" "bigint" "string" "symbol" "function" "object"
(1)不同数据类型的 typeof 值
// 历史遗留问题 console.log(typeof null); // object // Undefined 类型typeof值为 undefined console.log(typeof undefined); // undefined // 未定义的变量 typeof值也为 undefined console.log(typeof a); // undefined // 非数值 console.log(typeof NaN); // number // 无穷大 console.log(typeof Infinity); // number // 除 Null 和 Undefined 之外的原始数据类型 typeof 值都是对应的类型 console.log(typeof true); // boolean console.log(typeof 2); // number console.log(typeof 2n); // bigint console.log(typeof BigInt(1)); // bigint console.log(typeof 'str'); // string console.log(typeof Symbol(1)); // symbol console.log(typeof Boolean(true)); // boolean console.log(typeof Number(1)); // number console.log(typeof String('str')); // string // 基本包装类型 console.log(typeof new Boolean(true)); // object console.log(typeof new Number(1)); // object console.log(typeof new String('str')); // object // 字面量 console.log(typeof {}); // object console.log(typeof []); // object console.log(typeof /1/); // object // 除了 Function 对象实例, typeof值都是 object console.log(typeof new Object()); // object console.log(typeof new Array()); // object console.log(typeof new Map()); // object console.log(typeof new Set()); // object console.log(typeof new Date()); // object console.log(typeof new RegExp()); // object console.log(typeof Math); // object // Function 对象实例, typeof值为 function console.log(typeof new Function()); // function console.log(typeof Function); // function console.log(typeof Array); // function console.log(typeof Set); // function console.log(typeof Date); // function console.log(typeof RegExp); // function console.log(typeof Object); // function
(2) typeof null === 'object' 的原因
“typeof null”错误是 JavaScript 第一版的遗留物。在这个版本中,值存储在 32 位单元中,由一个小型标签(1-3 位)和值的实际数据组成。类型标签存储在单元的低位中。
其中有五个:
也就是说,最低位是任一位,那么类型标签只有一位长。或者为零,则类型标签的长度为三位,为四种类型提供两个额外的位。
有两个值是特殊的:
所以 typeof 认为 null 是一个 object
(3)对象类型 typeof 返回值的理解
针对对象类型,个人理解 的是:
(4)关于 typeof Boolean(true) 和 typeof new Boolean(true)不同的原因
// 简单调用Boolean() 函数 console.log(Boolean(true)); // true // 创建一个Boolean对象 console.log(new Boolean(true)); // Boolean {true}
Boolean(true) 的返回值就是Boolean类型的数据,newBoolean(true)返回的是一个Boolean对象。
(5)总结
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上
语法:
// object:某个实例对象 // constructor:某个构造函数 object instanceof constructor
(1)相关例子
// instanceof 右边是一个构造函数,所以undefined 和 null 不能使用instanceof,下面两条语句执行会报错 // console.log(undefined instanceof Undefined); // console.log(null instanceof Null); // 对于原始数据类型无法检测 console.log(2 instanceof Number); // false console.log(1n instanceof BigInt); // false console.log(true instanceof Boolean); // false console.log('str' instanceof String); // false console.log(Symbol(1) instanceof Symbol); // false // 可以检测内置对象类型 console.log([] instanceof Array); // true console.log(function(){} instanceof Function); // true console.log({} instanceof Object); // true console.log(/1/ instanceof RegExp); // true // 所有函数对象实例原型链上都存在Function console.log(Array instanceof Function); // true console.log(Set instanceof Function); // true console.log(Map instanceof Function); // true console.log(Date instanceof Function); // true console.log(RegExp instanceof Function); // true console.log(Function instanceof Function); // true console.log(Object instanceof Function); // true // Object 是所有实例对象原型链的尽头 console.log(Array instanceof Object); // true console.log(Set instanceof Object); // true console.log(Map instanceof Object); // true console.log(Date instanceof Object); // true console.log(RegExp instanceof Object); // true console.log(Function instanceof Object); // true console.log(Object instanceof Object); // true function foo() {} // foo 是 Function的一个实例 console.log(foo instanceof Object); // true console.log(foo instanceof Function); // true // new foo() 是 foo 的一个实例 console.log(new foo() instanceof foo); // true console.log(new foo() instanceof Object); // true console.log(new foo() instanceof Function); // false // foo() 返回的是undefined console.log(foo() instanceof Object); // false console.log(foo() instanceof Function); // false
(2) 当构造函数的proptotype发生改变时,instanceof 结果可能会出错
function foo() {} let f = new foo(); console.log(f instanceof foo) // true // foo 原型发生改变 foo.prototype = Array.prototype; console.log(f instanceof foo); // false
(3)手动实现 instanceof
function myInstanceof(left, right) { // 获得构造函数的原型 let prototype = right.prototype // 获得对象的原型 left = left.__proto__ // 判断对象的类型是否等于类型的原型 while (true) { // 原型链的尽头是 null,说明实例对象的原型链遍历结束 if (left === null) return false if (prototype === left) return true left = left.__proto__ } }
上述手动实现只是实现了基本功能,但与原生instanceof仍然存在差别,例如:
// 检验right 是否是一个对象(Object) if (right is not Object){ throw new TypeError(" Uncaught TypeError: Right-hand side of 'instanceof' is not Object") } // 检验 right 是否可被调用 if (right is not callable) { throw new TypeError(" Uncaught TypeError: Right-hand side of 'instanceof' is not callable") }
console.log(_instanceof('str', String)) // true
有关instanceof 原理,可继续深入了解原型链相关知识点。
(4)总结
例如:
console.log([] instanceof Array); // true console.log([] instanceof Object); // true
所以 instanceof 用于判断数据类型也存在弊端。
(1)相关例子
// null 和 undefined 没有构造函数 onsole.log(null.constructor) // TypeError onsole.log(undefined.constructor) // TypeError // 原始数据类型 console.log((2).constructor === Number); // true console.log((2n).constructor === BigInt); // true console.log((true).constructor === Boolean); // true console.log(('str').constructor === String); // true console.log(Symbol(1).constructor === Symbol); // true // 字面量 console.log(([]).constructor === Array); // true console.log((/1/).constructor === RegExp); // true console.log((function() {}).constructor === Function); // true console.log(({}).constructor === Object); // true // JavaScript中的内置函数对象的构造函数为 Function console.log(Array.constructor === Function); // true console.log(String.constructor === Function); // true console.log(Function.constructor === Function); // true console.log(Object.constructor === Function); // true // JavaScript中的内置普通对象的构造函数为 Object console.log(Math.constructor === Object); // true console.log(JSON.constructor === Object); // true console.log(Atomics.constructor === Object); // true console.log(Intl.constructor === Object); // true console.log(Reflect.constructor === Object); // true console.log(WebAssembly.constructor === Object); // true
(2)如果创建一个对象前修改构造函数的原型,会导致constructor不可靠
function Fn(){}; Fn.prototype=new Array(); var f=new Fn(); console.log(f.constructor===Fn); // false console.log(f.constructor===Array); // true
(3)总结
所以 constructor 判断数据类型也存在弊端。
(1)Object.prototype.toString()
每个对象都有一个
toString()
方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()
方法被每个Object
对象继承。如果此方法在自定义对象中未被覆盖,toString()
返回 "[object type]" ,其中type
是对象的类型 —— MDN
(2) Object.prototype.toString.call() 实例
// 原始数据类型 console.log(Object.prototype.toString.call(undefined)) // [object Undefined] console.log(Object.prototype.toString.call(null)) // [object Null] console.log(Object.prototype.toString.call(1)) // [object Number] console.log(Object.prototype.toString.call(1n)) // [object BigInt] console.log(Object.prototype.toString.call(true)) // [object Boolean] console.log(Object.prototype.toString.call('str')) // [object String] console.log(Object.prototype.toString.call(Symbol(1))) // [object Symbol] // 字面量 console.log(Object.prototype.toString.call({})) // [object object] console.log(Object.prototype.toString.call([])) // [object Array] console.log(Object.prototype.toString.call(/1/)) // [object RegExp] console.log(Object.prototype.toString.call(NaN)) // [object Number] console.log(Object.prototype.toString.call(Infinity)) // [object Number] console.log(Object.prototype.toString.call(globalThis)) // [object Window] // 内置函数对象实例 console.log(Object.prototype.toString.call(new Array())) // [object Array] console.log(Object.prototype.toString.call(new Map())) // [object Map] console.log(Object.prototype.toString.call(new Set())) // [object Set] console.log(Object.prototype.toString.call(new Date())) // [object Date] console.log(Object.prototype.toString.call(new Function())) // [object Function] // 内置普通对象本身 console.log(Object.prototype.toString.call(Math)) // [object Math] console.log(Object.prototype.toString.call(JSON)) // [object JSON] console.log(Object.prototype.toString.call(Reflect)) // [object Reflect] // 内置函数对象本身 console.log(Object.prototype.toString.call(Array)) // [object Function] console.log(Object.prototype.toString.call(Map)) // [object Function] console.log(Object.prototype.toString.call(Function)) // [object Function] console.log(Object.prototype.toString.call(Object)) // [object Function] // 浏览器全局对象 console.log(Object.prototype.toString.call(document)) // [object HTMLDocument] console.log(Object.prototype.toString.call(window)) // [object Window] // 自定义构造函数 function Foo() {} f = new Foo(); console.log(Object.prototype.toString.call(f)) // [object object] console.log(Object.prototype.toString.call(Foo)) // [object Function]
(3)基于Object.prototype.toString.call()实现数据类型判断
function getType(obj){ let type = typeof obj; if (type !== "object") { // 先进行typeof判断,如果是基础数据类型,直接返回 return type; } // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果 return Object.prototype.toString.call(obj).replace(/^[object (\S+)]$/, '$1'); // 注意正则中间有个空格 }
(4)总结
Object.prototype.toString.call() 能准确判断数据类型。