var a=b=15//这种写法表示 var a=15;b=15 b不会用var进行声明
let a=b=15//同理,b也不会用let声明
当栈内存或作用域形成,js代码开始执行之前,js解析器会把所有带var关键字的变量进行提前声明,带function关键字的函数进行提前声明和定义,并且已经声明,定义过的变量不会进行二次声明。
var fn=function (){console.log("2")}
function fn(){
console.log("1")
}
/*最后fn的值为console.log("2") 因为在变量提升阶段,首先声明
这个fn变量(var),此时fn的值为undefined,然后进行fn的函数
声明以及定义,此时fn的值为console.log("1"),之后开始执行代码
,在第一行,fn被赋值为console.log("2"),然后第二行的fn声明,
由于已经在变量提升阶段做过了,所以不会再声明。所以,fn最终的
值为console.log("2")*/
在函数执行时,js解析器会先开辟一个新的栈内存,然后对形参进行赋值(如果有的话),下来进行变量提升,最后执行代码。
如果我们不加var进行变量声明,那么所有的变量声明都是对window下的该属性进行赋值,即使在函数作用域下也是如此。(个人理解:不加var进行的变量声明实际上是一种省略window对象的语法糖,因为无论从哪个角度来说,他两个都是等效的。比如通过a=18定义的变量,用a in window这个方法进行检查,会发现window中存在a这个属性,并且,修改a或者window的a,另一方也会同时修改)如果我们使用typeof去检查一个未定义的变量,他会返回undefined。(个人理解:他检查的其实是window下的该属性,由于未定义过,所以会输出undefined)
如果通过var进行变量声明,在全局作用域下,他也是对window下该属性的赋值,和不带var没有任何区别。但是,如果在函数作用域下,他表示的则是在当前作用域中新建一个私有变量,与外界无关。同理,用function声明的函数也是私有函数。通过var声明的变量,只会对等号左边进行变量提升*,换句话说,如果用var声明一个函数,这个函数是不会被赋值给等号左边的变量的,但是等号左边的变量还是会进行声明。
var fn=function (){
console.log("hello")
}
/*执行过程 js解析器对fn进行变量提升,此时fn=undefined 然后
开始执行代码,在第一行fn被赋值为一个函数*/
如果在if条件下声明变量,则无论该if语句是否成立,都会对其中的用var进行声明的变量进行变量提升。如果声明的是函数(特指用function xx(){}这种方式进行声明),在较新的浏览器中,会进行变量声明,但不会进行赋值。
function f(){return true}
function g(){return true}
(function(){
if(g()){
function g(){return false}
}
})()/*这种写法叫立即执行函数,就是将函数声明转化为函数表达式,
然后进行在后面加括号直接调用
console.log(f(),g());//这里是没有执行结果的,会直接报错
TypeError: g is not a function*/
/* 原因
首先js解析器会进行变量提升,将f g进行定义,赋值。然后开始执
行代码,遇到立即执行函数,开辟一个新的栈空间,首先进行变量提升,
将立即执行函数内部的g进行变量声明=>var g 然后开始执行代码,
遇到if(g()),由于此时g的值为undefined,所以报错,g不是一个函数
*/
/*
常用的将函数声明转化为函数表达式的写法有
!function(){}()
+function(){}()
-function(){}()
~function(){}()
(function(){})()
(function(){}())
*/
现在处于混乱时期,es5和es6并行。所以,浏览器会根据变量的声明是否是let和const决定当前变量该使用哪种规则。
当我们使用let或const创建变量的时候,js解析器不会进行变量提升,但是在进入作用域时(包括函数作用域 全局作用域 块级作用域(通过单纯的花括号定义的作用域,不带function,比如 if () { },如果在花括号内用let const function声明变量,则花括号内部会被浏览器视作块级作用域)),会进行语法检查,检查是否有重名的变量(不管是let还是var声明的变量),如果有,则直接报错,同时检查是否有let变量在声明前调用,如果有,则报错。
let a=10,
b=10
let fn=function (){
let a=b=20
console.log(a,b)//==> 20 20
}
fn()
console.log(a,b)//==> 10 20
// 由于全是es6的语法,所以js解析器会进行es6的解析
/*
首先会进行语法检查 发现a b fn并没有重名的。然后会开始执行代码,声明a,b,fn三个变量
然后执行fn这个函数,创建一个新的栈空间,然后进行语法检查,发现只有一个变量a。开始执行
代码,声明一个a变量,存在这个栈空间内,然后给b(全局作用域下 存在全局作用域的栈空间内)
赋值为20 然后输出a(自己的栈空间下)和b(全局栈空间下)的值 然后执行结束,回收这个函数的栈空间
然后执行最后一行输出 变量a和b都是全局作用域下的变量
*/
var a=18
if(true){
console.log(a) // Cannot access ‘a‘ before initialization
let a=20
}
/*
在进入if语句时,首先进行语法检查 发现在第四行用let声明了a变量,然后开始从第三行执行,发现此
时调用了a变量,可是检查时a变量在第四行声明,于是就报错了
*/
如果在块级作用域内用function声明函数(不使用var),并且函数名与外部变量重名,则在执行到该函数声明位置前所有对这个函数名的操作都会对外部的变量进行操作。
var a = 0;
if (true) {
console.log(a);
a = 1;
console.log(a);
function a() {};
a = 21;
console.log(a)//21
}
console.log(a);//1
/*
解析
由于块级作用域内的函数a和外面的变量a重名,所以,在执行到这个函
数声明之前,对a的操作都属于对全局变量a的操作。
*/
var a=12,
b=13,
c=14
function fn(a){
console.log(a,b,c)
var b=c=a=20
console.log(a,b,c)
}
fn(10)
console.log(a,b,c)
//解析
/*
输出分别为
10 undefined 14
20 20 20
12 13 20
首先进入全局作用域,开始变量提升,声明a,b,c三个变量,此时他们的值
为undefined,然后声明fn这个函数变量,并为他赋值为console.log.....
然后开始执行代码,首先给a b c进行赋值,为12 13 14,然后调用函数fn,
开辟一个新的栈内存,先给fn的参数a赋值为10. 然后对b进行变量提升,此时b
的值为undefined。然后开始执行代码。首先输出 a(函数的形参) b(下一行
声明的变量) c(全局作用域的c),那么结果当然是10 undefined 14了,然后
对b,c(全局作用域),a分别赋值为20,再次输出a b c,值均为20,函数调用
结束,回收函数栈内存。输出 a b c,由于在函数里已经对c赋值为20了,所以输
出的就是12 13 20
*/
var foo = 1;
function bar() {
if(!foo) {
var foo = 10;
}
console.log(foo);
}
bar();
//解析
/*
输出10
首先在全局作用域下,变量foo进行声明,bar进行声明,赋值。然后开始执行代码,
给foo进行赋值。执行bar函数,创建一个私有栈空间,开始变量提升(将内部的foo
进行声明),此时foo的值为undefined,然后开始执行if判断,undefined取反为true
,对foo进行赋值10,然后输出10
*/
原文:https://www.cnblogs.com/lwsy/p/13360834.html