JavaScript中值传递,引用传递,函数传参,你学懂了吗?

JavaScript中值传递,引用传递,函数传参,你学懂了吗?

快来补习基础知识

JS类型

JavaScript中5+1种基本数据类型

  StringNumberBooleannullundefined、(Symbol:ES6新基本数据类型)

引用类型

  除了上面的六种基本类型之外,其他的类型如ArrayFunctionObjectDateRegExp等都是引用类型。他们都通过引用传递,在技术底层上都是对象。

基本数据类型的值传递

  如果一个基本的数据类型绑定到某个变量,我们可以认为该变量包含这个基本数据类型的值。

var x = 10;
var y = "abc";
var z = null;

  当我们使用=将这些变量赋值到另外的变量,实际上是将对应的值拷贝了一份,然后赋值给新的变量。我们把它称作值传递。

var x = 10;
var y = "abc";

var a = x;
var b = y;

console.log(x, y, a, b); // 10, 'abc', 10, 'abc'

  ax都包含 10,by都包含’abc’,并且它们是完全独立的拷贝,互不干涉。如果我们将a的值改变,x不会受到影响。

var x = 10;
var y = "abc";
var a = x;
var b = y;
a = 5;
b = "def";
console.log(x, y, a, b); // 10, 'abc', 5, 'def'
  • 栈内存中包括了变量的标识符和变量的值

基本类型传值内部原理

引用类型的值传递

  对于引用类型来说,也是值传递,不过传递的这个“值”是引用类型实例的地址。

//创建一个数组对象,地址=[0X001]
var obj = [1];

//将obj2 = [0x001]
var obj2 = obj;

//给[0x001]代表的对象执行push()
obj.push(2);

//所以二者输出的是一样的值,因为同时引用的是同一个对象
console.log(obj);
console.log(obj2);
  • 栈内存中保存了变量标识符和指向堆内存中该对象的指针
  • 堆内存中保存了对象的内容

引用类型传值内部原理

函数参数

  当我们对函数进行传参时,函数会将这些数据拷贝赋值给函数的参数变量:

  • 对于基本数据类型的参数,传递的是变量的值。
  • 对于引用类型的参数来说,传递的是对象的地址。

纯函数

  纯函数:给定一个函数,给定输入,返回唯一输出,除此之外不会对外部环境产生任何附带影响。所有函数内部定义的变量在函数返回后都被垃圾回收掉。

  但是如果函数传递的是一个对象,则传入的是一个地址值,则该变量的操作会影响到原本的对象,这样的编程手法将产生附带影响,使代码逻辑复杂和可读性变低。

  为此,数组函数,比如Array.mapArray.fiter以纯函数出现。虽然参数是一个数组变量,但在内部通过深度拷贝并赋值给一个新变量,然后在新数组上操作,以防止原始数组被更改。

  来看一个例子:

function changeAgeImpure(person) {
    person.age = 25;
    return person;
}
var alex = {
    name: "Alex",
    age: 30
};
var changedAlex = changeAgeImpure(alex);
console.log(alex); // { name: 'Alex', age: 25 }
console.log(changedAlex); // { name: 'Alex', age: 25 }

  在非纯函数changeAgeImpure中,将对象personage更新并返回。原始的alex对象也被影响,age更新为 25。

  让我们来看如何实现一个纯函数:

function changeAgePure(person) {
    var newPersonObj = JSON.parse(JSON.stringify(person));
    newPersonObj.age = 25;
    return newPersonObj;
}
var alex = {
    name: "Alex",
    age: 30
};
var alexChanged = changeAgePure(alex);
console.log(alex); // { name: 'Alex', age: 30 }
console.log(alexChanged); // { name: 'Alex', age: 25 }

  我们通过JSON.sringify将对象变为一个字符串,然后再通过JSON.parse将字符串变回对象。通过该操作会生成一个新的对象。


挑战一道经典面试题

  值传递和引用传递是在js中极为重要知识,下面一道面试题检测你是否真正理解这个知识:

function changeAgeAndReference(person) {
    person.age = 25;
    person = {
        name: "John",
        age: 50
    };

    return person;
}
var personObj1 = {
    name: "Alex",
    age: 30
};
var personObj2 = changeAgeAndReference(personObj1);
console.log(personObj1); // -> ?
console.log(personObj2); // -> ?

题解

  如果这道题你没有正确做出来,那你就有必要补习一下上面的基础知识。

//创建一个对象,地址[0x001]
var personObj1 = {
    name: 'Alex',
    age: 30
};
//将地址[0x001]通过personObj1传到函数内
var personObj2 = changeAgeAndReference(personObj1);

function changeAgeAndReference(person) {
    //arguments[0]:var person = [0x001];即person = personObj1的内存地址

    //将[0x001].age=25;
    person.age = 25;

    //arguments[0]开辟了新的堆内存空间[0x002],此时person = [0x002];
    person = {
        name: 'John',
        age: 50
    };

    //如果此时执行这条语句,则[0x002].age = 99;
    //person.age=99;

    //返回person,即[0x002];
    return person;
}

//[0x001]={ name: 'Alex', age: 25 }
console.log(personObj1);

//[0x002]={ name: 'John', age: 99 }
console.log(personObj2);

总结

  • 基本数据类型传递变量值,引用类型传递地址值,二者都是值传递。
  • 函数传参遵循上条规则。
  • 如果你是急于求成直接看总结的:学习没有捷径,请一步一个脚印!
发布了122 篇原创文章 · 获赞 25 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/HuoYiHengYuan/article/details/104607685