浏览器加载页面,想让代码执行,首先会形成一个栈内存(ECStack);然后开始让代码准备执行;
=> 最开始执行的一定是全局下的代码,此时形成一个全局的执行环境(全局执行上下文EC(G)),把EC(G)压缩到栈内存中去执行(进栈);每一个函数执行也是这样操作的···
=> 有些上下文在代码执行完成后,会从栈内存中移去(出栈),但是有些情况是不能移出去的(例如:全局上下文就不能移除···)
=> 在下一次有新的执行上下文进栈的时候,会把之前没有移除的都放到栈内存的底部,让最新要执行的在顶部执行。
变量提升
在当前的执行上下文中(不论是全局,还是函数执行私有的),JS代码自上而下执行之前,首先对默认把所有带var和function(判断和循环之外的)关键字的进行声明(declare,创建一个变量)和定义(defined,让变量和值关联在一起)。
带var的只是提前声明;带function的会提前声明+定义
1、在当前执行上下文中,不管条件是否成立,(只要不是在函数里的)变量提升是有效的。旧的浏览器(IE10及以下)function func(){…}声明+定义都处理了,新的浏览器中,用判断或者循环等包裹起来的函数,在变量提升阶段,不论条件是否成立,此处只是先声明。
/* * 低版本浏览器(包含IE10及以内) * 全局上下文中变量提升 * 没有 */ f=function (){return true;};//给GO中设置一个属性 f = function () {return true;} g=function (){return false;};//给GO中设置一个属性 g = function () {return false;} (function () { // /* // * 自执行函数执行,形成一个私有的执行上下文 // * [变量提升] // * function g(){return true;} // */ // // 条件解析: // // g() => 私有的G执行 TRUE // // []==![] => []==false => 0==0 => TRUE if (g() && [] == ![]) { //=>条件成立 f = function () {return false;} //f不是自己私有的,则向上查找,属于全局对象中的f,此处是把全局对象中的 f = function () {return false;} function g() {return true;} //跳过(变量提升处理过了) } })(); console.log(f()); //=>FALSE console.log(g()); //=>FALSE 这个G找全局的(函数里面的G是自己私有的) /* * 高版本浏览器 * 全局上下文中变量提升:没有 */ f=function (){return true;};//给GO中设置一个属性 f = function () {return true;} g=function (){return false;};//给GO中设置一个属性 g = function () {return false;} (function () { // /* // * 自执行函数执行,形成一个私有的执行上下文 // * [变量提升] // * function g; 高版本浏览器中,在判断和循环中的函数,变量提升阶段只声明不定义 // */ // // 条件解析: // // g() => undefined() => Uncaught TypeError: g is not a function 下面操作都不在执行了 if (g() && [] == ![]) { f = function () {return false;} function g() {return true;} } })(); console.log(f()); console.log(g());
自执行函数形成一个私有的执行上下文,不再全局里变量提升
2、函数表达式方式创建函数,在变量提升阶段,并不会给函数赋值,只有代码执行过程中,函数赋值后才可以执行(不能再函数赋值之前执行)=>符合严谨的执行逻辑
在全局执行上下文中,带var和不带var定义的区别
带var是创建一个全局变量,存放在全局变量对象VO(G)中
不带var创建的不是一个变量,而是在全局对象GO(global object)的一个属性。(全局对象:浏览器会默认自带很多供JS调取使用的内置API,这些属性都在GO中储存,在浏览器端,把GO对象赋值给window,在node端把GO赋值给global)
总结:在全局上下文中,基于var创建变量,会给VO(G)和GO中各自储存一份,不带var的知识给GO设置一个属性而已,当我们输出这个变量值时,先看是否为全局变量,是的话输出全局变量的值;如果不是,则看它是否为全局对象的属性,如果再不是则报错!!
在JS中一旦当前代码报错,那么下面的代码都不再执行。
浏览器有一个特征:做过的事情不会再重复做第二遍(不会重复声明)
/*
* 全局上下文中的变量提升
* fn = function(){ 1 } 声明+定义
* = function(){ 2 }
* var fn; 声明这一步不处理了(已经声明过了)
* = function(){ 4 }
* = function(){ 5 }
* 结果:声明一个全局变量fn,赋的值是 function(){ 5 }
*/
fn(); //=>5
function fn(){ console.log(1); } //=>跳过(变量提升的时候搞过了)
fn(); //=>5
function fn(){ console.log(2); } //=>跳过
fn(); //=>5
var fn = function(){ console.log(3); } //=>var fn; 这一步跳过,但是赋值这个操作在变量提升阶段没有搞过,需要执行一次 => fn = function(){ 3 }
fn(); //=>3
function fn(){ console.log(4); } //=>跳过
fn(); //=>3
function fn(){ console.log(5); } //=>跳过
fn(); //=>3