js中的变量提升/预编译

预编译/变量提升

当栈内存(作用域)形成,JS代码自上而下执行之前,浏览器首先会把所有带 “VAR”/“FUNCTION” 关键词的进行提前 “声明” 或者 “定义” ,这种预先处理机制称之为 “变量提升”或预编译
  • 声明(declare):var a (默认值undefined)
  • 定义(defined):a=12 (定义其实就是赋值操作)
  • [变量提升阶段]
    带“VAR”的只声明未定义
    带“FUNCTION”的声明和赋值都完成了

变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的进行提升,因为此时函数中存储的都是字符串而已)
在全局作用域下声明的函数或者变量是“全局变量”,同理,在私有作用域下声明的变量是“私有变量” [带VAR/FUNCTION的才是声明]浏览器很懒,做过的事情不会重复执行第二遍,也就是,当代码执行遇到创建函数这部分代码后,直接的跳过即可(因为在提升阶段就已经完成函数的赋值操作了

栈内存:

由于数据类型比较简单,他们都是直接在栈内存中开辟一个位置,把值存储进去,当栈内存销毁,存储的那么些值也被销毁了

堆内存

存储引用类型值得:如对象(键值对)函数(字符串)
当前堆内存释放了,那么这个引用值彻底没有了
堆内存的释放:
当堆内存没有被任何变量或者其他东西占用,浏览器会在空闲的时候,自主的进行内存回收,把所有不被占用的堆内存销毁掉(谷歌浏览器)
通过xxx=null:通过空对象指针null,可以让原始变量或其它变量,谁都不指向,那么原有的被占用的堆内存就没有被东西占用了,浏览器会销毁他

var a = 13;
var a = b;
    b = 12;
    console.log(a);//13
    函数执行前一刻进行变量提升,形成一个栈内存 , 全局作用域,声明和定义了a , 声明了b,然后把存贮的值放到b上,直接操作值,然后把b的值变为12
var ary1 = [12,23]
var ary2 = ary1;
ary.push(100)
console.log(ary1)//[12,23,100]

任意数求和
function sum (){
var total = null;
for(i = 0;i<arguments.length;i++){
   var item = arguments[i];
   !isNaN(item) ? total+=item:null
}
return total

}
sum(1,2,4,5)
如下图执行机制

在这里插入图片描述

带var 和不带var的区别

在 全局作用域下声明一个变量,也相当于给WINDOW全局对象设置了一个属性,变量的值就是属性值(私有作用域中声明的私有变量和WINDOW没啥关系)

console.log(a);//=>undefined
console.log(window.a);//=>undefined
console.log('a' in window); //=>TRUE 在变量提升阶段,在全局作用域中声明了一个变量A,此时就已经把A当做属性赋值给WINDOW了,只不过此时还没有给A赋值,默认值UNDEFINED
*  in:检测某个属性是否隶属于这个对象*
var a = 12;//=>全局变量值修改,WIN的属性值也跟着修改
console.log(a);//=>全局变量A  12
console.log(window.a);//=>WINDOW的一个属性名A  12
a = 13;
console.log(window.a);//=>13
window.a = 14;
console.log(a);//=>14
//=>全局变量和WIN中的属性存在 “映射机制”

只对等号左边进行变量提升

变量提升:
 *   var fn;   =>只对等号左边进行变量提升
 *   sum = AAAFFF111;
sum();
fn();//=>Uncaught TypeError: fn is not a function

//=>匿名函数之函数表达式
var fn = function () {
    console.log(1);
};//=>代码执行到此处会把函数值赋值给FN

fn();
//=>普通的函数
function sum() {
    console.log(2);
}

条件判断下的变量提升

  • 在当前作用域下,不管条件是否成立都要进行变量提升
  • 带VAR的还是只声明
  • 带FUNCTION的在老版本浏览器渲染机制下,声明和定义都处理,但是为了迎合ES6中的块级作用域,新版浏览器对于函数(在条件判断中的函数),不管条件是否成立,都只是先声明,没有定义,类似于VAR
变量提升
var a;  =>在全局作用域下声明的全局变量也相当于给WIN设置了一个属性 window.a=undefined
console.log(a);//=>undefined
if ('a' in window) {
   var a = 100;
}
console.log(a);//=>100
f = function () {return true;};//=>window.f=...(TRUE)
g = function () {return false;};//=>window.g=...(FALSE)
~function () {
   /*
    * 变量提升:
    *   function g;  //=>g是私有变量
    */
   if (g() && [] == ![]) {//=>Uncaught TypeError: g is not a function (此时的g是undefined)
       //=>[]==![]:TRUE
       f = function () {return false;};//=>把全局中的f进行修改 window.f=...(FALSE)
       function g() {return true;}
   }
}();
console.log(f());
console.log(g());

只对等号左边的变量 进行变量提升

/*
 * 变量提升:
 *   var fn;   =>只对等号左边进行变量提升
 *   sum = AAAFFF111;
 */
sum();
fn();//=>Uncaught TypeError: fn is not a function

//=>匿名函数之函数表达式
var fn = function () {
    console.log(1);
};//=>代码执行到此处会把函数值赋值给FN

fn();

//=>普通的函数
function sum() {
    console.log(2);
}

条件判断下的变量提升

 /*
 * 在当前作用域下,不管条件是否成立都要进行变量提升
 *   =>带VAR的还是只声明
 *   =>带FUNCTION的在老版本浏览器渲染机制下,声明和定义都处理,但是为了迎合ES6中的块级作用域,新版浏览器对于函数(在条件判断中的函数),不管条件是否成立,都只是先声明,没有定义,类似于VAR
 */
/*
/!*
 * 变量提升
 *   var a;  =>在全局作用域下声明的全局变量也相当于给WIN设置了一个属性 window.a=undefined
 *!/
console.log(a);//=>undefined
if ('a' in window) {
    var a = 100;
}
console.log(a);//=>100
*/

/*
 * 变量提升:无
 */
f = function () {return true;};//=>window.f=...(TRUE)
g = function () {return false;};//=>window.g=...(FALSE)
~function () {
    /*
     * 变量提升:
     *   function g;  //=>g是私有变量
     */
    if (g() && [] == ![]) {//=>Uncaught TypeError: g is not a function (此时的g是undefined)
        //=>[]==![]:TRUE
        f = function () {return false;};//=>把全局中的f进行修改 window.f=...(FALSE)
        function g() {return true;}
    }
}();
console.log(f());
console.log(g());

/*
 * 变量提升:
 *   function fn;
 */
// console.log(fn);//=>undefined
if (1 === 1) {
    console.log(fn);//=>函数本身:当条件成立,进入到判断体中(在ES6中它是一个块级作用域)第一件事并不是代码执行,而是类似于变量提升一样,先把FN声明和定义了,也就是判断体中代码执行之前,FN就已经赋值了
    function fn() {
        console.log('ok');
    }
}
// console.log(fn);//=>函数本身


重名问题处理

/*
 * 1.带VAR和FUNCTION关键字声明相同的名字,这种也算是重名了(其实是一个FN,只是存储值的类型不一样)
 */
/*
var fn = 12;
function fn() {

}
*/

/*
 * 2.关于重名的处理:如果名字重复了,不会重新的声明,但是会重新的定义(重新赋值)[不管是变量提升还是代码执行阶段皆是如此]
 */

/*
 * 变量提升:
 *   fn = ...(1)
 *      = ...(2)
 *      = ...(3)
 *      = ...(4)
 */
/*
fn();//=>4
function fn() {console.log(1);}
fn();//=>4
function fn() {console.log(2);}
fn();//=>4
var fn=100;//=>带VAR的在提升阶段只把声明处理了,赋值操作没有处理,所以在代码执行的时候需要完成赋值 FN=100
fn();//=>100() Uncaught TypeError: fn is not a function
function fn() {console.log(3);}
fn();
function fn() {console.log(4);}
fn();
*/

let创建的变量不能进行变量提升

let a = 10,
    b = 10;
let fn = function () {
    console.log(a, b);//=>Uncaught ReferenceError: a is not defined
    let a = b = 20;
    /*
     * let a=20;
     * b=20; //=>把全局中的 b=20
     */
    console.log(a, b);
};
fn();
console.log(a, b);

暂时性死区

/*
var a = 12;
if (true) {
    console.log(a);//=>Uncaught ReferenceError: a is not defined
    let a = 13;//=>基于LET创建变量,会把大部分{}当做一个私有的块级作用域(类似于函数的私有作用域),在这里也是重新检测语法规范,看一下是否是基于新语法创建的变量,如果是按照新语法规范来解析
}
*/

/*
// console.log(a);//=>Uncaught ReferenceError: a is not defined
console.log(typeof a);//=>"undefined" 在原有浏览器渲染机制下,基于typeof等逻辑运算符检测一个未被声明过的变量,不会报错,返回UNDEFINED
*/

// console.log(a);//=>Uncaught ReferenceError: a is not defined
console.log(typeof a);//=>Uncaught ReferenceError: a is not defined
let a;//=>如果当前变量是基于ES6语法处理,在没有声明这个变量的时候,使用TYPEOF检测会直接报错,不会是UNDEFINED,解决了原有的JS的死区
发布了10 篇原创文章 · 获赞 2 · 访问量 71

猜你喜欢

转载自blog.csdn.net/weixin_43324909/article/details/104354241