javaScript复习—基本类型和引用类型,以及如何判断数据类型

·  阅读 186

原始值和引用值类型和区别

在javaScript中,变量可以储存两种值:原始值和引用值。

  • 基本类型:原始值代表原始数据类型的值,比如Number、String、Boolean、Null、Undefined、Symbol

  • 引用类型:引用值指的是复合数据类型的值,比如Object(包括数组)、Function、Date、RegExp。所有引用类型都是Object的实例。

变量的存放

  • 基本类型的数据存放在栈内存中,基本值在内存中占有固定大小的空间,在栈内存中按值查找。(这里需要注意的是,虽然字符串长度可变,但是ECMAscript还是将它归为基本类型)
  • 引用类型的数据存放在堆内存中,引用值的大小不固定,但是他们的内存地址的固定大小的,因此可以将引用值的内存地址存放在栈内存中。也就是当查询引用类型的变量时,先从栈中读取内存地址,再通过地址找到堆中的值

在计算机的数据结构中,栈比堆的运算速度快,而将数组扩充、对象属性的添加等操作放在堆中,可以保证栈的效率不被影响。

原始值和引用值的传递问题

这部分是面试和笔记常见的考点,敲黑板,都给我记!

  • 原始值的传递:当我们把一个原始变量传递给另一个原始变量时,其实是把栈空间中的内容复制到另一个栈空间中,这时候两个原始值传递互并不影响

      let a = 1;
      let b = a ;
      a = 2;
      console.log(a);    // 2
      console.log(b);    // 1
    复制代码
  • 引用值传递: 其实变量中存放的是指向引用值的指针,所以当我们将一个引用变量传递给另一个引用变量时,其实是将这个指针复制了一份传递给另一个变量。简单来说,这两个变量都指向同一个引用值(假设是对象)。当通过方法修改其中一个变量引用的对象的属性后,通过另一个变量访问这个对象,会发现属性值也随之改变了。

      let a = {
          value: 1
      };
      let b = a;
      a.value = 2;
      console.log(a.value);    // 2
      console.log(b.value);    // 2
    复制代码

如何判断数据类型

typeof

typeof是一元操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回结果用该类型的字符串(全小写)表示,共7种:number、string、boolean、symbol、object、function、undefined

typeof 1;   // number 有效判断
typeof "hello world";  // string 有效判断
typeof true;    // boolean 有效判断
typeof Symbol();   // symblo 有效判断
typeof new Function;   // function 有效判断
typeof undefined;    // undefined 有效判断

typeof null;   // object  无效判断
typeof new Object;   // object 无效判断
typeof [];     // object 无效判断
typeof new Date();   // object 无效判断
typeof new RegExp();   // object 无效判断
复制代码

这里我要放一个我自己之前混起来的点(但是现在解决啦嘿嘿~)

    typeof Object;    // function
    typeof new Object;    // object
复制代码

检讨: 为什么会混起来!!!忘记了Object是对象的构造函数! 当然new Date和Date也是这样的,其他的也是!要举一反三哦!

总结:

  • typeof可以准确判断除null之外的基本类型结果
  • 对于null,返回结果是object
  • 对于引用类型,除function之外返回object
  • 对于function,返回结果为function

instanceof

instanceof用来判断A是否为B的实例(A instanceof B),如果是,则返回true,如果不是则返回false

instanceof实现逻辑如下:

instanceof(A,B) {
    if(A.__proto__ === B.prototype) {
        return true
    }
    return false
}
复制代码

这里就扯到原型链了,instanceof其实是在原型链上寻找构造函数的原型和实例对象的关系,如果实例对象通过__proto__指向构造函数的原型(及Parent.prototype === p.__proto__),则返回true

我们来瞅瞅这几个栗子:

1 instanceof Number;   // false
"hello world" instanceof String;    // false
true instanceof Boolean;  // false
null instanceof Object;  // false   快看!这个和typeof返回的不一样!!!!
null instanceof Null;    // 报错 实际并不存在
undefined instanceof Undefinef;   // 报错 实际并不存在
复制代码

instanceof没办法判断原始值的类型!原因是原始值并没有转换成对象,instanceof是没办法检测到原型的,所以直接返回false

不过下面这些都是可以判断的啦,我没有写全!

new Date instanceof Date;  // true
new Function instanceof Function;  //true
[] instanceof Array;  // true
[] instanceof Object;  // true
...
复制代码

大家快看看这个[],其实并不是判断错了哦,只是[].__proto__ === Array.prototype,而且[].__proto__.__proto__ === Object.prototype,所以Instanceof顺着原型链找到这个结果,就返回true

然后今天学到了完整的instanceof功能实现:

function myInstanceof(left,right) {
    // 首先如果是基本类型及null直接返回false
    if(typeof left !== 'object' || left == null) return false;
    let proto = Object.getPrototypeOf(left);
    while(true) {
        // 如果把原型链找到底了都没有找到
        if(proto == null)  return false;
        // 找到了
        if(proto == right.prototype)  return true;
        // 在原型链上找原型的原型
        proto = Object.getPrototyprOf(proto);
    }
}
复制代码

constructor

当一个函数被创建的时候,JS引擎就会为它加上prototype原型,而prototype存在一个constructor属性,指向这个函数的引用。

所以constructor判断数据类型的原理其实就是通过寻找构造函数来判断类型

function F() {}
var f = new F();
f.constructor === F;   // true
或者
f.\_\_proto\_\_.constructor === F   // true
复制代码

瞅瞅这些热乎乎的栗子呀:

new Date().constructor === Date; // true
new Function().constructor === Function; // true
"hello world ".constructor === String;  // true
复制代码

这里要注意的是访问原型上的内置函数和属性,会导致基本类型到引用类型的转换!所以"hello world"现在已经变成了引用类型了。

Object.prototype.toString.call()

toString()是Object原型上的方法,调用该方法,会返回当前对象的[[Class]]属性,[[Class]]是对象的内部属性,里面包含了对象的类型信息,其格式为[object Xxx],其中Xxx就是对应的具体类型。

对于Object对象,只需要直接调用此方法

var obj = {};
obj.toString();    // [object Object]
复制代码

而对于其他对象,则需要通过call或者apply来绑定调用的对象

Object.prototype.toString.call('') ;  // [object String]
Object.prototype.toString.call(1) ;   // [object Number]
复制代码

但是但是这个方法其实也有缺陷的!让我们瞅瞅ES6提出的Symbol.toStringtag符号,这个符号是所有对象都具有的属性,它的功能是定义对象[object Xxx]中的Xxx。

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改