JavaScript 的数据类型
基本区分方法
ECMAScript 标准定义了 7 种数据类型
6 种 基本类型:
Boolean, 两种取值: true 和 false
Null, 一种取值: null
Undefined, 一种取值: undefined
Number,JS 的数值为基于 IEEE 754 标准的双精度 64 位二进制格式的值 (-(263 -1) 到 263 -1)
String,JavaScript 的字符串类型用于表示文本数据它是一组 16 位的无符号整数值的元素它不可改变
Symbol, 符号是唯一的并且是不可修改的, 并且也可以用来作为 Object 的 key
一种复杂类型:
Object, 可以认为 Object 是一种键值对的集合 Array 和 Function 就是 Object 的子类型
另一种区分方法: 值和址
从 C 语言过来的朋友一定常听到传值和传址这样的说法, C 语言里有指针的概念, 指针本质上是一个内存地址, 程序员可以通过指针来修改某些内容
在 java 和 JavaScript 这样的类 C 语言中虽然没有了指针这么强大却危险的东西, 但是在函数中操作变量的方式却一脉而成
举个例子:
- var
- globalA = 1
- ;
- function foo (a) {
- a = 2;
- }
- foo(globalA);
- console.log(globalA);//1
在函数中, 如果入参是值类型, 那么函数将会在执行时上下文建立一个副本, 在函数中, 实际修改的这个副本, 不会影响真正的原始入参
如果入参是值类型, 也就是我们常说的引用类型, 那么将直接操作所引用的对象, 也就是所说的, 通过地址操作值
- var
- obj = {a:1}
- ;
- function foo2 (o) {
- o.a = 2;
- }
- ArrFoo(obj);
- console.log(obj);//{a:2}
此处需要注意的一点是, 函数的形参 o 虽然是引用类型, 但是它也是一个执行上下文中建立的副本, 如果直接将它重新赋值, 例如
o = {a:2};
这种写法是不能影响到原始的入参的, 执行完毕以后, 原始的 obj 不会被修改
上面提到的是我们不论在 C 还是 Java 中也会涉及的一些传址传值的基本概念
但是在 JavaScript 中有一些特别的地方
每个函数都可能是构造函数
值和址一般是在入参里做体现, 出参方面, 按照正常理解即可由于 JavaScript 的原型链特点, 每个函数都可能是构造函数在构造函数中, 情况稍有不同
构造函数中带有 return 语句, 如果 return 的是:
值类型, 那么构造函数会忽略掉这个值, 返回构造的新对象;
引用类型 (数组函数对象), 那么构造函数就会直接返回该引用类型;
- // 因为 Super1 返回的 123 是值类型, 它被丢弃, 直接返回构造对象
- function Super1(a) {
- this.a = a;
- return 123; // 将 return 语句注释掉也没影响
- }
- Super1.prototype.sayHello = function() {
- console.log("Hello")
- }
- console.log(new Super1(1));
- // Super2 直接返回了对象, 而前面构造函数的所有操作全被丢弃
- // 包括它的构造函数原型链全部都没有返回
- function Super2(a) {
- this.a = a;
- return {
- a: 2
- };
- }
- Super2.prototype.sayHello = function() {
- console.log("Hello")
- }
- console.log(new Super2(3));
换言之, 如果是 return 的是值类型, return 则没什么作用; 如果是引用类型, 则对构造对象的任何操作不生效, 直接返回原来的引用对象
不靠谱的 typeof
其实 js 有一个自带的操作符专门用来进行类型判定 typeof, 不过它只能判断几种基本类型对于复杂类型, 它有些无能无力, 而且, 对于一些基本类型, 它还有一些陷阱
类型 | 结果
- | -:
- Undefined | "undefined"
- Null | "object"
- Boolean | "boolean"
- Number | "number"
- String | "string"
- Symbol | "symbol"
- function | "function"
任何其他对象 (Array,Date 等原生对象) | "object"
typeof 有一些古怪的 bug
即使是在 typeof 可以判定的地方, 也会有一些 bug 例如:
最经典的
typeof null === 'object' // true
以及 new 操作符
- typeof Number(1) === 'number' // true
- typeof new Number(1) === 'object' // true
由于 typeof 的功能简陋, 尤其是无法对 object 下的子类型做出详细的判定, 所以我们常用另一个操作符进行判定
稍微靠谱的 instanceof
instanceof 用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性
大概原理就是不停地去判断当前对象 _ _ proto _ _ 上的对象是否与实例的 prototype 相等不相等的话, 就令当前对象
a. _ _ proto _ _ = a. _ _ proto _ _ . _ _ proto _ _ 继续判断直到结果为 null 和 true 为止
举个栗子:
- // 构造函数
- function Foo(){}
- var
- foo1 = new Foo()
- ;
- foo1.constructor === Foo; //true
- foo1.__proto__.constructor === Foo; // true;
- foo1.__proto__ === Foo.prototype; // true;
- foo1.__proto__.__proto__ === Object.prototype; // true
- foo1.__proto__.__proto__.__proto__ === null; // true
上面的代码展示了 instanceof 的判断原理, 具体实现可以参考规范:
- https://tc39.github.io/ecma262/#sec-instanceofoperator
- https://tc39.github.io/ecma262/#sec-ordinaryhasinstance
根据 instanceof, 我们基本上可以判断 Object 下的所有对象类型了但是 instanceof 也有一个问题
一切都在 window 下
在浏览器环境里, 所有的构造函数, 基本的对象, 都挂在 window 下 Object 也不例外这导致了一个问题, 在 iframe 这样的独立于当前窗口的环境里, instanceof 可能会有 bug 产生
对于 Array 这样的常用对象, 新版本提供了 Array.isArray 原生方法进行判定, 但是对于 Date 等对象, 就没有这么好了所以我们还是需要一个更加健壮的类型判定方法
Prototype.jsunderScore.js 和 jQuery 的前辈们为我们找到了一个绝妙的方法
Object.prototype.toString.call
通过这个技巧, 再结合 typeof 和 instanceof, 我们可以判断几乎全部的原生对象类型
下面是 underscore 的部分源码:
- _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'],
- function(name) {
- _['is' + name] = function(obj) {
- return toString.call(obj) === '[object' + name + ']';
- };
- });
- REFER
- typeof
- instanceof
- underscore.js
来源: https://www.cnblogs.com/liuyongjia/p/8452242.html