首页>前端教程>JavaScript教程

JavaScript基础12:函数传参

如果函数只是一段重复执行的代码块,那就不够强大了,最多就是节约了一点重复代码。

函数的强大在于通过获得不同的参数,从而得到不同的结果。

一、函数的参数

1、形参和实参

function functionName(parameter1, parameter2, parameter3) {
  // code to be executed
}

在声明函数的时候,定义的参数parameters在函数中充当占位符(也叫形参)的作用,参数可以为一个或多个。

调用一个函数时所传入的参数为实参,实参决定着形参真正的值。

function sum(a,b){
    return a+b;
}
sum(5,10);

a,b就是parameters形参,5,10就是实参,传递了具体的值。

2、参数的缺省

如果函数声明的时候定义了两个形参,但是传参的时候只传了一个参数,那么另一个参数就是undefined。

function sum(a,b){
    if(b===undefined){
      b=0;
    }
    return a+b;
}
sum(5);

在ES6中,可以在函数声明的时候指定默认值。

function (a=1, b=1) { // function code }

3、arguments对象

javascript函数有一个称为arguments对象的内置对象。

argument对象包含了调用函数时所有的实参集合。是传递给函数(并由函数接收)的实际值。

如果使用过多的参数(多于声明的参数)调用函数,则可以使用Arguments对象访问这些参数。

console.log(sum(1,2,3)); // 3
function sum(a,b){
[...arguments].forEach(element => {
console.log(element);  //1,2,3
});
return a+b;
}

声明函数的时候没有参数,实际调用的时候传递了参数,都可以通过Arguments对象访问传递过去的参数。

x = sumAll(1, 123, 500, 115, 44, 88);

function sumAll() {
  var i;
  var sum = 0;
  for (i = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }
  return sum;
}

4、参数通过值传递,对象通过引用传递

如果参数是基本数据类型:

JavaScript 参数通过值传递:函数只知道值,而不是参数的位置。

函数更改了参数的值,却不会更改参数的原始值。

对参数的更改在函数外部不可见。

如果参数是对象类型:

对象是通过变量的引用找到值的。

如果函数改变了对象属性,它也改变了原始值。

对象属性的改变在函数之外是可见的。

要理解这个概念,需要先理解JavaScript的数据类型,基本数据类型,也可以叫原始值的类型,有Number、String、Boolean、Null、 Undefined、Symbol(ES6)。而复杂数据类型,Object(在JS中除了原始数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象),是一种引用数据类型。

基本数据类型(存放在栈(stack)中)

基本数据类型是指存放在栈中的简单数据段,数据大小确定,内存空间大小可以分配,它们是直接按值存放的,所以可以直接按值访问。

下图演示了这种原始数据类型赋值的过程:

栈内存.png

如果从一个变量向另一个变量复制基本类型的值时,会将前者的值克隆一个,然后将克隆的值赋值到后者,因此这两个值是完全独立的,只是他们的value相同而已。

修改任何一个都不会影响另一个。

引用数据类型(存放在堆内存中的对象,每个空间大小不一样,要根据情况进行特定的配置)

引用类型是存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。

引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存在堆内存中的对象。

var obj1=new Object();
var obj2=obj1;
obj2.name="小张";
console.log(obj1.name);// 小张

说明这两个引用数据类型指向了同一个堆内存对象。obj1赋值给obj2,实际上这个堆内存对象将栈内存的引用地址复制了一份给了obj2,但是实际上他们共同指向了同一个堆内存对象,所以修改obj2其实就是修改那个对象,所以通过obj1访问也能访问的到。

堆内存.png

var a = [1,2,3,4,5];
var b = a;//传址 ,对象中传给变量的数据是引用类型的,会存储在堆中;
var c = a[0];//传值,把对象中的属性/数组中的数组项赋值给变量,这时变量C是基本数据类型,存储在栈内存中;改变栈中的数据不会影响堆中的数据
alert(b);//1,2,3,4,5
alert(c);//1
//改变数值 
b[4] = 6;
c = 7;
alert(a[4]);//6
alert(a[0]);//1

从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。

这就是传值与传址的区别。因为a是数组,属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。

堆内存和栈内存.png


最后: 总结基本数据类型和引用数据类型区别

1、声明变量时内存分配不同

 *原始类型:在栈中,因为占据空间是固定的,可以将他们存在较小的内存中-栈中,这样便于迅速查询变量的值

 *引用类型:存在堆中,栈中存储的变量,只是用来查找堆中的引用地址。

    这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响

2、不同的内存分配带来不同的访问机制

    在javascript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的按引用访问。

    而原始类型的值则是可以直接访问到的。

3、复制变量时的不同

 1)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的value而已。

2)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,

    也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。

    (这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)。多了一个指针

4、参数传递的不同(把实参复制给形参的过程)

首先我们应该明确一点:ECMAScript中所有函数的参数都是按值来传递的。

  但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还不就是因为内存分配时的差别。  

  1)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。

  2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!

因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。

参考文章:https://www.cnblogs.com/c2016c/articles/9328725.html

点赞


1
保存到:

相关文章

Top