变量复制与函数传参

看高程的时候,看到这么一个概念:“ECMAScript 中所有函数的参数都是按值传递” 。对于基本数据类型都好理解,但是对于引用数据类型,当时觉得就是按照引用类型传递的,没有搞懂说的按值传递是什么意思。

直到看技术博客的时候看到这么一句话让我豁然开朗:函数的参数传递,传递的都是值,参数是 Object 类型的也一样,也是值,只不过这个值是地址值。

针对于这个知识点,做了一个扩散,比较详细的分析了这个概念问题,在自己深入理解的同时也希望能帮到同样不是很理解的开发人员

js数据类型

首先,我们知道js存在两大类型的值:基本数据类型,和引用数据类型。

基本数据类型的值包括:String、Number、Boolean、Null、undefined

引用数据类型的值包括:Object、Array、Function

我们可以通过 typeof 来判断当前的值是什么数据类型

var num = 1;
console.log(typeof num);//返回的是number


var str = 'jack';
console.log(typeof str);//返回的是string


var boo =true;
console.log(typeof boo);//返回的是boolean


var und ;
console.log(typeof und);//返回的是undefined


var nul =null;
console.log(typeof null); //返回的是object

可以得出:基本数据类型在调用 typeof 判断类型的时候,除了 null 这个类型会返回 object 类型,其他的都是返回的是自己对应的类型

// 通过构造函数实例化的数组
var arr = new Array();
console.log(typeof arr); //返回的是object


// 通过字面亮创建的数组
var arr2 = [1,32];
console.log(typeof arr2);//返回的是object


var fn = function(){};
console.log(typeof fn); //返回的是function


var obj = new Object();
console.log(typeof obj); //返回的是object


var obj2 = {a:1};
console.log(typeof obj2); //返回的是object

可以得出:复杂数据类型,除了函数 function 调用typeof 的时候,会返回 function ,其他的都会返回 object

变量复制

js规定了,基本数据类型的值,是可以直接通过变量操作这个值的。但是引用数据类型的值,是不能被直接操作的。这是因为,引用数据类型的值,是保存在内存中的,js不能直接访问内存中的某个位置。我们改变这个对象的时候,都是通过引用这个对象在内存中的地址去改变它的, 

先声明一个变量,赋值一个 Number 类型

var num1 = 5

接着声明另一个变量,复制 num1 的值给它

var num2 = num1

 然后改写 num1 的值

num1 = 6

分别打印 num1、num2

 console.log( num1 ) // 6
 console.log( num2 ) // 5

 可以得出:在接收基本类型的值的变量被当作数据赋值给另外的变量的时候,只是单纯复制了这个变量所携带的值,并不是复制了这个变量。这两个变量的值是完全不相关的。

基本类型的值可能比较好理解,但是引用数据类型的值可能比较麻烦一点

先声明一个变量,通过字面量方式赋值一个对象

var obj1 = {"a":"a"}

接着声明一个变量,复制 obj1 的值给它

var obj2 = obj1

给 obj1新增一个属性

obj1.b = "b"

分别打印 obj1、obj2

 console.log( obj1 ) // {a: "a", b: "b"}
 console.log( obj2 ) // {a: "a", b: "b"}

可以得出:在对引用数据类型进行复制然后赋值的过程中,不论是对原来的变量进行修改,还是对赋值的变量进行修改,最后会发现,两个变量都是被修改之后的状态,这说明了,引用数据类型在被复制的过程中,和基本类型数据不一样,并不是单纯的复制了值,而是复制了这个数据的引用地址,指向的都是这个引用数据的内存空间,只要一个变量改变了,那么所有指向这个引用数据的地址的变量都会发生改变

函数形参

在函数中我们肯定知道,一个函数的形参,就相当于在函数顶部声明了一个未赋值的变量,可以举个例子

function setName (obj) {
  console.log("obj:" + obj)
}

setName() // 打印 obj:undefined

明显的,我们这个函数内部只有一个 console ,并没有定义obj 这个变量,但是我打印的时候,并没有报错,而是undefined,这就说明了,obj 这个变量其实是存在的。

往上追溯,函数体内部不存在 obj 这个变量,但是函数的形参内部有一个 obj 的参数,其实就很明显了,这个函数就相当于在函数体内部最顶层的位置,隐式的声明了一个 obj 变量,但是没有赋值,所以打印的时候 obj 变量存在,但是是 undefined

function setName (obj) {
  let obj;  // 隐式的声明,但是不赋值,默认就是undefined

  console.log("obj:" + obj)
}

setName() // 打印 obj:undefined

函数传参-传值

函数的参数传递,传递的都是值,参数是 Object 类型的也一样,也是值,只不过这个值是地址值。

根据上面的情况,下面的传值就好理解一点了,还是先用基本类型说明

通过字面量创建一个 值 为 基本数据类型的字符串 aaaa 的变量

var personal = "aaaa"

然后声明一个函数,且在函数内部重新给 obj 赋值

function setName (obj) {
  obj = "1234"
}

将 变量当作参数传入 到函数中

setName(personal)

打印 personal

alert(personal) // aaaa

按照上面说的函数参数问题,我们可以把这个函数看作是这样的

function setName (obj) {
  var obj;
  obj = "1234"
}

 当我传入定义的 字面量 personal 的时候,其实是复制了 personal 的值,然后传递到函数的时候,形参 obj 就相当于也复制了 personal 的值,然后 继续 赋值给 函数体内部 已经隐式定义但还未赋值 的 obj 变量,此时 obj = "aaaa",当我打印 personal 的时候,personal 的值是肯定没有改变的,但是当我在函数内部打印 obj 的时候,可以看到 函数内部的 obj 是变化了的

var personal = "aaaa"

function setName (obj) {
  var obj ;  // 形参 传递进来之后 obj = “aaaa”
  obj = "1234"; //给 变量 obj 重新赋值
  console.log(obj) // "1234" obj是变化了的
}

setName(personal) 
console.log( personal ) // "aaaa"

可以得出:当接收基础数据类型的值的变量,作为参数传递到函数中的时候,传递的是这个变量的值,并不是这个变量,这个参数一旦作为形参 ,传递到了函数体内部,就和 外面的 变量毫无关系。

 

将引用数据类型,作为参数 传递到函数中,这一类情况可能比较难理解,我们先看第一个例子

var person  = {
    name : "leaf"
};
function obj(o){
    o.name = "kafu";
   return o;
}
var result = obj(person);
console.log(result.name);// kafu
console.log(person.name);// kafu

很起怪的是,这个情况怎么看都是传递的引用啊,personal 的属性 都随之改变了,这说不过去啊。接下来我们看第二个例子,或许你就明白了

var person = {
    name : "leaf"
};
function obj(o){
    o = {
       name : "kafu"
    };
    return o;
}
var result = obj(person);
console.log(result.name);// kafu
console.log(person.name);// leaf

可以看到,personal 的值 并没有随之改变。这么对比,区别就出来了。

第一个例子中,传递了一个对象,然后函数体顶部 声明了 变量 o ,函数执行的时候,o 被赋值为 personal,此时 根据上面的变量复制的结论,这个 personal 实际上还是指向的是 内存中的 唯一地址。将 personal 赋值 给 o 之后,相当于 o 也指向了内存中的 personal 的地址,所以,o 改变了之后,personal 也会改变,看起来就是 按引用传递的, 但是实际上传递的是地址值

但是在第二个例子中,我将 变量 o 重新赋值了,其实就相当于,o 的指向地址已经是一个新的了,不是 personal 的地址,所以改变 o 不会对 personal 造成影响。

其实对于基本类型,参数传递的就是变量的值,对于引用类型,参数的传递是传递对象的地址值。这拷贝地址其实也可以理解为按值传递了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值