资讯专栏INFORMATION COLUMN

JavaScript中的求值策略

MrZONT / 2447人阅读

摘要:关于的求值策略,问中函数的参数传递是按值传递还是按引用传递回答很经典。所以不能说中函数的参数传递严格按值传递或按引入传递。中还采用一种参数传递策略,叫按共享传递。中参数是必须先求值再作为实参传入函数的。参考求值策略中函数参数的默认值

最近在研究 lambda 演算中的 η-变换 在 JavaScript 中的应用,偶然在 stackoverflow 上看到一个比较有意思的问题。关于 JavaScript 的求值策略,问JS中函数的参数传递是按值传递还是按引用传递?回答很经典。

一栗以蔽之
function changeStuff(a, b, c) {
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);         // 10
console.log(obj1.item);   // changed
console.log(obj2.item);   // unchanged

如果说JS中函数的参数传递是按值传递,那么在函数changeStuff内部改变b.item的值将不会影响外部的obj1对象的值。

如果说JS中函数的参数传递是按引入传递,那函数changeStuff内部所做的改变将会影响到函数外部所有的变量定义,num将会变成100、obj2.item将会变成changed。很显然实际不是这样子的。

所以不能说JS中函数的参数传递严格按值传递按引入传递。总的来说函数的参数都是按值传递的。JS中还采用一种参数传递策略,叫按共享传递。这要取决于参数的类型。

如果参数是基本类型,那么是按值传递的;

如果参数是引用类型,那么是按共享传递的。

参数传递
ECMAScript 中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。-- 《JavaScript高级程序设计》

红宝书上讲所有函数的参数都是按值传递的,到底是不是呢?让我们分析下上面的栗子:

按值传递

JavaScript中基本类型作为参数的策略为 按值传递(call by value):

function foo(a) {
  a = a * 10;
}

var num = 10;

foo(num);

console.log(num); // 10 没有变化

这里看到函数内部参数的改变并没有影响到外部变量。按值传递没错。

按共享传递

JavaScript中对象作为参数传递的策略为 按共享传递(call by sharing):

修改参数的属性将会影响到外部对象

重新赋值将不会影响到外部对象

按上面栗子函数内部修改了参数b的属性item,会影响到函数外部对象,因而obj1的属性item也变了。

function bar(b) {
  b.item = "changed";
  console.log(b === obj1) // true
}

var obj1 = {item: "unchanged"};

bar(obj1);

console.log(obj1.item);   // changed 修改参数的属性将会影响到外部对象

b === obj1打印结果为true可以看出,函数内部修改了参数的属性并没有影响到参数的引用。bobj1共享一个对象地址,所以修改参数的属性将会影响到外部对象。

而将参数c重新赋值一个新对象,将不会影响到外部对象。

function baz(c) {
  c = {item: "changed"};
  console.log(c === obj2) // false
}

var obj2 = {item: "unchanged"};

baz(obj2);

console.log(obj2.item);   // unchanged 重新赋值将不会影响到外部对象

将参数c重新赋值一个新对象,那么c就绑定到了一个新的对象地址,c === obj2打印结果为false,判断他们不再共享同一个对象地址。它们各自有独立的对象地址。所以重新赋值将不会影响到外部对象。

总结

可以说 按共享传递按值传递 的特例,传递的是引用地址的拷贝。所以红宝书上说的也没错。

可以把 ECMAScript 函数的参数想象成局部变量。-- 《JavaScript高级程序设计》
延伸 - 惰性求值

前面了解到了所有函数的参数都是按值传递的。JavaScript 中参数是必须先求值再作为实参传入函数的。但是在ES6中有一个特例。

参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。 -- 《ECMAScript 6 入门》
let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101

上面代码中,参数p的默认值是x + 1。这时,每次调用函数foo,都会重新计算x + 1,而不是默认p等于 100。

参考

求值策略

Is JavaScript a pass-by-reference or pass-by-value language?

ES6 中函数参数的默认值

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92452.html

相关文章

  • 「译」代码优化策略 — Idle Until Urgent

    摘要:我将这个策略称之为闲置直到紧急。请注意,在脚本执行时,它作为单个任务需要毫秒才能运行完成。很明显,解决方案是将这些代码分解为多个任务。原因如下推迟组件初始化仅在组件尚未渲染时才有用。这称为输入优先级。 showImg(https://img.alicdn.com/tfs/TB1u.rsepzqK1RjSZFzXXXjrpXa-1919-913.png); Idle Until Urge...

    Ashin 评论0 收藏0
  • JS中的值是按值传递,还是按引用传递呢?

    摘要:按引用传递时,函数的形参接收实参的隐式引用,而不再是副本。探究值的传递方式的基本类型,是按值传递的。但这样是否说明的对象是按引用传递的呢我们再看下面的例子仍然是并未被修改为如果是按引用传递,修改形参的值,应该影响到实参才对。 最近遇到个有趣的问题:JS中的值是按值传递,还是按引用传递呢? 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(ca...

    Jochen 评论0 收藏0
  • 【译】小二百行 JavaScript 打造 lambda 演算解释器

    摘要:在开始解析之前,先通过词法分析器运行源码,这会将源码打散成语法中全大写的部分。我们基于每个规则的名称的左侧为其创建一个方法,再来看右侧内容如果是全大写的单词,说明它是一个终止符即一个,词法分析器会用到它。 本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/661原文:http://tadeuzagallo.com/blog/writing-a-l...

    KitorinZero 评论0 收藏0
  • JavaScript 编程精解 中文第三版 十二、项目:编程语言

    摘要:来源编程精解中文第三版翻译项目原文译者飞龙协议自豪地采用谷歌翻译部分参考了编程精解第版确定编程语言中的表达式含义的求值器只是另一个程序。若文本不是一个合法程序,解析器应该指出错误。 来源:ApacheCN『JavaScript 编程精解 中文第三版』翻译项目原文:Project: A Programming Language 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用...

    Near_Li 评论0 收藏0
  • JavaScript程序设计》—— 第四章 表达式语句

    摘要:每个候选项都是大括号中的语句序列。短路运算符有一个很重要的功能它们并不真的需要布尔值操作数,注意换句话说,不会将数字转换为布尔值。练习对表达式求值。首先对求值,转换为继续对右边表达式求值,为,造成短路,不对进行计算,返回对表达式求值。 4.1 声明语句 声明语句也叫变量语句,这种语句会创建新变量。可以在声明变量时给出初始值,如果没有明确给出,变量的值就是undefined。 ...

    solocoder 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<