前言:最近算是自学到一个阶段~已经开始面试。这次参加铁人赛的主题以 JS 基础知识为主,并会尽量将面试碰到的问题稍做整理结合进内容。作者仍努力学习当中,若有错误还请指正~感谢大家!(•ө•)♡
开市第一篇来个 by value、by reference 小boss,自己对於这个概念看了不下十次了...没慧根就只能勤能补拙啦!
截至目前的 JS 版本,JS 的资料型别依然分成两大类:「原始型别、物件型别」,且不支援开发者自定义其他型别。
这篇主要内容是针对两大类的介绍以及他们如何存值、取值。
原始型别包含以下七种资料类型
当我们要从这些型别引用值时,这些值都是属於原始值 (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() 却可以操作?要注意避免把赋值这件事跟原始值本身搞混了。
我们可以给变数重新分配一个「新值」,但不能去修改「原本的值」。
这个点是我後来才理解的,没注意到就会忽略掉 ,主要是对原始值这个词花了点时间才能意会。(´・_・`)
除了上述提到的原始型别以外,其他的值都是物件型别,而物件包括了 Function、Array、Object、Date、Map、Set 等等,总之若不是原始型别,就会归到物件型别。
物件型别要详细说的话,还得开更多篇幅才能好好解释其中的奥妙( 菜鸡如我也还在宇宙中探索 )。但以刚开始认识 JS 来说,我认为先记得物件型别跟原始型别的差异在於,物件型别是可变的、以及把这些类型分清楚就好,因为会关系到下半部分提到的另一个新手大魔王 by value、 by reference。
请记住,所有值最终都会是这八种资料类型其中之一
值属於原始型别或是物件型别,影响到值被储存、取用的方式。
先记得结论:
原始型别的值是 pass by value
物件型别的值是 pass by reference
在讲两者的区别前,大家必须先知道 JS 背後对於变数的值是怎麽储存的。
浏览器运作时,会占用电脑的内存来执行网站所需要的程序码。它将内存分配给 HTML CSS JS 等档案,而在 JS 中,做法是会把用来存变数值的地方分成 stack(栈) 跟 heap(堆)。
stack 占用的空间比较小、系统分配效率高,因此所存的是 value 的值以及 reference 的指针,可以看做一种简单储存,每个值的大小都差不多。
heap 占用的空间大、自己存完还得分配指针到 stack → 效率相对低,用来存 reference 的值。
( stack 跟 heap 其实底层原理还得探讨到资料结构,但这就偏题了,所以本篇不论述太多 )
好的~了解上面之後就先来看 pass by value
原始型别的值是以 pass by value 的形式操作,储存以及复制的都是「值本身」
当我们的变数是原始型别时,复制这个变数,会发生什麽事?
var idol1 = 'Taeyeon'; // 字串=原始值
var idol2 = idol1;
第一行 宣告 idol1 为一个变数, idol1 在 stack 会指向 'Taeyeon' 这个值
接着宣告 idol2 = idol1 ,此时 pass by value 的做法会在 stack 复制一个新的 'Taeyeon' 并指向 idol2
这种做法的坑在哪?我们现在把本来排定要第一场表演的 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 影响。
这就是 pass by value 的特性,当你的值为原始型别时,彼此形同陌路(误。
物件型别则是 pass by reference 引用数据类型
当我们创建一个变数并赋予值为物件型别时,他会在 stack 中存着一个指针,指针指向 heap 的值。此後,当 JS 解析你要取得该物件的时候,就先在 stack 检索该地址,然後从 heap 取出实值。这麽解释可能有点蒙,看例子!
var obj1 = { name:'Agnes' }
var obj2 = ['apple','grape','lemon']
我们宣告了两个物件,实际上是如此存储的
obj1 在 stack 里存的是一个检索地址 0x001 ,这个 0x001 可以想成指针指向 heap 里的值
那复制引用类型的值会发生什麽事?
var obj1 = { name:'Agnes' }
var obj2 = ['apple','grape','lemon']
var obj3 = obj1; // 把 obj3 赋值
这个动作会把 obj3 在 stack 的指针,指向跟 obj1 一样的地址
特性是在我们改变值时,会连带影响到其他指向同地址的变数
var obj1 = ['apple','grape','lemon']
var obj2 = { name:'Agnes' }
var obj3 = obj1
obj3[0]='banana'
console.log(obj1)
去实际看会发现 obj1 也被改成 banana 了,这就是取用同个地址的值的结果
所以要记得,如果是引用类型的数据~会影响彼此喔!
当了解这个观念後,也许你会突然豁然开朗最初写 code 的时候,为什麽有时候资料被改变,有时候却没有变?
再补充一个 pass by reference 的相关知识
我们刚所复制的方式是赋予另个变数,但如果是赋予完全同样的值,那是会创建一个新地址喔!JS 并不会去比对「他们的值一样,所以他们是同个物件。」
因此我们若更改 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
>>: 2021-Day8. 第一印象很重要!!从使用者加好友时,就建立良好关系:Line加好友欢迎讯息实作(一)
到目前为止,我们介绍 Maybe Monad 其是专门处理无值情境以及 IO Monad 则是处理同...
Q1. 什麽是 堆叠型 SQL注入? 堆叠型 SQL注入也称为 堆查询注入,英文为 stacked ...
我们接着继续开发 测验区 的部分 测验区 先来看看画面: 可以看到测试区除了上面的总分之外,我们有四...
写出不乾净的code跟WEED一样 一开始写的时候可能会感到轻松快乐 但是後续的维护或修改会把自己搞...
学习内容 这篇内容是纪录阅读官方文件 Create a feature component 的内容。...