怎样判断一个变量是数组还是对象?

判断的基本方法

1. typeof(不可以)

通常情况下,我们第一时间会想到typeof运算符,因为typeof是专门用于类型检测的,但是typeof并不能满足这样的需求,比如

let a = ['7','4','1']
console.log(typeof(a)  //输出object
复制代码

2. instanceof

instanceof运算符用于通过查找原型链来检查某个变量是否为某个类型数据的实例,使用instanceof运算符可以判断一个变量是数组还是对象。

  var a = [1, 2, 3];
      console.log(a instanceof Array); // true
      console.log(a instanceof Object); // true
      //从此我们可以看出a既是数组,也是对象

  var userInfo = { userName: "zhangsan" };
      console.log(userInfo instanceof Array); // false
      console.log(userInfo instanceof Object); // true
      //userInfo只是对象,而不是数组
复制代码

基于以上逻辑,我们可以封装以下函数,用于判断变量是数组类型还是对象类型

var a = [1,2,3]
function getType(obj){
   if(obj instanceof Array){
       returen 'Array'//如果是数组,则返回数组类型
   }else if(obj instanceof Object){
       return 'Object'
   }else{
       return '既不是Array也不是Object'
   }
}
console.log(getType(a))
复制代码

最近一次面试中,面试老师追问,instanceof的底层原理是什么? 那我们就来浅挖一下instanceof的底层原理

2.1 instanceof底层是如何工作的:

在 MDN 上是这样描述 instanceof 的:

instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象原型链中的任何位置

换句话说,如果A instanceof B,那么 A 必须是一个对象,而 B 必须是一个合法的 JavaScript 函数。在这两个条件都满足的情况下:

判断 B 的 prototype 属性指向的原型对象(B.prototype)是否在对象 A 的原型链上。

如果在,则为 true;如果不在,则为 false。


function instance_of(L, R) {//L 表示左表达式,R 表示右表达式 
    var O = R.prototype;   // 取 R 的显示原型 
    L = L.__proto__;  // 取 L 的隐式原型
    while (true) {    
        if (L === null)      
             return false;   
        if (O === L)  // 当 O 显式原型 严格等于  L隐式原型 时,返回true
             return true;   
        L = L.__proto__;  
    }
}
复制代码

2.2 利用原型继承,切断了原来的prototype的指向,而指向了一个新的对象,这时instanceof又是如何进行判断的呢?

没有发生继承关系

 function Person(name,age,sex){
     this.name = name;
     this.age = age; 
     this.sex = sex;
 }
 function Student(score){
     this.score = score; 
  }
 var per = new Person("小明",20,“男”);
 var stu = new Student(98) 
 console.log(per instanceof Person);  // true
 console.log(stu instanceof Student);  // true
 console.log(per instanceof Object);  // true
 console.log(stu instanceof Object);  // true
复制代码

没有发生继承关系的原型图

无继承关系原型.webp

发生了继承关系

 function Person(name,age,sex){
     this.name = name;
     this.age = age;
     this.sex = sex;
 }
 function Student(name,age,sex,score){
     Person.call(this,name,age,sex);  
     this.score = score;
  }
 Student.prototype = new Person();  // 这里改变了原型指向,实现继承
 var stu = new Student("小明",20,"男",99); //创建了学生对象stu
 console.log(stu instanceof Student);    // true
 console.log(stu instanceof Person);    // true
 console.log(stu instanceof Object);    // true
复制代码

发生继承关系后的原型图

image.png

发生继承后instanceof的工作流程分析:

首先看 stu instanceof Student
 function instance_of(L, R) { //L即stu ;  R即Student
   var O = R.prototype;  //O为Student.prototype,现在指向了per
    L = L.__proto__;    //L为stu._proto_,也随着prototype的改变而指向了per
    while (true) {    //执行循环
          if (L === null)  //不通过
              return false;   
          if (O === L)    //判断: Student.prototype ===stu._proto_?
              return true;  //此时,两方都指Person的实例对象per,所以true
          L = L.__proto__;                   
      }
 } 
复制代码

所以,即使发生了原型继承,stu instanceof Student 依然是成立的。

再看 stu instanceof Person,instanceof是如何判断stu继承了Person
 function instance_of(L, R) { // L即stu ;  R即Person        
   var O = R.prototype; // O为Person.prototype     
    L = L.__proto__;   //L为stu._proto_,现在指向的是per实例对象
    while (true) {   // 执行循环                   
       if (L === null)   //不通过                            
           return false;                    
       if (O === L)    //判断:   Person.prototype === stu._proto_ ?      
            return true;   //此时,stu._proto_ 指向per实例对象,并不满足
        L = L.__proto__;  //令L=  stu._proto_._proto_,执行循环
   }                      //stu._proto_ ._proto_,看图示知:
}                        //指的就是Person.prototype,所以也返回true
复制代码

2.3 结论

1、instanceof 的作用

用于判断一个引用类型是否属于某构造函数;

还可以在继承关系中用来判断一个实例是否属于它的父类型。

2、和typeof的区别:

typeof在对值类型number、string、boolean 、null 、 undefined、 以及引用类型的function的反应是精准的;但是,对于对象{ } 、数组[ ] 、null 都会返回object 为了弥补这一点,instanceof 从原型的角度,来判断某引用属于哪个构造函数,从而判定它的数据类型。

3.通过构造函数来判断

判断一个变量是否是数组还是对象,其实就是判断变量的构造函数是Array类型还是Object类型。 因为一个对象的实例都是通过构造函数创建的。 因为一个对象的实例都是通过构造函数创建的。

 var a = [1, 2, 3];
      console.log(a.__proto__.constructor === Array); //true
      console.log(a.__proto__.constructor === Object); // false
复制代码

同样这里,这里我们也可以封装一个函数,来判断变量是数组类型还是对象类型。

  function getType(o) {
        //获取构造函数
        var constructor = o.__proto__.constructor;
        if (constructor === Array) {
          return "Array";
        } else if (constructor === Object) {
          return "Object";
        } else {
          return "参数类型不是Array也不是Object";
        }
      }
      var a = [1, 2, 3];
      console.log(getType(a));
复制代码

4. 通过toString( )函数来判断

每种引用类型都会直接或间接继承Object类型,因此它们都包含toString( )函数。 不同数据类型的toString( )函数返回值也不一样,所以通过toString( )函数就可以判断一个变量是数组还是对象,当然,这里我们需要用到call方法来调用Object原型上的toString( )函数来完成类型的判断。

var arr = [1, 2, 3];
var obj = { userName: "zhangsan" };
console.log(Object.prototype.toString.call(arr)); //[object Array]
console.log(Object.prototype.toString.call(obj)); // [object Object]
console.log(arr.toString()); // 1,2,3
复制代码

5. 通过Array.isArray( )函数来判断

Array.isArray 方法用来判断变量是否为数组。 当检测Array实例时, Array.isArray 优于 instanceof,因为Array.isArray能检测iframes.

    var arr = [1, 2, 3];
      var obj = { name: "zhangsan" };
      console.log(Array.isArray(1)); //false
      console.log(Array.isArray(arr)); //true
      console.log(Array.isArray(obj)); //false
复制代码

以上内容仅供学习参考,如有错误欢迎大家指出

Guess you like

Origin juejin.im/post/7075928358088867871