console.log(foo());
function foo() {
return bar();
var bar = function(){ return 5};
var bar = () => 6;
var bar = (function(){return 7})();
function bar() {return 8;}
}
var a = 5;
function todo() {
var a = 9;
return function(){
a=7
}
}
todo()();
console.log(a);
试试做做上面两题;看console.log能打印出什么。
答案:第一题 8 第二题:5
做对了吗,恭喜你;做错了的小伙伴们,请继续往下看哦。
第一题考的是函数声明与函数表达式、变量提升
在定义一个函数的时候通常有两种声明方式:
function bar() {return 8;} // 函数声明
var bar = function(){ return 5} // 函数表达式
不同之处
- 函数表达式后面加括号可以直接执行
- 函数声明会提前预解析
我们来看 一个例子
foo(); // 函数声明
foo_later(); // foo_later is not a function
function foo(){ console.log('函数声明'); }
var foo_later = function(){ console.log('函数表达式'); }
变量提升(hoist)
JavaScript解析器会在自身函数作用域内将变量和函数声明提前(hoist),也就是说,上面的例子其实被解析器理解解析成了以下形式:
function foo(){ console.log('函数声明'); } // 函数声明全部被提前
var foo_later; // 函数表达式(变量声明)仅将变量提前,赋值操作没有被提前
foo(); // 函数声明
foo_later(); // foo_later is not a function
foo_later = function(){ console.log('函数表达式'); }
所以 函数声明会被提到顶部;变量也会被提升,但是赋值仍然保留在原地
我们再回头看第一题 就会 被解析成什么样
console.log(foo()); // 放到最后
function foo() {
return bar();
var bar = function(){ return 5}; //变量提升,赋值留原地
var bar = () => 6; //变量提升,赋值留原地
var bar = (function(){return 7})(); //变量提升,赋值留原地
function bar() {return 8;} // 全部提升
}
function foo() {
var bar; // undefined
function bar() {return 8;} //function
return bar(); // 8;结束了
bar = function(){ return 5};
bar = () => 6;
bar = (function(){return 7})();
}
console.log(foo()); // 8
补充一点函数表达式
定义里面的指定的函数名是不是被提升的
function text7() {
a(); // TypeError "a is not a function"
b();
c(); // TypeError "c is not a function"
d(); // ReferenceError "d is not defined"
var a = function() {}; // a指向匿名函数
function b() {}; // 函数声明
var c = function d() {}; // 命名函数,只有c被提升,d不会被提升。
a();
b();
c();
d(); // ReferenceError "d is not defined"
}
text7();
下面解析有错❌
注意区分块级作用域和函数作用域
if(true){
function aaa(){
alert('1');
}
}
else{
function aaa(){
alert('2');
}
}
aaa();
与我们预想的不同,该段代码弹出的是“2”.这是因为两个函数声明在if语句被执行之前就被预解析了,所以if语句根本没有用,调用aaa()的时候直接执行了下面的函数。
上面解析有错
**函数的声明比变量的声明的优先级要高,**一旦变量被赋值后,将会输出变量
function text6() {
function a() {}
var a;
log(a); //打印出a的函数体
var b;
function b() {}
log(b); //打印出b的函数体
// !注意看,一旦变量被赋值后,将会输出变量
var c = 12
function c() {}
log(c); //12
function d() {}
var d = 12
log(d); //12
}
text6();
请问下面这道题的答案
function text6() {
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
console.log(a); // ? 1
}
text6();
这里需要注意的是,在function b()中,
var a = () {} // a类型function ;console.log(typeof a); function
a=10; // 重新把10复制给a, 此时的a是function b()中的内部变量 10
return; //
function a() {} // 被提升所以,外面输出的a 依旧是最开始定义的全局变量
function text6() {
var a = 1;
function b() {
a = 10;
return;
}
b();
console.log(a); // 10 b()没有定义 a所以向上查找还是 text6中的a;所以text6中的a被改变了
}
text6();
第二题考的作用域
- 全局作用域;
- 局部作用域;
在js中有四种方式 进入作用域;
- 语言内置: 所有的作用域里都有this和arguments;(需要注意的是arguments在全局作用域是不可见的)
- 形式参数: 函数的形式参数会作为函数体作用域的一部分;
- 函数声明: 像这种形式: function foo() {};
- 变量声明: 像这样: var foo;
在JavaScript中全局变量的作用域比较简单,它的作用域是全局的,在代码的任何地方都是有定义的。然而函数的参数和局部变量只在函数体内有定义,只存在于局部作用域中。另外局部变量的优先级要高于同名的全局变量,也就是说当局部变量与全局变量重名时,局部变量会覆盖全局变量(如下面例子)。
var num = 1;
function func() {
var num = 2;
return num;
}
console.log(func(),num); //2 1
注:声明局部变量时一定要定义否则会向上查找,如果没找到,一直查到window,解释器会将该变量当做全局对象window的属性。
var num2 = 1;
function func() {
num2 = 2; num3=3
return num2;
}
console.log(func(),num2,window.num3); //2 2 3
函数作用域内部声明的变量,会成为函数的局部变量, 只在函数体内有效,只能在函数体内访问,函数体外部是不可访问的(不可见),不会改变函数体外的变量。
由于只能在函数体内部识别局部变量;因此能够在不同函数中使用同名变量。
注意:不要滥用window全局变量,能被任何局部作用域修改。
var a = 5;
function todo() {
var a = 9;
return function(){
a=7
}
}
todo()();
console.log(a);
所以刚开始的第二题; 全局变量 a =5; 局部变量声明 a=9; 局部变量只在函数体内有效;虽然改成了7但也不能改变函数体外的值;所以 全局的console.log(a)是5