JavaScript函数形参、函数内部变量、函数名命名冲突问题

JavaScript函数形参、函数内部变量、函数名命名冲突问题,第1张

函数形参和函数内声明变量同名问题

我们首先看下下面的执行结果

function ab(x) {
    console.log(x);
    var x;
    console.log(x);
}
ab(30);

// 30
// 30

输出结果竟然都是30,是不是非常奇怪???不着急,再看

function ab1(x) {
    console.log(x);
    var x = 4;
    console.log(x);
}
ab1(40)
// 40
// 4

输出结果更加脑崩了,

 继续往下看,只能接受残酷的现实

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);

// 1
// 2
// 3

撞墙算了!!!!

function ab2(x) {
    var x = 40;
    console.log(x);
    function x() {
        console.log('x');
    }
    console.log(x);
}
ab1(40)
// function x(){ console.log('x');}
// function x(){ console.log('x');}

去屎吧!!!我不玩了。

授业解惑

作为js忠实使用者,基础扎实的就知道了,js有变量和函数声明提升的问题,但是只知道变量声明提升还不能完全解决我们的疑惑,还有那么一点没有get到点子上。那这么一点是什么呢???

js引擎对于变量定义的取舍规则,请记住!!!取舍规则!!!js引擎对于重复定义的规则是以最近的变量指定作为变量在执行时的值。

举个例子:

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

根据取舍规则,上面这个例子中,除了变量声明,js引擎发现有变量声明重复,就会以var a = 1作为执行时的值,故而输出1。第二行的var a;是无效了。

注意!!!js规范中,无论是形参还是函数中声明的变量,js对他们的处理是没有任何区别的,都会保存在函数的栈内存中作为局部变量进行处理。

经过上面的规则规范,我们就能清晰的解决了上面的疑惑了。首先ab函数中,可以转换为

function ab(x) {
    var x = 30; // 形参变成内部局部变量
    var x; // 变量声明提升,x有重复定义,故而这一句无效
    console.log(x);
    console.log(x);
}

ab(30); // 哈哈哈,终于说通了。

那形参是引用类型怎么办??一样

var foo = {n : 1};
(function(foo) {
    var foo = foo;
    var foo;
    console.log(foo.n);
    foo.n = 3;
    foo = {n : 2}; // 这个时候,局部变量foo指向了新的{n: 2}这个新的对象,
    console.log(foo.n);
})(foo);

console.log(foo.n);
函数名跟变量名同名问题

首先看下面的例子

var a;
function a() {
    a = 2;
}
a();
console.log(a);
// 2

上面例子中,a变量声明被a函数覆盖,所以最后输出为2,a函数执行时,内部作用域有一个a=2语句,其实在非严格模式下,此时a默认就是一个全局变量,根var a已经没有关系了。要是改成如下:

var a=1;
function a() {
    a = 2;
}
a();
console.log(a);
// Uncaught TypeError: a is not a function at :5:1

报错了,根据变量重复取舍规则,还原真实场景如下:

var a;
function a() {
    a = 2;
}
a=1;
a();
console.log(a);

此时a已经不是函数了,故而报错了。如果我们将a函数封装在立即执行函数里,结果还会报错吗??请往下看:

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

console.log(a); // 1

问君能有几多愁,白了少年头啊!虽然没报错,但是立即函数里竟然输出的是函数体。为什么???

那是因为立即调用的函数表达式(IIFE)有一个自己独立的作用域,如果函数名称与内部变量名称冲突,就会永远优先执行函数本身;所以上面的结果输出是函数本身。还是规范的问题。

命名函数表达式

顺便学习一下命名函数的知识,一般形如var a = function bear(){},brear就是叫做命名函数表达式。

命名函数表达式名字只在新定义的函数作用域内有效,因为规范规定了标示符不能在外围的作用域中有效。

下面两个例子:

var a = function foo(){
    return typeof foo; // foo在内部作用域内有效
} // foo即为函数表达式

console.log(foo) // undefined外部访问无效
var b = function a() {
    a = 10;
    console.log(a);
}

b(); // function a(){...}输出函数体

第一个好理解,第二个例子中,除了a函数表达式只能在函数体内有效之外,同名的变量在函数体能引起命名冲突,优先执行函数本身,所以b执行之后,a输出还是函数体本身。

给命名函数表达式赋值的最主要的原因是为了方便在调试的时候方便的看到当前调用的函数名称,命名函数表达式可以在堆栈跟踪、调用堆栈、断点列表等中看到。

总结 函数形参和函数内声明变量同名问题,无论是形参还是函数中声明的变量,js对他们的处理是没有任何区别,都会保存在函数的栈内存中作为局部变量进行处理;函数名和函数内声明变量同名问题,根据js规则,如果函数名称与内部变量名称冲突,就会永远优先执行函数本身;命名函数表达式名字只在新定义的函数作用域内有效,不能在外围的作用域中有效;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1295906.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存