关于js函数 形参和局部变量名相同 的问题
原文:https://segmentfault.com/q/1010000007278354?_ea=1295176
问题:
function f1(a) {
console.log(a);// 10; 这里我开始觉得是undefined的
// 我以为var a=1会先把var a=undefined 放在函数的最前面 但是好像并没有
var a=1;
console.log(a);// 1
console.log(arguments[0])// 1; 这里我觉得也是10
}
f1(10)
按照f1这个打印看来 var 声明的a 好像和形参a是有联系的 但是他们不是应该没有联系吗?
function f2(a) {
console.log(a); //10
var a;
console.log(a);//10
console.log(arguments[0])//10
}
f2(10)
f2 里面的a好像什么事都没做
这个问题所造成的结果,主要有两个源头,一个是函数中的arguments对象,另一个是变量在函数中的提升。
函数中的arguments对象
先看arguments对象,它是个函数中的隐性对象,是一个类数组(array-like)的对象。它有一些特性在这里看到,实际都是很细的特性,以下几个例子来说明:
第一个例子是一般函数,只是把传入的形参(参数)输出在主控台:
function foo(a, b){
console.log(a, b); //1, 10
}
foo(1, 10);
a与b改为用arguments,这很正常的用法如下:
function foo(a, b){
console.log(arguments[0], arguments[1]); //1, 10
}
foo(1, 10);
在函数中改arguments对象中的值,对原本的a与b会不会一起更动。有。:
function foo(a, b){
arguments[0] = 9;
arguments[1] = 99;
console.log(a, b); //9, 99
}
foo(1, 10);
在函数中改a与b的值,对arguments对象中的值会不会一起更动。有。:
function foo(a, b){
a = 8;
b = 88;
console.log(arguments[0], arguments[1]); //8, 88
}
foo(1, 10);
题外话,另一个新特性是加上ES6(ES2015)的函数参数的预设值,只要用了这个特性,arguments必无法再改变形参:
function foo(a=1, b=10){
arguments[0] = 9;
arguments[1] = 99;
console.log(a, b); //1, 10
}
foo();
函数中的变数提升(Hoisting)
变数提升是只提升定义,赋值(指定值)部份不会提升。以下为简单的例子,在函数中也是同样的情况:
console.log(x); //undefined,而不是报错
var x = 5;
相等于下面这样:
var x;
console.log(x);
x = 5;
解题
第一个函数
问题的第一例的代码是像下面这样,我把注解先去掉:
function f1(a) {
console.log(a);
var a=1;
console.log(a);
console.log(arguments[0]);
}
f1(10)
f1中首先看到的是var的变数提升,所以在执行时,第1个console.log(a)前面,应该会有var a的定义出现,但没有赋值部份。这里可以先写出在a=1之后的console.log(a)必然是1,然后根据上面关于arguments对象的测试结果,最后一个也是输出1
function f1(a) {
var a;
console.log(a);
a=1;
console.log(a); //1
console.log(arguments[0]); //1
}
f1(10)
另一个问题来了,a以10传入时,遇到一个var a,会变为undefined吗?先说结论,结论是"不会"。
在js中对于变量的宣告,是对剖析器的指示(directive),而不是在执行期的命令(command),不要忘了js是个直译式的脚本语言,所有的真实执行,是由js引擎中的剖析器(或分析器),先对代码作整理剖析完了,才会执行。有个最明显的特性是变量的类型是动态的,要看赋给变量什么值,才会决定这个变量的类型,你给变量一个数字,变量就变为数字类型,给它个字串,它就是个字串类型。
那么,像这里的重覆宣告,js又是怎么认为的?看下面的例子:
//第一例
var a = 10;
var a;
console.log(a); //10
//第二例
var b = 10;
var b = undefined;
console.log(b); //undefined
js在执行时会对相同变量宣告作归纳的处理,以最近的变量指定作为变量在执行时的值。以第一例来说,第二行的var a只是个宣告,而不是赋值,除非在执行前的语句,都没有对a变量赋值的其他语句,a才会被js引擎当作undefined值,也就是"不知道是什么东西,没定义"。第二例则是直接赋给b变量个undefined,js引擎当然是就认为b是个undefined类型。
所以,以本问题的在函数中的第一个console.log(a);来说,它a的值是10,而不是你认为的undefined。
第二个函数
function f2(a) {
console.log(a);
var a;
console.log(a);
console.log(arguments[0])
}
f2(10)
这个函数也一样有变量提升,所以相当于下面的代码:
function f2(a) {
var a;
console.log(a);
console.log(a);
console.log(arguments[0])
}
f2(10)
依照第一个函数的最后说明,变量宣告var a被合并(归纳)掉,所以全部的console.log都是打印出10,收工。
风格建议
这个例子可以看出几个建议的撰写风格。
第1: 变量的宣告都要在函数的最上面,提高可阅读性以避免在执行语句中间宣告。
第2: 不要重覆宣告变量,尤其常见是在for语句中。用var宣告的变量作用域是以函数为分界,而不是区域语句(for、if…)。
第2: 不要在函数的区块中间,宣告与形参同名的变量。除非你很确定在这是什么与在作什么,形参预设值自然有预设值的写法。
第3: 不要再用arguments对象,它的行为是怪异的,而且设计有问题,又是"隐性"对象。与其你花时间研究它,不如把时间花在学其馀参数写法。arguments对象是js中有名的坏设计,连js的发明者都自己说了,参考:https://brendaneich.com/2011/...
关于js函数 形参和局部变量名相同 的问题的更多相关文章
- js函数形参和实参的区别
在<Javascript权威指南>中这样定义: 参数有形参(parameter)和实参(argument)的区别,形参相当于函数中定义的变量,实参是在运行时的函数调用时传入的参数. 说明白 ...
- A Byte of Python 笔记(5)函数:定义、形参、局部变量、默认参数、关键参数
第7章 函数 函数是重要的程序段.它们允许你给一块语句一个名称,然后你可以在程序的任何地方使用这个名称任意多次地运行这个语句块.这被称为 调用 函数. 定义函数 函数通过 def 关键字定义.def ...
- javascript js函数重名后面的覆盖前面的
js 函数重名后面的覆盖前面的 var x = 1; var y = 0; var z = 0; function add(n) { return n = n + 1; } ...
- JS函数是如何执行的
当局部变量和函数参数同名时,该怎么理解呢? function test(a){ var a=a||5; alert(a) } test() //没传参的话,就是5:传参的话就alert参数 ===== ...
- JS函数和变量
JS函数和变量 函数: 函数是由事件或者当它被调用时执行的可重复使用的代码块. 是一个独立的代码块,实现特定功能模块. 函数他不进行调用触发的话,不会自己主动执行. 像ATM机一样,不去取钱的话不会 ...
- js函数的使用
js函数应用 [函数的声明及调用]: 1.函数声明: function 函数名(参数1,参数2,·····){ //函数体 retu ...
- 如何编写高质量的 JS 函数(1) -- 敲山震虎篇
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm7Xi7JxQ作者:杨昆 一千个读者,有一千个哈姆雷特. 此系列文 ...
- 05.JS函数
前言: 学习一门编程语言的基本步骤(01)了解背景知识(02)搭建开发环境(03)语法规范(04)常量和变量(05)数据类型(06)数据类型转换(07)运算符(08)逻辑结构(09)函数9.函数——f ...
- javascript函数中变量重名
<script type="text/javascript"> function fun(a){ console.log(a); // function var a=1 ...
随机推荐
- Codeforces 585E. Present for Vitalik the Philatelist(容斥)
好题!学习了好多 写法①: 先求出gcd不为1的集合的数量,显然我们可以从大到小枚举计算每种gcd的方案(其实也是容斥),或者可以直接枚举gcd然后容斥(比如最大值是6就用2^cnt[2]-1+3^c ...
- Amphetamine的cf日记
之前挂上的 今天填坑 2018.2.14 #462 A 给两个集合,B分别可以从一个集合中选一个数,B想乘积最大,A想最小,A可以删除一个第一个集合中的元素,问最小能达到多少. 这题..水死啦.我居然 ...
- Endless Spin
clj的题.图是假的别看 得先做这个[HAOI2015]按位或 本题如果还用[HAOI2015]按位或 的方法,2^50拜拜 但是思路一定是这样的:min-max容斥,考虑每个S的第一触及次数期望 这 ...
- dfs.datanode.du.reserved 预留空间不生效的问题
生产环境(cdh5.5.1)遇到一个问题:已经设置 dfs.datanode.du.reserved预留空间为20G,但是磁盘仍然被写满了. 当挂载磁盘作为datanode的存储空间,如果磁盘大小为2 ...
- cc1: warnings being treated as errors解决办法
安装GDB时出现cc1: warnings being treated as errors Edit the Makefile and delete this line:WERROR_CFLAGS = ...
- libuv移植到android
编译环境是linux + ndk,你要先添加好NDK路径的环境变量,然后进入libuv目录执行以下两句完成编译. $ source ./android-configure $NDK gyp $ mak ...
- 2017 ACM Arabella Collegiate Programming Contest(solved 11/13)
省选考前单挑做点ACM练练细节还是很不错的嘛- 福利:http://codeforces.com/gym/101350 先来放上惨不忍睹的virtual participate成绩(中间跑去食堂吃饭于 ...
- 搜索:DLX算法
精确覆盖问题:在一个0-1矩阵中,选定部分行,使得每一列都有且只有一个1.求解一种选法 舞蹈链(Dance Link),也就是一个循环十字链表,可以快速的删掉和恢复某行某列 结合了舞蹈链的搜索就称作D ...
- 816C. Karen and Game 贪心
LINK 题意:给出n*m的矩阵图,现有对行或对列上的数减1的操作,问最少几步使所有数变为0,无解输出-1 思路:贪心暴力即可.先操作行和先操作列结果可能不同注意比较. /** @Date : 201 ...
- Stat2—主成分分析(Principal components analysis)
最近在猛撸<R in nutshell>这本课,统计部分涉及的第一个分析数据的方法便是PCA!因此,今天打算好好梳理一下,涉及主城分析法的理论以及R实现!come on…gogogo… 首 ...