资讯专栏INFORMATION COLUMN

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

xiaotianyi / 2726人阅读

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

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中基础的一个难点,结合前面的一些,变量提升,重复宣告,形参与局部变量同名,算是解释清楚了。

若有错误,欢迎指出。

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

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

相关文章

  • JavaScript基础系列---执行环境与作用域链

    摘要:延长作用域链下面两种语句可以在作用域链的前端临时增加一个变量对象以延长作用域链, 问题 今天看笔记发现自己之前记了一个关于同名标识符优先级的内容,具体是下面这样的: 形参优先级高于当前函数名,低于内部函数名 形参优先级高于arguments 形参优先级高于只声明却未赋值的局部变量,但是低于声明且赋值的局部变量 函数和变量都会声明提升,函数名和变量名同名时,函数名的优先级要高。执行代...

    J4ck_Chan 评论0 收藏0
  • JavaScript函数

    摘要:函数使用它们实参的值来计算返回值,称为该函数调用表达式的值。通常函数名的第一个字符为小写。在函数内部定义的变量,外部无法读取,称为局部变量。注意对于命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。 函数 1.函数简介 通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用。 ECMAScript中的函数使用function关键字来声明,后跟一组参数以及...

    Martin91 评论0 收藏0
  • JavaScript函数个人分享

    摘要:函数函数的概述所谓函数就是只被定义一次但可能被执行或调用多次变量与函数的区别变量存的是数据内容而函数存的是语句块定义函数备注定义函数时函数体的内容不会被执行调用函数时函数体才被执行注意一般以个函数就去完成一个功能函数声明方式语法函数名称 函数 函数的概述 所谓函数就是只被定义一次,但可能被执行或调用多次 变量与函数的区别: 变量存的是数据内容而函数存的是语句块 定义函数 备注: ...

    pkhope 评论0 收藏0
  • JavaScript学习笔记

    摘要:因为即使包含了其他的代码,也只会下载并执行属性内的外部脚本文件,嵌入的额外代码会被忽略。在脚本中,如果程序员在对某个变量赋值之前未声明,赋值操作将自动声明该变量。共有中数据类型。阻止事件冒泡火狐浏览器创建元素添加子元素删除子元素数组拼接方法 所有的web开发都是【请求】+【响应】 推荐JavaScript中使用单引号引用字符串,HTML中使用双引号,防止冲突 JavaScript代码...

    2shou 评论0 收藏0
  • JavaScript函数与作用域

    摘要:函数函数是什么函数是这样的一段代码它只定义一次但可能被执行或调用多次简单来说函数就是一组可重用的代码可以在程序的任何地方调用定义函数函数声明方式定义函数时函数体的内容是不会被执行的这是一个函数字面量直接量方式这是一个函数调用函数定义一个函数 函数 函数是什么 函数是这样的一段JavaScript代码 它只定义一次 但可能被执行或调用多次简单来说 函数就是一组可重用的代码 可以在程序的任...

    jayce 评论0 收藏0

发表评论

0条评论

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