ECMAScript变量 - 传递参数 (高频面试点)

简介: ECMAScript变量 - 传递参数 (高频面试点)

ECMAScript中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。


如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样.对很多开发者来说,这一块可能会很不好理解,毕竟变量有按值和按引用访问,而传惨则只有按值传递。


在按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用ECMAScript的话说,就是argument对象中的一个槽位)。在按引用传递参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反映到函数外部。(这在ECMAScript中是不可能的。)


来看下面这个例子:


function addTen(num) {
    num += 10
    return num
}
let count = 20
const result = addTen(count)
console.log(count)  // 20 没有变化
console.log(result) // 30
复制代码


这里,函数addTen()有一个参数num,它其实是一个局部变量。在调用时,变量count作为参数传入.count的值是20,这个值被复制到参数num以便在addTen()内部使用。在函数内部,参数num的值被加上了10,但这不会影响函数外部的原始变量count。参数num和变量count互不干扰,它们只不过碰巧保存了一样的值。如果num是按引用传递的,那么count的值也会被修改为30.这个事实在使用数值这样的原始值时是非常明显的。但是,如果变量中传递的是对象,就没那么清楚了。


比如,再看这个例子:


function setName(obj) {
    obj.name = '何小生'
}
let person = new Object()
setName(person)
console.log(person.name)  // '何小生'
复制代码


这一次,我们创建了一个对象,并把它保存在变量person中。然后,这个对象被传给setName()方法,并被复制到参数obj中。在函数内部,objperson都指向了同一个对象。结果就是,即使对象是按值传进函数的,obj也会通过引用访问对象。当函数内部给obj设置了name属性时,函数外部的对象也会反映这个变化,因为obj指向的对象保存在全局作用域的堆内存上。很多开发者错误地认为,当在局部作用域中修改而变化反映到全局时,就意味着参数是按引用传递的。为证明对象是按值传递的,我们再来看看下面这个修改后的例子。 例子:


function setName(obj) {
    obj.name = '何小生'
    obj = new Object()
    obj.name = '小生'
}
let person = new Object()
setName(person)
console.log(person.name)   // '何小生'
复制代码


这个例子前后唯一的变化就是setName()中多了两行代码,将obj重新定义为一个有着不同name的新对象。当person传入setName()时,其name属性被设置为何小生。然后变量obj被设置为一个新对象且name属性被设置为小生。如果person是按引用传递的,那么person应该自动将指针改为指向name小生的对象。可是,当我们再次访问person.name时,它的值时何小生。这表明函数中参数的值改变之后,原始的引用仍然没变。当obj在函数内部被重写时,它变成了一个指向本地对象的指针。而那个本地对象在指向函数结束时就被销毁了。


注意:ECMAScript中函数的参数就是局部变量


总结小例子:


//数组
function setArray(arr) {
    arr[0] = 1;
    var arr = new Array(); //这里的地址改变了
    arr[0] = 6;
    return arr;
}
var lastArr = new Array();
lastArr[0] = 0;
var newArr = setArray(lastArr);
console.log(lastArr[0] + '|' + newArr[0]); // 1|6
//对象
function setNameAgain(obj) {
    obj.name = 'aaa';
    var obj = new Object(); // 如果是按引用传递的,此处传参进来obj应该被重新引用新的内存单元
    obj.name = 'ccc';
    return obj;
}
var person = new Object();
person.name = 'bbb';
var newPerson = setNameAgain(person);
console.log(person.name + ' | ' + newPerson.name); //aaa | ccc
复制代码


在向参数传递基本类型的时候,被传递的值会被复制给一个局部变量(arguments对象中的一个元素)。

在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部!

重点时需要搞清楚引用类型的地址与指向地址的指针!!!

相关文章
|
7月前
【变态面试题】【两种解法】不能创建临时变量(第三个变量),实现两个数的交换
【变态面试题】【两种解法】不能创建临时变量(第三个变量),实现两个数的交换
56 0
【变态面试题】【两种解法】不能创建临时变量(第三个变量),实现两个数的交换
|
存储 自然语言处理 算法
ES高频面试问题:一张图带你读懂 Elasticsearch 中“正排索引(正向索引)”和“倒排索引(反向索引)”区别
ES高频面试问题:一张图带你读懂 Elasticsearch 中“正排索引(正向索引)”和“倒排索引(反向索引)”区别
ES高频面试问题:一张图带你读懂 Elasticsearch 中“正排索引(正向索引)”和“倒排索引(反向索引)”区别
|
4月前
|
Java
【Java基础面试七】、请介绍一下实例变量的默认值
这篇文章介绍了Java中实例变量的默认值:引用数据类型的默认值是null,而基本数据类型的默认值根据其类型分别是0、0L、0.0F、0.0、'\u0000'和false。
【Java基础面试七】、请介绍一下实例变量的默认值
|
4月前
|
Java 编译器
不同变量的赋值时机 | 常见的面试题 | 静态代码块
这篇文章讨论了Java中不同变量的赋值时机,包括基本数据类型、引用数据类型、类变量、实例变量和局部变量,并解释了静态代码块、代码块和构造函数的执行顺序。
不同变量的赋值时机 | 常见的面试题 | 静态代码块
【IO面试题 五】、 Serializable接口为什么需要定义serialVersionUID变量?
serialVersionUID用于标识类的序列化版本,确保在反序列化时类的版本一致性,避免因类定义变更导致的不兼容问题。
|
5月前
|
存储 设计模式 监控
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
53 0
|
5月前
|
存储 JavaScript 前端开发
面试官:JS中变量定义时内存有什么变化?
面试官:JS中变量定义时内存有什么变化?
45 0
|
7月前
|
Python
2024年最新【Python】变量 的定义和使用,阿里巴巴蚂蚁金服面试流程
2024年最新【Python】变量 的定义和使用,阿里巴巴蚂蚁金服面试流程
2024年最新【Python】变量 的定义和使用,阿里巴巴蚂蚁金服面试流程
|
7月前
|
存储 JavaScript 前端开发
每日一道javascript面试题(九)函数的参数可以和函数体中的变量重名吗
每日一道javascript面试题(九)函数的参数可以和函数体中的变量重名吗
|
存储 Java
第一季:1自增变量【Java面试题】
第一季:1自增变量【Java面试题】
65 0