JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用课程,课程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教学影片里,建立30个JavaScript的有趣小东西。
另外,Wes Bos 也很无私地在 Github 上公开了所有 JS 30 课程的程序码,有兴趣的话可以去 fork 或下载。
了解 JavaScript 的 Passed by Value
和 Passed by Reference
以及 Object 的 shallow copy
和 deep copy
。
在 JavaScript 里,如果变数的资料型态属於 Primitive(原生型别),则在传递变数时,采用的是 Passed by Value (传递值)。
在下面的两个例子,我们分别将age
、name
传递给age2
、name2
,之後改变age2
、name2
的数值。结果原本的age
、name
均不会受到影响,因为在传递过程是 Passed by Value,将原本存於age
、name
的值复制一份给age2
、name2
。
let age = 100;
let age2 = age;
console.log(age,age2);
age = 200;
console.log(age,age2);
let name = 'Mes';
let name2 = name;
console.log(name,name2);
name = 'wesley';
console.log(name,name2);
在 JavaScript 里,如果变数的资料型态属於 Object(物件型别),则在传递变数时,采用的是 Passed by Reference (传递位址)。
下面的例子,我们将阵列 players
传递给常数 team
,之後将team[3]
(Poppy)改成Lux
,再把两个阵列都印到 console,会发现到两个阵列的第四个位置都被修改成新的值(Lux)。
这是因为物件在传递的过程中采取的是 Passed by Reference,也就是将物件在记忆体上的位置传递给另一个物件(两者共享同一个记忆体位置),所以只要更改其中一方,另一方也会受到影响。
const players = ['Wes','Sarah','Ryan','Poppy'];
const team = players;
console.log(players,team);
team[3] = 'Lux';
console.log(players,team);
如果想修改阵列的值又不影响另一个阵列的话,我们可以使用slice()
来复制阵列并回传新阵列(拥有自己的记忆体空间)。
下面的例子,我们呼叫players.slice()
将阵列players
完整的复制一份到新阵列中并传递给team2
,之後再修改team2
的第四个位置,这次我们发现到原来的阵列players
并没有被修改到。
注意:slice()
进行的是shallow copy
(浅复制)!!!
const team2 = players.slice()
team2[3] = 'Lux';
console.log(players,team2)
其他修改阵列的值又不影响另一个阵列的方法有:
slice()
一样会回传串联後的新阵列。const team3 = [].concat(players);
//use the new ES6 Spread
const team4 = [...players];
const team5 = Array.from(players);
另一个例子,我们宣告物件person
并把它传递给captain
,在captain
上新增number: 90
後将两个物件都印到 consle,此时会发现在captain
新增的number: 90
,也会被加到person
上。会这样是因为物件在传递过程是 Passed by Reference,两物件同时指向一个记忆体空间。
const person = {
name: 'Wes Bos',
age: 80
};
const captain = person;
captain.number = 99;
console.log(person,captain);
如果不想修改到另一个物件的话,我们可以使用Object.assign(target, ...sources)
来复制一个或多个物件的属性到另一个目标物件,最後回传目标物件。
下面我们利用Object.assign({},person,{number: 99,age:12});
,将物件person
和{number:99,age:12}
的属性复制到目标物件({ })。
如果复制的多个物件的属性有重复,以後面物件的属性为准进行合并。举例来说age
是重复出现的属性,最後复制属性值时,要以後面出现的age:12
为准合并物件。
注意 : Object.assign()
做的是 shallow copy(浅复制),如果要复制的物件属性包含子物件,就会复制到子物件的参照(reference)!!!。
const person = {
name: 'Wes Bos',
age: 80
};
const cap2 = Object.assign({},person,{number: 99,age:12});
console.log(person,cap2);
原来的person
并没有被改动到。
前面我们有提到,Object.assign()
做的是 shallow copy(浅复制),若要复制的物件属性包含子物件,就会复制到子物件的参照。
所以只要去改动新物件的子物件属性值,就会连带影响原本复制的物件子属性值。
下面我们用Object.assign()
复制来源物件(wes
)的属性到目标物件({}),再将目标物件传递给dev
物件,要留意wes
的属性包含子物件属性(social
)。
例子中我们修改复制来的物件属性(name
)和子物件属性(social.twitter
),之後把wes
、dev
物件印到 console 会发现复制的来源物件wes
的属性(name
)不受影响,但wes
的子物件属性(social.twitter
)却被影响。
const wes = {
name: 'Wes',
age: 100,
social: {
twitter: '@wesbos',
facebook: 'wesbos.developer'
}
}
//shallow copy
const dev = Object.assign({},wes);
dev.name = 'Wesley';
console.log(wes,dev);
dev.social.twitter = '@coolman';
console.log(wes.social,dev.social);
如果不想改动复制来源物件的子属性值(social.twitter
),我们可以将要复制的物件用JSON.stringify()
先转换成 JSON String(因为是Primitive,所以是 Passed by Value),再用JSON.parse()
把它还原成物件再回传给dev2
。
下面我们一样修改子物件的属性值,但这次的原物件子属性值并没有受到影响。
const wes = {
name: 'Wes',
age: 100,
social: {
twitter: '@wesbos',
facebook: 'wesbos.developer'
}
}
// deep copy
const dev2 = JSON.parse(JSON.stringify(wes));//先转成string再换回object
dev2.social.twitter = '@coolman';
console.log(wes.social,dev2.social);
JavaScript: var, let, const 差异
Array.prototype.concat()
Spread syntax (...)
Array.from()
Object.assign()
JSON.stringify()
JSON.parse()
>>: GitHub Action YAML 撰写技巧 - 环境变数(Environment Variables) 与 秘密 (Secrets)
程序基本结构 程序的基本结构可概分为循序式结构、选择式结构,与重复式结构三种,几乎是在循序结构式的基...
前言 本文说明使用TA-Lib函式库计算及呈现布林通道。 布林通道 布林通道(Bollinger B...
今天来介绍,数位凭证,讲一点点数位签章,以及最重要的凭证绑定原理及用途 避免有人透过手机使用 Bu...
「render prop」这个词指的是一种用一个其值为函式的 prop 来在 React comp...
前言:今天要来介绍第二种分割资料的排序法,就让我们来看看这个有趣的排序法吧! 合并排序: 首先会将一...