百科问答小站 logo
百科问答小站 font logo



JavaScript中对 function 的参数进行重新赋值的影响? 第1页

  

user avatar   rednaxelafx 网友的相关建议: 
      

题主所说的代码形式就是为了在语言层面不支持默认参数(default parameter)功能时的变通做法。正因为它的意图是“在参数没有得到赋值时给它一个默认值”,所以常常会赋值回到原本名字的参数上(而不是新开一个别的名字的局部变量)。

在ES2015(ES6)语言层面添加了对默认参数的支持后,这种做法的必要性就大幅降低了。可参考MDN文档的介绍:

Default parameters

注意上面MDN文档中给出的使用ES6语言层面默认参数之前的解决办法:

       function multiply(a, b) {   var b = typeof b !== 'undefined' ?  b : 1;    return a*b; }  multiply(5); // 5      

这个参数检查显然比题主给的例子的“param = param || {}”更复杂。

MDN的范例版本反映了ES6语言层面的默认参数的语义——只有当传入的参数为语言内建的undefined值时才使用默认参数值,否则使用传入的参数;

而题主给的例子则是当传入参数为任意假值(undefined、null、0等等)时都采用默认参数值。这比MDN的范例的限制更宽松,因而也更有可能出现意想不到的情况。谨慎使用。

如果一个实际场景就是期待传入的参数是个对象,如题主的例子那样,那么直接用 || {}其实也够用——假值都过滤掉就正好符合需求。

=====================================

题主所说的第1和第2种情况其实没有任何区别。要干净一点的话第1种更干净。

请参考

Annotated ES5 - 10.5 Declaration Binding Instantiation

中的第4.d点和第8点。第4.d点讲解了函数的参数被放进环境的过程,而第8点则讲解了局部变量被创建和放进环境的过程——注意它强调只有当环境中一个名字还没有被声明的时候,才会创建该名字的局部变量。

(注意关于varAlredyDeclared的部分。这个规定保证了同一作用域内var变量不会被重复声明——重复的声明会等价于在开头只声明了一次。)

换句话说,这个规定也就指定了JavaScript的变量声明提升(variable declaration hoisting)的语义。在一个函数里,一个名字的变量无论被用var声明多少次,它们实际语义都等价于被合并在一起提升到函数开头的地方声明。

在声明的意义上,函数的参数就跟局部变量一样,只不过函数的参数早于所有局部变量的声明而被记录进环境里。这就是为什么题主给的第2种代码实际上跟第一种代码的意思是完全一样的——后面的var param并不会重复声明变量,也不会声明一个新的同名局部变量去遮蔽之前参数所声明的那个。

特别注意“声明”(declaration)和“定义”(definition)是不同的,前者只负责变量名在环境中的存在与否,而后者才是真正的赋值动作。举例说:

       function foo() {   var a = 1; // declaration + definition   var a;     // declaration only   var a = 3; // declaration + definition   a = 4;     // definition only }      

根据JavaScript的规定,带有初始化表达式(initialiser)的变量声明既是声明也是定义。在变量声明提升后,上述代码的实际效果跟下面的代码是一样的:

       function foo() {   var a; // declaration (hoisted)   a = 1; // definition   // var a; // (hoisted)   a = 3; // definition   a = 4; // definition }      

留意这里变量声明经过提升就只有一份有效果了,但变量定义(赋值)的每一份都还在其原本的位置上起作用。可以参考

Annotated ES5 - 12.2 Variable Statement

的规定:

A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.

=====================================

另外要注意在ES2015(ES6)里的let/const声明与之前的var声明的规定不一样了,var声明的局部变量还是函数作用域的,而let/const声明的binding则是词法块作用域的。但在合并声明的意义上let/const与var有相似之处——都会检测作用域里是否已经有该名字的变量声明。

var在同一作用域里的多个同名声明会被合并,而不会创建新变量或遮蔽之前的同作用域同名变量;

而let/const则会检测当前作用域里是否已经有该名字的声明,并且拒绝重复的声明。

在JavaScript这种闭包实现了引用捕获的语言里,要验证多个声明是否创建了多个变量很简单——让多个闭包捕获变量,最后看它们捕获的值是否相同:

       function foo() {   var a = 1   var f1 = function () { return a }   var a = 2   var f2 = function () { return a }   return {     f1: f1,     f2: f2   } } var o = foo() console.log(o.f1()) // 2 console.log(o.f2()) // 2      

可以看到foo()里的f1与f2嵌套函数捕获到的a是同一个变量a,所以它们后面被调用时返回的a值一样。

如果把这个例子的变量a声明改为用let声明,

       function foo() {   let a = 1   var f1 = function () { return a }   let a = 2   let f2 = function () { return a } // Uncaught SyntaxError: Identifier 'a' has already been declared   return {     f1: f1,     f2: f2   } }      

则该函数被加载时就会报错,拒绝同一作用域里同名变量的重复声明。

参数的处理同理:

       function bar(x) {   let x; // Uncaught SyntaxError: Identifier 'x' has already been declared }      



  

相关话题

  大一新生如何自学JavaScript? 
  x^y=y^x,(x<y)如果用大学知识如何解? 
  已经 2022 年了,学习 JavaScript 继承方式的意义是什么? 
  向量组等价时其秩一定想等吗? 
  C、C++、Java、JavaScript、PHP、Python、Ruby 这些语言分别主要用来开发什么? 
  既然引用计数在做 GC 时有性能问题,为什么智能指针会广泛应用它? 
  如何看待 TS 团队发起的 「JS 类型标注」提案 Types as Comments? 
  为什么很多国内公司不使用 jQuery 等开源 JS 框架(库),而选择自己开发 JavaScript 框架? 
  你见过哪些美妙的函数图像? 
  如何求这个复杂的函数的不定积分? 

前一个讨论
#define 不是简单的替换吗,为什么下面的代码错误?
下一个讨论
如何看待朱颜科普相对论?





© 2024-11-22 - tinynew.org. All Rights Reserved.
© 2024-11-22 - tinynew.org. 保留所有权利