前言
在JavaScript中,它的内存分为三种类型:代码空间、栈空间、堆空间,其中代码空间用于存放可执行代码。
本文带大家来深入理解下栈空间与堆空间(堆内存与栈内存),欢迎各位感兴趣的开发者阅读本文。
理解数据类型
最新的 ECMAScript 标准定义了 9 种数据类型:
- 6 种原始类型,使用 typeof 运算符检查
- undefined:
typeof instance === "undefined"
- Boolean:
typeof instance === "boolean"
- Number:
typeof instance === "number"
- String:
typeof instance === "string
- BigInt:
typeof instance === "bigint"
- Symbol :
typeof instance === "symbol"
- null:
typeof instance === "object"
- Object:
typeof instance === "object"
,任何构造函数对象实例的特殊非数据结构类型,也用做数据结构:new Object,new Array,new Map,new Set,new WeakMap,new WeakSet,new Date,和几乎所有通过new
关键词创建的东西。 - Function:非数据结构,尽管 typeof 操作的结果是:
typeof instance === "function"
。这个结果是为 Function 的一个特殊缩写,尽管每个 Function 构造器都由 Object 构造器派生。
typeof
操作符的唯一目的就是检查数据类型,如果我们希望检查任何从 Object 派生出来的结构类型,使用typeof
是不起作用的,因为总是会得到"object"
。检查 Object 种类的合适方式是使用 instanceof 关键字。但即使这样也存在误差。
动态类型
JavaScript 是一种弱类型或者说动态语言。我们不需要提前声明变量的类型,在程序运行过程中,类型会被自动确定。这也意味着我们可以使用同一个变量保存不同类型的数据:
var info = "字符串类型"; // string类型 info = 20; // number类型 info = true; // boolean类型
隐式转换
+
和-
运算符转换
console.log("20" + 6) // "106" 字符串拼接 string + number = string console.log("16" - 6) // 10 减法运算 string - number = number
- 比较运算符
// ==(等于),会自动转换数据类型再比较 // ===(严格等于),不会自动转换数据类型,如果数据类型不一致,返回false;如果一致,再比较。 false == 0; // true false === 0; // false undefined == null; // true,(undefined是null的子集)
- NaN(Not a Number)这个特殊的Number与所有其他值都不相等,包括它自己:
NaN === NaN; // false isNaN(NaN); // true (isNaN() 函数用于判断NaN)
- 浮点数相等比较
1 / 3 === (1 - 2 / 3); // false // 浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值 Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
包装对象
在JavaScript中,一切皆对象。Array
(数组)和 Function
(函数)本质上都是对象,就连三种原始类型的值 — — Number
(数值)、String
(字符串)、Boolean
(布尔值) — — 在一定条件下,也会自动转为对象,也就是原始类型的 包装对象。
一般来说,只有对象是可以对属性进行读写操作的,但是我们平常用的很多的字符串方法和属性,都是通过.
操作符访问的,例如:
console.log("神奇的程序员".length); console.log("我是大白".indexOf("白"));
如上述代码所示,在我们调用这些方法和属性时,JS内部已经隐式地帮我们帮创建了一个包装对象了,上述代码JS在运行时会处理成这样:
console.log(new String("神奇的程序员").length); console.log(new String("我是大白").indexOf("白"));
浏览器自己隐式创建的包装对象和我们显式创建的包装对象不严格相等,我们举个例子
说明下:
var name = "神奇的程序员"; var info = new String("神奇的程序员"); console.log(name == info); // true console.log(name === info); // false
运行结果如下:
image-20210323224807378
类型检测
接下来我们来学习下js中几个常用的类型检测方法。
typeof运算符
typeof
可以检测变量的数据类型,返回如下6种字符串number
、string
、boolean
、object
、undefined
、function
我们举个例子说明下:
var age = 1; console.log(typeof age); // number var info = undefined; console.log(typeof info); // undefined var title = null; console.log(typeof title); // object,(null是空对象引用/或者说指针)。 var obj = new Object(); console.log(typeof obj); // object var arr = [1,2,3]; console.log(typeof arr); // object var fn = function(){} console.log(typeof fn); // function
运行结果如下:
image-20210323224959529
instanceof运算符
instanceof
,用于检测某个对象的原型链是否包含某个构造函数的prototype
属性。instanceof
适用于检测对象,它是基于原型链运作的。instanceof
除了适用于任何object
的类型检查之外,也可以用来检测内置对象,比如:Array
、RegExp
、Object
、Function
instanceof
对基本数据类型检测不起作用,主要是因为基本数据类型没有原型链。
我们举个例子来说明下:
console.log([1, 2, 3] instanceof Array); // true console.log(/abc/ instanceof RegExp); // true console.log({} instanceof Object); // true console.log(function() {} instanceof Function); // true
运行结果如下:
image-20210323225421217
constructor属性
构造函数属性,可确定当前对象的构造函数,我们举个例子说明下:
var o = new Object(); console.log(o.constructor == Object); // true var arr = new Array(); console.log(arr.constructor == Array); // true
运行结果如下:
image-20210323225557196
hasOwnProperty属性
判断属性是否存在于当前对象实例中(而不是原型对象中),我们举个例子来说明下:
const info = { title: "书", name: "大白" }; console.log(info.hasOwnProperty("title")); // true
运行结果如下:
image-20210323225809992
堆栈内存空间
接下来,我们看下什么是堆、栈内存空间。
栈内存空间
见名知意,栈内存空间 就是用栈作为数据结构在内存中所申请的空间。
对栈这种数据结构不了解的开发者,请移步我的另一篇文章:数据结构:栈与队列。
我们来回顾下栈的特点:
- 后进先出,最后添加进栈的元素最先出。
- 访问栈底元素,必须拿掉它上面的元素。
我们画个图来描述下栈,如下所示:
image-20210323113726313
堆内存空间
同样的,见名知意,堆内存空间就是用堆作为数据结构在内存中所申请的空间。
对堆这种数据结构不了解的开发者,请移步我的另外两篇文章:数据结构:堆、实现二叉堆
通常情况下,我们所说的 堆 数据结构指的是 二叉堆 ,我们来回顾下二叉堆的特点:
- 它是一颗完全二叉树
- 二叉堆不是最小堆就是最大堆
我们画个图来描述下 最大堆 与 最小堆 ,如下所示:
image-20210323134717994