Javascript中函数的形参和函数内局部变量同名的问题

前言
上次牛客网做到这样一个题,非常有意思,陷阱非常多,当时觉得搞明白了,现在再看到,又糊涂了,发现了新的点,看一下:

var foo={n:1};


(function (foo) {
    console.log(foo.n);
    foo.n = 3;
    var foo = {n:2};
    console.log(foo.n);
})(foo);


console.log(foo.n);

乍一看,是局部和全局变量的一些区分问题,其实坑很多,下面我们一点点分析下;

变量的定义(宣告)和赋值
首先我们看一段代码:

var a = 1;
var a;
console.log(a);//1

这里第二行对a是一个重复宣告,而不是赋值,变量只有定义(宣告)后未赋值的情况下才会输出undefined,除非手动赋值undefined;那么这里,JS引擎对于重复宣告的规定以最近的变量指定(也就是赋值)作为变量在执行时的值,所以第二行的var a;其实相当于无效;

函数中形参和局部变量同名
在我们自己写代码时,一般不会做这种蠢事情,把形参和局部变量定义为同名,可如果真的这样做了呢?
那就要分析下JS执行上下文中的变量对象了,这个知识点不牢固的同学可以移步这里:重拾ECMAScript基础——变量、作用域;
作用域链对变量的保存都是在变量对象中的,那么ES5对形参在变量对象中是如何保存的呢,请看规范:

10.5 Declaration Binding Instantiation
Every execution context has an associated VariableEnvironment. Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment‘s Environment Record. For function code, parameters are also added as bindings to that Environment Record.

就是说,无论是形参还是函数中声明的变量,JS对他们的处理是没有区别的,都是保存在这个函数的变量对象中作为局部变量进行处理;那么结合上面我们说到的变量的重复宣告,接下来同名的问题就很简单了,看代码:

(function fun (param) {
    var param;
    console.log(param);//1
    param = 2;
    console.log(param);//2
})(1);

在这里,同名的局部变量和形参其实是同一个东西,都是在函数的变量对象里的保存的那个变量;

如果变量是引用类型呢?
那么如果变量是个对象的话,就是我们文章一开始提到的题目了,下面我们分析下:

var foo = {n : 1};
(function(foo) {
    console.log(foo.n);
    foo.n = 3;
    var foo = {n : 2};
    console.log(foo.n);
})(foo);
console.log(foo.n);
var foo = {n : 1};
function fun(foo) {
    var foo;
    console.log(foo.n);
    foo.n = 3;
    foo = {n : 2};
    console.log(foo.n);
};
fun(foo);
console.log(foo.n);

上下两段代码,意思是一样的,我把匿名立即执行函数换成了普通函数并在下一行调用,方便大家理解;

其实分析一下,就是这么几个问题;
内部foo变量提升;
内部foo和形参同名;
内部foo重复宣告;
所以内部var的那个foo和形参foo是同一个东西,并没有发生变化;
然后重复宣告不影响之前的赋值,所以第一个为1;
接下来,foo.n=3,由于形参为对象,所以是传进来的是一个对象的引用(指针);
对这块知识点不牢固的同学还是请移步我之前那篇文章;
那么这个引用指向的堆内存的那块空间里的n改变为3;
接下来,foo={n:2};这个就很有意思了,我们刚才也说了,传进来的是个引用;
现在给这个引用赋值,实际上就是让它指向新开辟的空间,存放着{n:2}这个对象;
那么之前的引用就断掉了,也就是说形参foo已经不指向全局里那个foo指向的空间了;
固然,在函数里,会输出新空间里的2;
而在函数外,旧空间仍然没有改变,故为3;

总结
其实这个题目考了很多知识点,最后就是参数传递中引用类型的用法,这个也是ECMAScript中基础的一个难点,结合前面的一些,变量提升,重复宣告,形参与局部变量同名,算是解释清楚了。

若有错误,欢迎指出。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值