最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • javascript数据类型检测及检测方法封装

    正文概述 掘金(让瑕疵沉淀)   2020-12-16   462

    前言

    数据类型检测在开发中有非常广泛的应用,常见的有四种方法,每种方法都有优缺点和使用场景。

    • typeof
    • instanceof & constructor
    • Object.prototype.toString.call([value])

    数据类型

    js 中数据类型分为基本类型和引用类型,基本类型有六种:

    • number
    • string
    • boolean
    • null
    • undefined
    • symbol (es6)

    引用类型包括对象object、数组array、函数function等,统称对象类型:

    • object

    typeof

    我们最常用的莫过于 typeof,typeof 是一元操作符,放在其单个操作数的前面,操作数可以是任意类型。返回值为表示操作数类型的一个字符串。

    特点

    在检测基本类型值(null除外)和函数类型值的时候很方便。

    在基本类型中,null返回了object。

    引用类型中,除了函数外(如 Array、Function、Date、RegExp、Error 等)其他返回的object。

    typeof返回值都是小写的字符串,如下。

    • undefined
    • object
    • boolean
    • number
    • string
    • object

    场景

    形参赋值默认值

    function func(n,m){
    	n === undefined ? n=0:null;
      typeof m === 'undefined' ? m=0:null;
      
      // 当 n=false 基于逻辑或和逻辑与 会有问题
      n=n||0
      
      // ES 2020 新增 空位合并运算符 ??
      //表示我们仅在第一项为 null 或 undefined 时设置默认值
      m = m ?? 0
    }
    

    instanceof

    用来检测实例是否属于某个类的运算符

    特点

    不能处理基本数据类型

    let arr =[],reg = /^$/;
    
    arr instanceof Array // true
    reg instanceof Array // false
    
    
    let n=12,m= new Number('12')  // 分别是字面量和构造函数创建
    
    n instanceof Array // false
    m instanceof Array // true
    

    不能正确处理继承中的类

    只要在当前实例原型链中(proto)出现过的类,检测结果都是true。如果修改原型链或者检测预先类都会出现一点问题。

    let arr =[];
    
    arr instanceof Object // false
    

    constructor

    特点

    在类的原型上一般都有constructor属性,存储当前类的本身,利用这一点,验证是否为所属类,从而进行类型判断。

    但是constructor的值太容易被修改了。

    let n = 12,arr=[];
    
    n.constructor === Number // true
    arr.constrctor === Array //true
    arr.constrctor === Object //false
    
    arr.constrctor = 11 // Func.prototype={} 或者修改原型链
    arr.constrctor === Array //false
    

    Obejct.prototype.toString.call()

    首先来看英文版的定义:

    当 toString 方法被调用的时候,下面的步骤会被执行:

    1. 如果 this 值是 undefined,就返回 [object Undefined]
    2. 如果 this 的值是 null,就返回 [object Null]
    3. 让 O 成为 ToObject(this) 的结果
    4. 让 class 成为 O 的内部属性 [[Class]] 的值
    5. 最后返回由 "[object " 和 class 和 "]" 三个部分组成的字符串

    特点

    调用Object原型上的方法时,方法中的this是要检测的数据类型,结果会返回一个由 "[object " 和 class 和 "]" 组成的字符串,而 class 是要判断的对象的内部属性。

    这个方法很强大,能检测出几乎所有的基本类型和引用类型,但是无法检测自定义的类。

    console.log([12,34].toString()) // "[12,34]"
    

    所以我们可以识别至少 14 种类型。

    // 以下是11种:
    var number = 1;          // [object Number]
    var string = '123';      // [object String]
    var boolean = true;      // [object Boolean]
    var und = undefined;     // [object Undefined]
    var nul = null;          // [object Null]
    var obj = {a: 1}         // [object Object]
    var array = [1, 2, 3];   // [object Array]
    var date = newDate();   // [object Date]
    var error = newError(); // [object Error]
    var reg = /a/g;          // [object RegExp]
    var func = functiona(){}; // [object Function]
    
    functioncheckType() {
        for (var i = 0; i < arguments.length; i++) {
            console.log(Object.prototype.toString.call(arguments[i]))
        }
    }
    
    checkType(number, string, boolean, und, nul, obj, array, date, error, reg, func)
    

    除了以上 11 种之外,还有:

    console.log(Object.prototype.toString.call(Math)); // [object Math]
    console.log(Object.prototype.toString.call(JSON)); // [object JSON]
    
    functiona() {
        console.log(Object.prototype.toString.call(arguments)); // [object Arguments]
    }
    a();
    

    无法检测自定义的类

    class myCat{}
    Object.prototype.toString.call(myCat) // "[object Function]"
    

    数据类型检测方法封装

    简单的封装

    function isType(obj, type) {
    	return Object.prototype.toString.call(obj).includes(type);
    }
    
    let a = true;
    console.log(isType(a, 'Boolean'));
    console.log(isType(a, 'Object'));
    

    使用高阶函数

    虽然上面的函数能实现数据类型检测,但是手动书写字符串容易造成问题,接着使用高阶函数改造。

    考虑到实际情况下并不会检测 Math 和 JSON,所以去掉这两个类型的检测。

    // 函数柯里化:函数参数只有一个
    function isType(type) {
    	return function(obj) {
    		return Object.prototype.toString.call(obj).includes(type);
    	};
    }
    
    let types = [
    	'Boolean',
    	'Number',
    	'String',
    	'Function',
    	'Array',
    	'Date',
    	'RegExp',
    	'Object',
    	'Error',
    	'Null',
    	'Undefined'
    ];
    let fns = {};
    types.forEach((type) => {
    	fns['is' + type] = isType(type);
    });
    
    console.log(fns.isBoolean(a)); 
    

    提升性能

    Object.prototype.toString.call  性能不如 typeof,但胜在可通用。如果是基本类型,就使用 typeof,引用类型就使用 toString。

    function isType(type) {
    	return function(obj) {
    		let res = typeof obj;
    		return res === 'object' || res === 'function'
    			? Object.prototype.toString.call(obj).toLowerCase().includes(type)
    			: res.includes(type);
    	};
    }
    
    let types = [
    	'boolean',
    	'number',
    	'string',
    	'function',
    	'array',
    	'date',
    	'regExp',
    	'object',
    	'error',
    	'null',
    	'undefined'
    ];
    let fns = {};
    types.forEach((type) => {
    	fns['is' + type[0].toUpperCase() + type.substr(1)] = isType(type);
    });
    
    console.log(fns.isBoolean(true));
    console.log(fns.isBoolean([])); 
    

    返回数据类型方法封装

    方法封装

    此外鉴于 typeof 的结果是小写,我也希望所有的结果都是小写。

    // 第一版
    var class2type = {};
    
    // 生成class2type映射
    "Boolean Number String Function Array Date RegExp Object Error Null Undefined".split(" ").map(function(item, index) {
        class2type["[object " + item + "]"] = item.toLowerCase();
    })
    
    function type(obj) {
        return typeof obj === "object" || typeof obj === "function" ?
            class2type[Object.prototype.toString.call(obj)] || "object" :
            typeof obj;
    }
    

    在 IE6 中,null 和 undefined 会被 Object.prototype.toString 识别成 [object Object]。

    // 第二版
    var class2type = {};
    
    // 生成class2type映射
    "Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) {
        class2type["[object " + item + "]"] = item.toLowerCase();
    })
    
    function type(obj) {
        // 一箭双雕
      if (obj == null) {
            return obj + "";
        }
        return typeof obj === "object" || typeof obj === "function" ?
            class2type[Object.prototype.toString.call(obj)] || "object" :
            typeof obj;
    }
    

    类型检测

    有了 type 函数后,我们可以对常用的判断直接封装,比如 isFunction:

    function isFunction(obj) {
        return type(obj) === "function";
    }
    

    jQuery 判断数组类型,旧版本是通过判断 Array.isArray 方法是否存在,如果存在就使用该方法,不存在就使用 type 函数。

    var isArray = Array.isArray || function( obj ) {
        return type(obj) === "array";
    }
    

    但是在 jQuery v3.0 中已经完全采用了 Array.isArray。

    其他类型检测

    plainObject

    plainObject 来自于 jQuery,可以翻译成纯粹的对象,所谓"纯粹的对象",就是该对象是通过 "{}" 或 "new Object" 创建的,该对象含有零个或者多个键值对。除了 {} 和 new Object 创建的之外,jQuery 认为一个没有原型的对象也是一个纯粹的对象。

    实际上随着 jQuery 版本的提升,isPlainObject 的实现也在变化,我们今天讲的是 3.0 版本下的 isPlainObject,我们直接看源码。

    // 上节中写 type 函数时,用来存放 toString 映射结果的对象
    var class2type = {};
    
    // 相当于 Object.prototype.toString
    var toString = class2type.toString;
    
    // 相当于 Object.prototype.hasOwnProperty
    var hasOwn = class2type.hasOwnProperty;
    
    function isPlainObject(obj) {
        var proto, Ctor;
    
        // 排除掉明显不是obj的以及一些宿主对象如Window
      	if (!obj || toString.call(obj) !== "[object Object]") {
            return false;
        }
    
        /**
         * getPrototypeOf es5 方法,获取 obj 的原型
         * 以 new Object 创建的对象为例的话
         * obj.__proto__ === Object.prototype
         */
        proto = Object.getPrototypeOf(obj);
    
        // 没有原型的对象是纯粹的,Object.create(null) 就在这里返回 true
      	if (!proto) {
            return true;
        }
    
        /**
         * 以下判断通过 new Object 方式创建的对象
         * 判断 proto 是否有 constructor 属性,如果有就让 Ctor 的值为 proto.constructor
         * 如果是 Object 函数创建的对象,Ctor 在这里就等于 Object 构造函数
         */
        Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    
        // 在这里判断 Ctor 构造函数是不是 Object 构造函数,用于区分自定义构造函数和 Object 构造函数
      	return typeof Ctor === "function" 
      				&& hasOwn.toString.call(Ctor) === hasOwn.toString.call(Object);
    }
    
    console.log(hasOwn.toString.call(Ctor)); // function Object() { [native code] }
    console.log(Object.prototype.toString.call(Ctor)); // [object Function]
    

    发现返回的值并不一样,这是因为 hasOwn.toString 调用的其实是 Function.prototype.toString,而且 Function 对象覆盖了从 Object 继承来的 Object.prototype.toString 方法。

    函数的 toString 方法会返回一个表示函数源代码的字符串。具体来说,包括 function关键字,形参列表,大括号,以及函数体中的内容。

    EmptyObject

    jQuery提供了 isEmptyObject 方法来判断是否是空对象,代码简单,我们直接看源码:

    functionisEmptyObject( obj ) {
    
            var name;
    
            for ( name in obj ) {
                return false;
            }
    
            return true;
    }
    

    isEmptyObject 就是判断是否有属性,for 循环一旦执行,就说明有属性,有属性就会返回 false。

    但是根据这个源码我们可以看出isEmptyObject实际上判断的并不仅仅是空对象。

    console.log(isEmptyObject({})); // true
    console.log(isEmptyObject([])); // true
    console.log(isEmptyObject(null)); // true
    console.log(isEmptyObject(undefined)); // true
    console.log(isEmptyObject(1)); // true
    console.log(isEmptyObject('')); // true
    console.log(isEmptyObject(true)); // true
    

    jQuery可能是因为考虑到实际开发中 isEmptyObject 用来判断 {} 和 {a: 1} 已经足够,如果真的是只判断 {},完全可以使用封装的type函数筛选掉不适合的情况。

    Window对象

    Window 对象作为客户端 JavaScript 的全局对象,它有一个 window 属性指向自身。我们可以利用这个特性判断是否是 Window 对象。

    function isWindow( obj ) {
        return obj != null && obj === obj.window;
    }
    

    isArrayLike

    isArrayLike,看名字可能会让我们觉得这是判断类数组对象的,其实不仅仅是这样,jQuery 实现的 isArrayLike,数组和类数组都会返回 true。

    function isArrayLike(obj) {
    
        // obj 必须有 length属性
      	var length = !!obj && "length"in obj && obj.length;
        var typeRes = type(obj);
    
        // 排除掉函数和 Window 对象
      	if (typeRes === "function" || isWindow(obj)) {
            return false;
        }
    
        return typeRes === "array" || length === 0 ||
            typeof length === "number" && length > 0 && (length - 1) in obj;
    }
    

    重点分析 return 这一行,使用了或语句,只要一个为 true,结果就返回 true。

    所以如果 isArrayLike 返回true,至少要满足三个条件之一:

    1. 是数组
    2. 长度为 0
    3. lengths 属性是大于 0 的数组,并且obj[length - 1]必须存在

    第一个就不说了,看第二个,为什么长度为 0 就可以直接判断为 true 呢?

    那我们写个对象:

    var obj = {a: 1, b: 2, length: 0}
    

    isArrayLike 函数就会返回 true,那这个合理吗?

    回答合不合理之前,我们先看一个例子:

    function a(){
        console.log(isArrayLike(arguments))
    }
    a();
    

    如果我们去掉length === 0 这个判断,就会打印 false,然而我们都知道 arguments 是一个类数组对象,这里是应该返回 true 的。

    所以是不是为了放过空的 arguments 时也放过了一些存在争议的对象呢?

    第三个条件:length 是数字,并且 length > 0 且最后一个元素存在。

    为什么仅仅要求最后一个元素存在呢?

    让我们先想下数组是不是可以这样写:

    var arr = [,,3]
    

    当我们写一个对应的类数组对象就是:

    var arrLike = {
        2: 3,
        length: 3
    }
    

    也就是说当我们在数组中用逗号直接跳过的时候,我们认为该元素是不存在的,类数组对象中也就不用写这个元素,但是最后一个元素是一定要写的,要不然 length 的长度就不会是最后一个元素的 key 值加 1。比如数组可以这样写

    var arr = [1,,];
    console.log(arr.length) // 2
    

    但是类数组对象就只能写成:

    var arrLike = {
        0: 1,
        length: 1
    }
    

    所以符合条件的类数组对象是一定存在最后一个元素的!

    这就是满足 isArrayLike 的三个条件,其实除了 jQuery 之外,很多库都有对 isArrayLike 的实现,比如 underscore:

    var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
    
    var isArrayLike = function(collection) {
        var length = getLength(collection);
        returntypeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
    };
    

    isElement

    isElement 判断是不是 DOM 元素。

    var isElement = function(obj) {
        return !!(obj && obj.nodeType === 1);
    };
    

    起源地下载网 » javascript数据类型检测及检测方法封装

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元