【重学JS之路】js基础类型和引用类型

729 阅读5分钟

前言

本人的github:github.com/TomatoesMan…希望能给个star,鞭策一下,谢谢

本人的博客:TomatoesMan.github.io

接下来一段时间,将会重学一遍JS基础,开始第一篇,那我们从 js 最基础的讲起,基础类型和引用类型

1、什么是基础类型(值类型、原始类型)

保存在栈中的类型就是基础类型,原因是基础类型存储的空间很小,存放在栈中方便查找,且不易于改变

js 中的基础类型(值类型、原始类型)有哪些

UndefinedNullBooleanNumberStringSymbolES6中新加类型)// undefined和null是所有类型的子类型

2、什么是引用类型

指有多个值构成的对象,引用数据类型是存储在堆中,也就是说存储的变量处的值是一个指针,指向存储对象的内存地址。存在堆中的原因是:引用值的大小会改变,所以不能放在栈中,否则会降低变量查询的速度

js 中的引用类型有哪些

Object;

3、如何判断类型

3.1 typeof

具体使用:

console.log(typeof a); //'undefined'
console.log(typeof true); //'boolean'
console.log(typeof "123"); //'string'
console.log(typeof 123); //'number'
console.log(typeof NaN); //'number'
console.log(typeof null); //'object'
let obj = new String();
console.log(typeof obj); //'object'
let fn = function () {};
console.log(typeof fn); //'function'
console.log(typeof class c {}); //'function'
let sym = Symbol();
console.log(typeof sym); //'symbol'

实现原理如下:

不同的对象在底层都表示为二进制, 在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型, null 的二进制表示是全 0, 自然前三位也是 0, 所以执行 typeof 时会返回“object”。

所以 typeof null 的返回值是 object,这是个“历史遗留问题”。
因为 typeof 判断引用类型时不太准确,所以我们选用其他方法。

3.2 instanceof

通常来说,instanceof 是判断一个实例是否属于某种类型
具体使用:

console.log(1 instanceof Number); //false
console.log("1" instanceof String); //false
console.log(true instanceof Boolean); //false
console.log(function Foo() {} instanceof Function); //true
console.log({} instanceof Object); //true
console.log([] instanceof Array); //true

从以上结果可以看出,instanceof 对引用类型来说判断的非常准确,但是对于基础类型却不能精准的判断。原因是 instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。其意思就是判断对象是否是某一数据类型的实例,1、‘1’、true 并不是实例,所以为 false。
如下所示:

console.log(new Number(1) instanceof Number); //true
console.log(new String("1") instanceof String); //true
console.log(new Boolean(true) instanceof Boolean); //true

但是对 undefined 和 null,却比较特殊

console.log(new null() instanceof Null); //Uncaught TypeError: null is not a constructor
console.log(new undefined() instanceof Undefined); //Uncaught TypeError: undefined is not a constructor

原因是因为 undefined 和 null 并不是构造函数

3.3 constructor

具体使用:

console.log((1).constructor === Number); //true
console.log("1".constructor === String); //true
console.log(true.constructor === Boolean); //true
console.log([].constructor === Array); //true
console.log({}.constructor === Object); //true
console.log(function Foo() {}.constructor === Function); //true

看着貌似很准确的样子,但是还是有缺陷,当创建一个对象改变他的原型的时候,就不再准确了!

function Foo() {}
Foo.prototype = new Array();
let foo = new Foo();
console.log(foo.constructor === Array); //true

这是因为 js 每个函数都有一个 prototype 属性,指向原型对象,对象.prototype.constructor 指向的是该对象的构造函数,当把该对象的原型对象更改时,该函数的构造对象也会更改,所以就会出现如上的问题

3.4 Object.prototype.toString.call()

具体使用:

console.log(Object.prototype.toString.call(1)); //[object Number]
console.log(Object.prototype.toString.call("1")); //[object String]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call([])); //[object Array]
console.log(Object.prototype.toString.call({})); //[object Object]
console.log(Object.prototype.toString.call(function Foo() {})); //[object Function]
console.log(Object.prototype.toString.call(undefined)); //[object Undefined]
console.log(Object.prototype.toString.call(null)); //[object Null]
console.log(Object.prototype.toString.call(Symbol())); //[object Symbol]

这是最准确的类型判定的方法,就算改变原型也不会有问题。
具体分析一下这个方法背后的故事:
每个 Object 原型上都有一个 toString 的方法,当调用这个方法的时候会执行三个步骤:
1、获取对象的类名(对象类型)
2、将[object、获取的对象类型]组合为字符串
3、返回字符串

为什么要用 call 方法?

因为 Array、String 等类型都对其继承下来原型上的 toSring 方法进行了重写,无法返回我们想要的结果,所以我们通过 call 方法,将 Object.prototype.toString 上的 Object 的指向改变,才能对类型进行精准的判断。

Object.toString 和 Object.prototype.toString 的区别?

Object.toString 是 Object 构造器的方法,返回的是函数

Object.toString({}); //"function Object() { [native code] }"

Object.prototype.toString 是 Object 原型上的方法,返回的是类型字符串,才是我们想要的结果

Object.prototype.toString({}); //"[object Object]"

如果觉得有帮助,还希望能给个我的博客github:github.com/TomatoesMan…来个star,谢谢!