一、声明提前
1、变量声明提前——变量提升
可参照这里的描述:
#hello,JS:01数据类型、运算符、运算符优先级
//先输出a,再声明
var a=3
console.log(a) //undefined
var a=3
再如:
console.log(a)//undefined
console.log(b) //报错
var a=3
为什么console.log(a)
输出undefined
,而console.log(b)
则会报错呢?
出现这样的情况是为什么?实际上JS引擎在一行行执行代码的时候,有一些默认的运行我们并不知道,即:
var a //undefined,变量a已经前置声明,则结果为undefined console.log(a) //undefined
console.log(b) //报错,没有变量b,引用失败
a=3
最后是:变量的声明前置的完整运行:
var a //undefined,已经前置声明为undefined
console.log(a) //undefined
a=3
console.log(a)
--> undefined 3
2、函数的声明前置
先看这段代码:
sum(5,3) //放在任何的地方,但是只是一个函数值,并没有打印这个函数出来
function sum(a,b){
return a+b
}
设置两种看两种执行结果:
看代码,不是没有任何的变量声明?为什么就可以使用函数输出结果?这是因为函数所执行的JS引擎默认操作与变量声明前置机制是类似。
相当于:包含变量数据的function函数前置,即以上代码等同于
function sum(a,b){
return a+b
}
sum(5,3)
//直接返回8
3、函数表达式的声明前置
还是先看代码:
fn()
var fn = function(){
console.log('fn...')
}
//报错,直接说fn不是一个函数
这里,function函数是一个变量,相当于把一个数字赋值给fn
,而这个function函数表达式,事实上也是有一个声明前置的,即:
var fn //undefined
fn() //此为函数,会执行,但是如果是undefined(),这种是不成立,即报错
fn = function(){
console.log('fn...')
}
那么原始代码是怎么执行的?
原始代码:
fn()
sum(3,4)
var fn = function(){
console.log('fn...')
}
function sum(a,b){
return a+b
}
对于浏览器来说它做了什么:一个声明前置:包括变量声明前置和函数声明前置
var fn //变量声明前置
function sum(a,b){
return a+b } //函数声明前置
fn()
sum(3,4)
fn = function(){
console.log('fn...')
}
二、立刻执行的函数表达式
注:关于js的语法规则如何体现?
1、先看下面这个代码
(function(){ console.log('wangxiaoqin') })()
-->"wangxiaoqin"
先暂且不管它的结果如何产生。先了解一下JS的语法规则
按照这样写,为何只有function(){}
单独作为一个变量时,通过模仿语句a( );
,function(){}();
这个语句则会操作会报错,这是为什么?
对于JS引擎来说不认为是一个表达式,很像一个函数声明,再加一个括号,即会报错。那么如何让这个语句正常赋值?直接将整个函数声明加一个括号,即:
(function(){})()
由于作为运算符,括号和括号里的内容组合为一个表达式。加上括号之后,会让JS引擎认为它是一个表达式(或引用类型),那么就符合了JS的语法规则。
总结:
当在一个函数声明后加了圆括号(也是一种运算符)后运行的话,会报错。因为这被认为是语法错误。在JS中,以function开头会被认为是语句,而语句不应该以圆括号结尾。所以此时可以选用的解决办法是把整个语句用圆括号包起来。
2、那么刚才列举的代码:
(function(){
console.log('wangxiaoqin') })()
-->"wangxiaoqin" //即函数表达式,立刻去执行它
//等同于
var fn = function(){
}
fn()
这类型的函数表达式有什么用?这里涉及了函数中所对应的作用域的概念,假设我们在这类函数里添加一个变量
(function(){
var a =3 console.log('wangxiaoqin')
})()
console.log(a)
//运行,后台报错,a是没有被定义的。
//因为a变量是不被看到的,因为a在function函数的这个作用域里,与外界无关
3、立刻执行函数的好处:
var fn=function(){}
相当于
fn()===(函数表达式)()
()
作为一种运算符,用这种局部作用域的方式将函数引用包裹起来,形成一个立即执行的表达式,好处在于:
A、函数不必再另外命名,避免了污染全局,不会在复杂页面协作中造成错乱;
B、实现一个作用域隔离,封装外部无法读取的私有变量;
C、避免命名冲突,符合js语法规则,并立刻执行。
三、命名冲突
当在同一个作用域内定义了名字相同的变量和方法的话,会根据前置顺序产生覆盖
var fn = 3;
function fn(){
}
console.log(fn);
// 3
相当于
var fn function fn(){} //覆盖上面的 fn = 3 //重新赋值 console.log(fn) //为函数
当函数执行有命名冲突的时候,可以认为在还是内部一开始有隐藏的声明变量这个操作
function fn(fn){
console.log(fn);
var fn = 3;
console.log(fn);
}
fn(10) //10 3
//等同于有一个默认的var fn = arguments[0]的操作
function fn(){
var fn = arguments[0] //1、将它先声明前置,再赋值,再输出
console.log(fn);
var fn = 3; //2、再赋值
console.log(fn);
} fn(10)
//10 3