JS专题之数据类型和类型检测

本文共 1750 字,读完只需 7 分钟

数据类型有哪些?

ECMAScript 的数据类型分为简单数据类型(也被称为基本数据类型,原始数据类型):

  1. Undefined
  2. Null
  3. String
  4. Number
  5. Boolean

在 ES6 中新增一个简单数据类型 Symbol,所以简单数据类型总共有 6 个,还有复杂数据类型(也叫作引用数据类型):Object。

所有 js 中所有的值最终都将是以上 7 种数据类型之一。

基本数据类型

基本数据类型是保存在的数据结构中的,是按值访问的,基本数据类型的值不会变 (值本身无法被改变) 数据之间进行的是它们的比较。
看代码:

var a,b;
a = "jay_chou";
b = a;
console.log(a);  // jay_chou
console.log(b);  // jay_chou
a = "coldplay";  // 改变 a 的值,并不影响 b 的值
console.log(a);   // coldplay
console.log(b);  // jay_chou
复制代码

引用数据类型

除了基本的数据类型外,剩下的就是引用类型,统称为 Object 类型, 细分的话,有 Object 类型,Array 类型,Date 类型, Regexp 类型,function, Math 类型;

引用数据类型的特点:

  • 引用数据类型的值保存的是对象在内存中的地址
  • 引用数据类型的值是可变的
  • 引用数据类型的比较是引用的比较
  • 另外:js 不能直接操作对象的内存空间 比方:
var a = {
    name: "jay_chou"
}

var b = a;

var c = {
    name: "jay_chou"
}

a === b; // true
a === c; // false
复制代码

一、为什么要进行类型检测?

JavaScript 是动态语言,动态语言和静态语言的区别在于:类型是在编译时检查(静态)还是在运行时检查(动态)。

基于动态类型的特点,如果不注意进行类型检测,JS 很容易在运行代码发生才发现报错。

举个列子:
函数在定义时,参数可以是任何类型,但是函数在实际运行的时候,传入的实参可能是其他同事函数的返回值,也可能是后台的返回值,假如不符合代码逻辑里要求的数据类型,那么就会报错导致 bug 影响程序运行。

因此,我们不光要做类型检测,也应该给自己的函数注释好参数类型和返回值类型,还要和后端定义好接口数据类型格式。

当比较的两个值的类型不同的时候 == 运算符会进行类型转换,但是当两个值的类型相同的时候,即使是 == 也相当于是 ===;=== 在比较两个值的时候,还会比较值的数据类型。

二、typeof 方式

typeof 的返回值总是字符串,字符串的可能值有:

  1. undefined
  2. boolean
  3. number
  4. string
  5. symbol
  6. object
  7. function

typeof 其实是一元操作符,和 + - * / 一样,不是一个函数,进行比较的时候,typeof 后面可以跟(), 也可以不跟。

undefined:

typeof undefined; // undefined

很多库因为考虑到 undefined 可能会被意外重写,用 void 0 来判断是否是 undefined。

var isUndefined = function (obj) {
	return obj === void 0;
}
复制代码

MDN 上对 void 词条的说明是:

The void operator evaluates the given expression and then returns undefined.

意思是说 void 运算符能对给定的表达式进行求值,然后返回 undefined。也就是说,void 后面你随便跟上一个表达式,返回的都是 undefined,都能完美代替 undefined。

string, number, boolean, symbol, function, object :

typeof "abc"; // string

typeof 123; // number

typeof NaN; // number

typeof true; // boolean

typeof Symbol(); // symbol

typeof function () {}; // function

typeof {}; // object

null

typeof null; // object!!!

js 中,不同的对象在底层都表示为二进制,在Javascript中二进制前三位都为 0 的话会被检测为 Object 类型,null 的二进制表示全为0,自然前三位也是 0,所以执行 typeof 时会返回 "object"。

**Array, Date, Regexp, Math: **

typeof []; // object

数组的判断不考虑兼容性的话,可以用 Array.isArray() 方法进行检测。

typeof new Date(); // object

typeof /s/g; // object

typeof Math; // object

typeof new String("foo"); // object!!!

typeof new Number(123); // object!!!

typeof new Boolean(true); // object!!!

typeof new Function(""); // function

typeof new Error(); // object

基于以上,基本类型大部分都能被准确检测并返回正确的字符串,并不是所有的类型都能被正确检测出来。所以在实际应用中,避免用基本包装类型new Number() 这种方式来初始化数据。

三、instanceof 方式

上面说到基本包装类型:new Number(), new Boolean, New String();

它们用 typeof 判断,会检测成对象。那针对基本包装类型可以用 instanceof 来判断。

instanceof 运算符可以用来检测某个构造函数的 prototype 属性是否存在于另外一个要检测对象的原型链上。

// 定义构造函数
function Person(){} 

var person1 = new Person();

// 因为 Object.getPrototypeOf(person1) === Person.prototype
person1 instanceof Person;  // true
复制代码

现在我们检测一下:

var str = new String('abc');  // 基本包装类型
var strValue = "foo";

strValue instanceof String;  // false
str instanceof String;  // true
str instanceof Object;  // true

[] instanceof Array;  // true
[] instanceof Object;  // true
复制代码

如果我们修改构造函数的原型后,这个方法也不怎么靠谱了:

var str = new String('abc');
str.__proto__ = Object.prototype;
str instanceof String;  // false !!!
str instanceof Object;  // true 
复制代码

四、toString() 方式

ECMAScript 的 Boolean 值、数字和字符串的原始值的有趣之处在于它们是伪对象,这意味着它们实际上具有属性和方法。

ECMAScript 定义所有对象都有 toString() 方法,无论它是伪对象,还是真对象。因为 String 类型属于伪对象,所以它一定有 toString() 方法。

使用 Object.prototype.toString 方法, 可以获取到变量的准确的数据类型.

Object.prototype.toString.call(1);  // '[object Number]'
Object.prototype.toString.call('1');  // '[object String]'
Object.prototype.toString.call(NaN);  // '[object Number]'
Object.prototype.toString.call(foo);  // '[object Function]'
Object.prototype.toString.call(Symbol());  // "[object Symbol]"
Object.prototype.toString.call([1,2,3]);  // '[object Array]'
Object.prototype.toString.call(undefined);  // '[object Undefined]'
Object.prototype.toString.call(null);  // '[object Null]'
Object.prototype.toString.call(true);  // '[object Boolean]'
Object.prototype.toString.call(/^s/g);  // '[object RegExp]'
Object.prototype.toString.call(Math);  // "[object Math]"
Object.prototype.toString.call(new Error());  // "[object Error]"
Object.prototype.toString.call(new Date());  // "[object Date]"
复制代码

toString 就能解决基本包装类型的检测错误和 instanceof 的检测不安全。

基于 toString 我们可以构造很多工具函数用来检测数据类型,这一块实现的方案很多,本文就按下不表。

五、应用场景

js 类型检测常见的应用场景: 应用场景:添加默认值

function foo(a, b) {
    // 方式一
    if (typeof b=== 'undefined') {
      b = 0;
    }

    // 方式二:不适用foo(10, false)这种情况
    b = b || 0;
}
foo(10);
复制代码

回调函数调用

function fn(callback) {
    //typeof callback === 'function' ? callback() : null;
    callback && callback();
}
fn(function () {
    
});
复制代码

还有一个很常见的应用场景当然是后台返回数据的类型的检测了。

总结

js 是动态语言,数据类型的检查是在运行时执行,为了避免代码莫名其妙报错,所以做好数据类型的检测很有必要。

欢迎关注我的个人公众号“谢南波”,专注分享原创文章。

掘金专栏 JavaScript 系列文章

  1. JavaScript之变量及作用域
  2. JavaScript之声明提升
  3. JavaScript之执行上下文
  4. JavaScript之变量对象
  5. JavaScript之原型与原型链
  6. JavaScript之作用域链
  7. JavaScript之闭包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值传递
  11. JavaScript之例题中彻底理解this
  12. JavaScript专题之模拟实现call和apply
  13. JavaScript专题之模拟实现bind
  14. JavaScript专题之模拟实现new
  15. JS专题之事件模型
  16. JS专题之事件循环
  17. JS专题之去抖函数
  18. JS专题之节流函数

猜你喜欢

转载自juejin.im/post/5c14d3dae51d452f602895e4
今日推荐