JavaScript 的资料型别 (data type) 及存取值

前言:最近算是自学到一个阶段~已经开始面试。这次参加铁人赛的主题以 JS 基础知识为主,并会尽量将面试碰到的问题稍做整理结合进内容。作者仍努力学习当中,若有错误还请指正~感谢大家!(•ө•)♡

开市第一篇来个 by value、by reference 小boss,自己对於这个概念看了不下十次了...没慧根就只能勤能补拙啦!

截至目前的 JS 版本,JS 的资料型别依然分成两大类:「原始型别、物件型别」,且不支援开发者自定义其他型别。

这篇主要内容是针对两大类的介绍以及他们如何存值、取值。

https://ithelp.ithome.com.tw/upload/images/20210913/20141763JbELrkSNW4.png

Primitive type 原始型别

原始型别包含以下七种资料类型

当我们要从这些型别引用值时,这些值都是属於原始值 (primitive values)。

原始值是什麽呢?原始值代表不可被改变的值,听起来有点抽象对吧~

举个例子

var a = 'hey'
a.toUpperCase()
console.log(a) // 依然是 hey
a = a.toUpperCase()
console.log(a) // 得到 HEY

这边我们先把 a 变量赋值为字串 hey,并且用了转为大写的 method,但可以发现没有作用。因为字串 hey 是原始值,我们日常规范的英文 h 就是 h,h 不会是 y,也不是 H。又或者 1 就是 1,1 不会是 0,这就是原始值的意思。我觉得如果把原始值翻作原语也许会更好理解些~

而在 JS 中,原始值是无法改变的,导致第二行 a.toUpperCase() 没有效果。

那麽为什麽 a = a.toUpperCase() 却可以操作?要注意避免把赋值这件事跟原始值本身搞混了。

我们可以给变数重新分配一个「新值」,但不能去修改「原本的值」。

这个点是我後来才理解的,没注意到就会忽略掉 ,主要是对原始值这个词花了点时间才能意会。(´・_・`)

Object type 物件型别

除了上述提到的原始型别以外,其他的值都是物件型别,而物件包括了 Function、Array、Object、Date、Map、Set 等等,总之若不是原始型别,就会归到物件型别。

物件型别要详细说的话,还得开更多篇幅才能好好解释其中的奥妙( 菜鸡如我也还在宇宙中探索 )。但以刚开始认识 JS 来说,我认为先记得物件型别跟原始型别的差异在於,物件型别是可变的、以及把这些类型分清楚就好,因为会关系到下半部分提到的另一个新手大魔王 by value、 by reference。

请记住,所有值最终都会是这八种资料类型其中之一


理解值的储存及引用 「 pass by value、pass by reference」

值属於原始型别或是物件型别,影响到值被储存、取用的方式。

先记得结论:

原始型别的值是 pass by value

物件型别的值是 pass by reference

https://ithelp.ithome.com.tw/upload/images/20210913/20141763pHLP2fJRbC.png

在讲两者的区别前,大家必须先知道 JS 背後对於变数的值是怎麽储存的。

浏览器运作时,会占用电脑的内存来执行网站所需要的程序码。它将内存分配给 HTML CSS JS 等档案,而在 JS 中,做法是会把用来存变数值的地方分成 stack(栈) 跟 heap(堆)。

https://ithelp.ithome.com.tw/upload/images/20210913/2014176378MYX9zcq3.png

stack 占用的空间比较小、系统分配效率高,因此所存的是 value 的值以及 reference 的指针,可以看做一种简单储存,每个值的大小都差不多。

heap 占用的空间大、自己存完还得分配指针到 stack → 效率相对低,用来存 reference 的值。

( stack 跟 heap 其实底层原理还得探讨到资料结构,但这就偏题了,所以本篇不论述太多 )

好的~了解上面之後就先来看 pass by value

Pass by value

原始型别的值是以 pass by value 的形式操作,储存以及复制的都是「值本身」

当我们的变数是原始型别时,复制这个变数,会发生什麽事?

var idol1 = 'Taeyeon'; // 字串=原始值
var idol2 = idol1;

第一行 宣告 idol1 为一个变数, idol1 在 stack 会指向 'Taeyeon' 这个值

接着宣告 idol2 = idol1 ,此时 pass by value 的做法会在 stack 复制一个新的 'Taeyeon' 并指向 idol2

https://ithelp.ithome.com.tw/upload/images/20210913/20141763poPStnDfxE.png

这种做法的坑在哪?我们现在把本来排定要第一场表演的 Taeyeon 改为 Joy

var idol1 = 'Taeyeon'; 
var idol2 = idol1;

idol1 = 'Joy'; // 更改 idol1 的值
console.log(idol2);

改变 idol1 的值後,你可能会想说 idol2 是从 idol1 复制来的,那 idol2 应该也是 Joy 对吧?

No~~~ 正如刚所说的,idol2 在 stack 其实是一个独立的新值,所以 idol2 仍然是 Taeyeon !并不会被 idol1 影响。

https://ithelp.ithome.com.tw/upload/images/20210913/20141763pQeXDXDgF5.png

这就是 pass by value 的特性,当你的值为原始型别时,彼此形同陌路(误。

Pass by reference

物件型别则是 pass by reference 引用数据类型

当我们创建一个变数并赋予值为物件型别时,他会在 stack 中存着一个指针,指针指向 heap 的值。此後,当 JS 解析你要取得该物件的时候,就先在 stack 检索该地址,然後从 heap 取出实值。这麽解释可能有点蒙,看例子!

var obj1 = { name:'Agnes' }
var obj2 = ['apple','grape','lemon']

我们宣告了两个物件,实际上是如此存储的

https://ithelp.ithome.com.tw/upload/images/20210913/20141763UNsvomTkfm.png

obj1 在 stack 里存的是一个检索地址 0x001 ,这个 0x001 可以想成指针指向 heap 里的值

那复制引用类型的值会发生什麽事?

var obj1 = { name:'Agnes' }
var obj2 = ['apple','grape','lemon']

var obj3 = obj1; // 把 obj3 赋值

这个动作会把 obj3 在 stack 的指针,指向跟 obj1 一样的地址

https://ithelp.ithome.com.tw/upload/images/20210913/201417631M8jLtITAc.png

特性是在我们改变值时,会连带影响到其他指向同地址的变数

var obj1 = ['apple','grape','lemon']
var obj2 = { name:'Agnes' }

var obj3 = obj1
obj3[0]='banana'

console.log(obj1)

去实际看会发现 obj1 也被改成 banana 了,这就是取用同个地址的值的结果

https://ithelp.ithome.com.tw/upload/images/20210913/20141763m5KRNZrsBx.png

所以要记得,如果是引用类型的数据~会影响彼此喔!

当了解这个观念後,也许你会突然豁然开朗最初写 code 的时候,为什麽有时候资料被改变,有时候却没有变?

再补充一个 pass by reference 的相关知识

我们刚所复制的方式是赋予另个变数,但如果是赋予完全同样的值,那是会创建一个新地址喔!JS 并不会去比对「他们的值一样,所以他们是同个物件。」

https://ithelp.ithome.com.tw/upload/images/20210913/20141763FXAopeFw7c.png

因此我们若更改 obj2 的值也与 obj4 无关

var obj1 = ['apple','grape','lemon']
var obj2 = { name:'Agnes' }

var obj3 = obj1
obj3[0]='banana'

var obj4 = { name:'Agnes' }
obj2.name = 'Bill' // 更改 obj2
console.log(obj4) // 依然是 Agnes

这时候头脑动很快的小朋友就会开始想说,如果我想要拥有一样的物件类型的值,但不要变动到原本的值该怎麽做?请 Google「深拷贝、浅拷贝」来处理!因为这观念是个不小议题,这边就不详细说明,看之後有没有篇幅再来笔记~

参考资料
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Data_structures
https://www.javascripttutorial.net/javascript-primitive-vs-reference-values/
https://roy-code.medium.com/普通类型和对象的区别-栈内存-stack-堆内存-heap-44295724848c
https://www.cnblogs.com/heioray/p/9487093.html


<<:  Day 03 HTML<列表标签>

>>:  2021-Day8. 第一印象很重要!!从使用者加好友时,就建立良好关系:Line加好友欢迎讯息实作(一)

Day 23 - Either Monad

到目前为止,我们介绍 Maybe Monad 其是专门处理无值情境以及 IO Monad 则是处理同...

【第十四天 - 堆叠型 SQL注入】

Q1. 什麽是 堆叠型 SQL注入? 堆叠型 SQL注入也称为 堆查询注入,英文为 stacked ...

【I Love Vue 】 Day 27 爱荷华博弈任务(八) - 测验画面2

我们接着继续开发 测验区 的部分 测验区 先来看看画面: 可以看到测试区除了上面的总分之外,我们有四...

Day20 vue.js椅毒供毒之整理code

写出不乾净的code跟WEED一样 一开始写的时候可能会感到轻松快乐 但是後续的维护或修改会把自己搞...

新新新手阅读 Angular 文件 - Day05

学习内容 这篇内容是纪录阅读官方文件 Create a feature component 的内容。...