# (八)参数传递

本篇文章主要讲的是JavaScript中最正常不过的现象——函数参数传递,本篇文章篇幅不长,但一定能引发属于你自己的思考! 大家可能会发现,系列的最近几篇文章都围绕着函数来讲,毕竟作为Js中的一等公民,它无处不在;

# 目录

前言

目前,前端内功系列已经是第八篇了,也想听听大家的意见,无论是写作风格和之后几篇文章的侧重点,大家都可以提哦~

在研究这个问题之前,大家可以回忆一下,Js基本的数据类型有哪些?如果分类?

  • 原始数据类型值 primitive type,比如Undefined,Null,Boolean,Number,String
  • 引用类型值,也就是对象类型 Object type,``比如Object,Array,Function,Date`等。

为什么这么分类?

因为声明变量时不同的内存分配:

  • 原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。它可以直接存储,是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 中。这样存储便于迅速查寻变量的值。

  • 引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。你可以想像成房间内放着你需要的物品,而你手里拿着房间的钥匙。这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,通过记录钥匙就可以找到对应存储的数据。是的存储钥匙地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

一、值传递

在红宝书中,曾经提到:ECMAScript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。

基本类型值的传递如同基本类型变量的复制一样:

function addTen(num) {
    num += 10;
    return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20,没有变化
alert(result); //30

这样的结果大家应该都理解,被传入函数的数据并没被修改,

二、引用传递

参数传递的另一种传递方式——引用传递:函数接收的不是值的拷贝,而是对象的隐式引用。(因为拷贝复杂的数据结构会在性能上产生问题),我们来看下面的代码。

var group = {
    num: 10
}
function func(obj) {
    obj.num += 10;
    console.log('obj:', obj);
}
func(group); // { num: 20 }

我是这样理解的:

  1. 实际上是给了函数一把钥匙A钥匙A上记录着哪个房间保存着函数需要的东西,函数就配了一把一摸一样的钥匙B
  2. 之后函数用钥匙B打开了对应的房间,对房间的物品进行了改变,离开房间;
  3. 转过身来你又用钥匙A打开了这个房间,你意识到了函数其实改变了房间的布局;
  4. 而为什么是配了一把钥匙呢?显然配一把钥匙比建造一间一模一样的房间,要简单靠谱的多吧?

# 一句话概括本节

按引用传递:函数内部对参数的任何改变都是影响该对象在函数外部的值,因为两者引用的是同一个对象,也就是说:这时候参数就相当于外部对象的一个别名。

三、共享传递(call by sharing)

该策略是1974年由Barbara Liskov为CLU编程语言提出的。

**该策略的要点是:**函数接收的是对象对于的拷贝(副本),该引用拷贝和形参以及其值相关联。

这里出现的引用,我们不能称之为“按引用传递”,因为函数接收的参数不是直接的对象别名,而是该引用地址的拷贝。

**最重要的区别就是:**函数内部给参数重新赋新值不会影响到外部的对象(和上例按引用传递的case),但是因为该参数是一个地址拷贝,所以在外面访问和里面访问的都是同一个对象(例如外部的该对象不是想按值传递一样完全的拷贝),改变该参数对象的属性值将会影响到外部的对象。

我们来看下面的例子:

var obj = {
    value: 1
};
function foo(o) {
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

上面的例子就是传入Object类型的但结果却和引用传递不同。

有很多开发人员(包括我)错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来, 就说明参数是按引用传递的。我们再看一看《你不知道的Js》中的例子:

function setName(obj) {
   obj.name = "余光";
   obj = new Object();
   obj.name = "未知";
}
var person = new Object();
setName(person);
alert(person.name);  // 余光

setName()函数中添加了两行代码:

  1. obj 重新定义了一个对象;
  2. 为该对象定义了一个带有不同值的 name 属性;
  3. obj.name 属性设置为'未知'(注意此时obj和外部传入的obj有哪些联系?)

为什么会这样?

  1. 虽然在函数内部修改了参数的值,但原始的引用仍然保持未变。
  2. 这是因为,当在函数内部重写 obj 时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。

# 一句话概括本节

共享传递不可能去解除引用和改变对象本身,但可以去修改该对象的属性值。

四、总结

我们来总结一下前面几节最核心的内容:

写在最后

在这里插入图片描述

写在最后

# 参考

写在最后

JavaScript内功系列:

  1. this、call、apply详解,系列(一) (opens new window)
  2. 从原型到原型链,系列(二) (opens new window)
  3. 从作用域到作用域链,系列(三) (opens new window)
  4. JavaScript中的执行上下文(四) (opens new window)
  5. JavaScript中的变量对象(五) (opens new window)
  6. JavaScript之自执行函数表达式(六) (opens new window)
  7. JavaScript中的闭包,给自己一场重生(七) (opens new window)
  8. 本文
  9. 下篇预告:Js中的数据类型都有哪些?

关于我

  • 花名:余光
  • WX:j565017805
  • 沉迷JS,水平有限,虚心学习中

其他沉淀

如果您看到了最后,不妨收藏、点赞、评论一下吧!!! 持续更新,您的三连就是我最大的动力,虚心接受大佬们的批评和指点,共勉!

Last Updated: 2/14/2023, 2:43:41 PM