[JS笔记] 按值传递 & 按引用传递

JS中的变量传递到底是 按值传递 还是 按引用传递 .

看了很多资料但是觉得还是好难懂,自己来做一下总结吧

变量

平常我在向刚学习编程的小朋友们讲变量的时候,都会打一个比方:

你可以试着把变量当成是一个箱子, 箱子里的东西 就是 变量的值.

一. 基本类型值

小的东西可以直接放在箱子里, js中的几个基本的数据类型就像小小的东西一样可以直接扔在箱子里.

基本数据类型: Undefined, Null, Boolean, Number, String.


这些可以直接扔在箱子里的 数值, 我们就叫他 基本类型值.

var foo = '123'; //String
var bar = 123; //Number

二. 引用类型值

有时候,稍微大一点的东西箱子已经放不下了,它只能找另外的地方放着.但是,就算放不下,我们也要假设这个箱子放着他,于是只能给它贴上一个标签~


(我的大大大大大鸡腿箱子放不下了,就贴个标签说它放在冰箱里吧)

也就是说,箱子里没有存着真的东西, 他只是存着一张纸,一张写着东西放在哪里的纸.

这些需要写一张纸指示放在哪里的 数值, 我们就叫他 引用类型值

通常 引用类型值 指的是 由多个值组成的对象.(也就是太大啦~箱子放不下啦)

var tricky = {}; //创建一个对象
// ↓ - 由多个值组成的对象
tricky.name = 'tricky';
tricky.sex = 'male';

复制变量值

复制变量值给人 造成的感觉 会因为变量值类型的不同而不同.但实质上用箱子比喻来理解的话就容易理解多了

复制变量就是把箱子里的东西复制到另一个新的箱子里.

基本类型值复制变量之后,新变量内容如果有改变,并不会影响到原来的变量.

也就是说,新的箱子里面的东西变了,并不会影响到原来的箱子

var str= "123";
var newstr = str; //复制变量
var newstr = "456"; //把新的变量的值改变了一下
alert(str); //还是输出123,而并不是456.

而如果是引用类型值进行变量复制, 它复制的并不是箱子里所谓藏的什么东西,而是指示着东西放在哪里的那张纸.

也就是说,我把冰箱里的大鸡腿换成了大菠萝,那原来箱子里的东西就会变成大菠萝了!

var fridge = {};
fridge.content = "big drumstick"; //冰箱里藏着大鸡腿
var newfridge = fridge; //看起来是把鸡腿传递过去,实际上是传递了 指示鸡腿在哪里的纸.
newfridge.content = "pineapple"; //把冰箱里的鸡腿换成菠萝
alert(fridge.content); //输出 "pineapple" 而不是 "big drumstick".

传递参数

终于可以讲我们标题所要说的东西了.

JS中所有函数的参数都是按值传递的 -- 这是一句正确而不严谨的结论

按值传递(call by value)引用传递(call by reference) 都是一种 求值策略(Evaluation Strategy), 它决定变量之间,以及函数调用时实参和形参之间值是如何传递的.

当你为一个函数传递参数的时候,到底是 按值传递 还是 引用传递 呢?

按值传递

基本类型的变量值在给函数传递参数的时候使用的就是 按值传递.
按值传递: 函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。

用我们箱子的比喻来讲,就是直接把内容copy一份放到新的箱子里,新箱子里的东西虽然和旧箱子里的东西一样但是毫不相关,就算你换掉新箱子里的东西也影响不到旧箱子里的东西

function changestr(str){
    str = "456";
    return str;
}
var s = "123";
var result = changestr(s);
alert(s); //123 , 并不会因为上一句话而改变了原来的值
alert(result); //456

按引用传递

我们来看看引用类型的值给函数传递参数的时候是不是 按引用传递.
按引用传递: 函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。

用我们箱子的比喻来讲,就是把这个箱子放在镜子面前,假设你拿得到镜子里面的东西哈,你改变了镜子里面的东西,镜子外面的东西也会被改变.

function setContent(obj){
    obj.content = "pineapple";
}
var fridge = {};
fridge.content = "big drumstick";
setContent(fridge);
alert(fridge.content); //输出 pineapple

这个看起来好像就是按引用传递,毕竟改了 新箱子obj 的内容它直接影响到了 旧箱子fridge 了.
但是..

function setContent(obj){
    obj.content = "pineapple";
    obj = {}; //掏空箱子
    obj.content = "apple"; //重新赋值
}
var fridge = {};
fridge.content = "big drumstick";
setContent(fridge);
alert(fridge.content); //输出 pineapple,而不是apple

咦,怎么旧箱子的内容不会随着改变呢?原因是 js里面的引用类型的变量传递实际上是使用了 按共享传递(call by sharing)

这种策略是这样的:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)

其实我觉得用箱子更容易理解,就是把箱子里指示东西放在哪里的纸放在了新的箱子里.一旦纸发生了变化,原来纸上所指示的东西并不会发生变化.


再回过头看上面那段代码, obj = {}; 这一句其实就是切断了 箱子 和 冰箱 之间的联系.它以后发生任何改变都不会影响到旧箱子里的东西了.而是影响到桌子上的东西了.然而, 冰箱 和 旧箱子之间还是存在联系的.所以输出原来的值.

结论

好像资料和资料之间还是存在争议的.但是能理解他们的效果和作用就行了嘛~

在我看来,用箱子来比喻的话好像就没有了什么按引用传递或者按共享传递了.

这一切,看起来都像 按值传递.
(只不过值的内容一个是真正的内容一个是"纸")

  JavaScript 前端