美文网首页
JS预解释 & 作用域链

JS预解释 & 作用域链

作者: 空压机百科 | 来源:发表于2019-11-19 22:04 被阅读0次

浏览器加载HTML页面时,首先会提供一个供全局JS代码执行的环境称之为全局作用域全局作用域在后台用global 表示,在js中用 window 表示。

预解释(变量提升):

当栈内存(作用域)形成,js代码执行之前,浏览器首先会默认的把所有带 varfunction 的进行提前声明或定义。

理解声明和定义:

1、 var num = 13
声明:var num;告诉浏览器在全局作用域中有一个 num 的变量了
定义:num = 13;给变量进行赋值
2、对于带 var 和 function 关键字的在预解释的时候操作是不一样的
var 在预解释的时候只是提前声明
function 在预解释的时候提前的声明+定义都完成了
3、预解释只发生在当前的作用域下,例如:开始只对 window 下的进行预解释,只有函数执行的时候才会对函数中的进行预解释

var num = 13;
var obj = {name: "小明", age: "7"}
function fn(num1,num2){
   var total = num1 + num2
   console.log(total)  
}  //内存图如下
内存图
JS中的内存分类:

1、栈内存:用来提供一个供 JS 代码执行的环境(全局作用域 / 私有作用域),所有基本类型值都会在栈内存开一个位置进行储存
2、堆内存:用来存储引用数据类型的值(对象存储的是属性名和属性值,函数存储的是代码字符串

如何区分私有变量和全局变量:

1、在全局作用域下声明(预解释的时候)的变量是全局变量
2、在 “私有作用域中声明的变量” 和 “函数形参” 都是私有变量
作用域链:在私有作用域中,代码执行的时候遇到一个变量,首先我们需要确定它是否为私有变量,如果是私有变量那么和外面没有任何关系;如果不是则往当前作用域的上级进行查找,如果上级作用域也没有则继续查找,一直找到 window 为止。

函数的执行过程:

函数执行的时候(直接目的:让函数体中的代码执行),首先会形成一个新的私有作用域链,然后执行如下步骤。
1、如果有形参首先给形参赋值
2、进行私有作用域中的预解释
3、私有作用域中的代码从上到下执行
4、.....
闭包:函数形成一个新的私有作用域保护了里面的私有变量不受外界干扰(外面的修改不了私有的,私有的也修改不了外面的)。闭包作用:保护私有变量和外界没有联系,形成不销毁栈内存里面私有变量信息保存下来
案例图解:

console.log(total)  //undefined
var total = 0
function fn(num1,num2){
  console.log(total)  //undefined
  var total = num1 + num2   
  console.log(total)  //300
}
fn(100,200)
console.log(total)   //0
内存图

案例:

console.log(total)  //undefined
var total = 0
function fn(num1,num2){
  console.log(total)  //total不是私有的在全局下查找,total为是全局的0,
  total = num1 + num2   //全局的total = 300
  console.log(total)  //300
}
fn(100,200)
console.log(total)   //全局的total = 300
注意:

1、私有作用域中出现的一个变量不是私有的,则往上级作用域进行查找,上级没有则继续向上查找,一直找到 window 为止,如果 window 下也没有?
如果是获取值:console.log(xxx) 则报错
如果是设置值:xxx = 100 相当于给 window 增加了一个属性名 xxx ,属性值是 100

2、条件判断下变量提升
预解释的时候不管你的条件是否成立,都要把带 var 的提前声明
带function的在老版本浏览器下,声明和定义都处理,为了迎合ES6中的块级作用域新版浏览器对于函数(在条件判断中的函数),不管条件是否成立,都是先声明,没有定义,类似于var。如果成立声明加定义不成立只声明。条件成立进来后的第一件事是给函数赋值然后代码执行

//window 的预解释:var num;-> widndow.num;
if(!("num" in window)){  //"num" in window -> true
  var num = 12;
}
console.log(num) //undefined 
<!--- ================ -->
f = function (){return true}; //window.f = ...(true)
g = function (){return false}; //window.g = ...(false)
!function(){
   //变量提升:函数值声明未定义 function g是私有变量未undefined
   if(g() && [] === ![]){//undefined()报错 
      f = function(){return false;};
      function g(){return true};
   }
}();
console.log(f());
console.log(g());

in:检测某个属性是否隶属于这个对象

3、预解释的时候只预解释 ”=“ 左边的,右边的是值,不参与预解释
匿名函数之函数表达式:把函数定义的部分当作一个值赋值给变量/元素的某一个事件

//window下的预解释:var fn
fn();  //报错
var fn = function({
   console.log("ok")
})

4、自执行函数定义的那个 function 在全局作用域下不进行预解释,当代码执行到这个位置的时候定义和执行一起完成了

自执行函数:定义和执行一起完成
!function(num){}(100)
(function(num){}(100))

5、函数体中 return 下面的代码虽然不执行了,但还需要进行预解释;return 后面跟着的是返回值,所以不进行预解释

function fn(){
   //预解释:var num;
   console.log(num); //undefined
   return function(){
     
   };
   var num = 100;
}
fn();

6、重名问题处理
在预解释的时候,如果名字已经声明过了(带var和function关键字声明相同名字,算是重名),不需要重新声明,但需要重新赋值;不管是变量提升还是代码执行阶段皆是如此

//window预解释:
//声明 + 定义fn = xxxfff000
//声明 var fn(前面声明了不需要重新声明)
//声明(不重复进行)+ 定义 fn = xxxfff111
//fn = xxxfff111
fn();  //-> 2
function fn(){console.log(1);};
fn(); //-> 2
var fn = 10; //fn = 10
fn(); //10() 报错
function fn(){console.log(2)};
fn()

7、let创建的变量不存在变量提升
在ES6中基于let或const等方式创建变量或者函数,不存在变量提升机制
切断了全局变量和window属性的映射机制
在相同的作用域中,基于let不能声明相同名字的变量
虽然没有变量提升,但是当前作用域代码执行前,浏览器会做一个重复性检测当前作用域下所有变量,一旦发现重复直接报错,代码不会执行

如何查找当前作用域的上一级作用域?

看当前函数在哪个作用域下定义的,那么他的上一级作用域就是谁,和函数在哪执行没有任何关系
在作用域链查找的过程中,如果找到window也没有这个变量相当于给window设置了一个属性
案例图解:

var num = 12;
function fn(){
   var num = 120;
   return function(){
      console.log(num)
   }
}
var f = fn();
f();  //120
!function(){
   var num = 1200;
   f();  //120
}()
内存图

带var和不带var区别:
在全局作用域下声明一个变量,相当于给window全局对象设置了一个属性,变量的值是属性值(私有作用域中声明的私有变量和window没啥关系),如果不加var的本质是window的属性,且全局变量和window中的属性存在“映射机制”
私有作用域中带var为私有变量和外界没由任何关系,不带var不是私有变量,会向通过作用域链进行查找

浏览器在开辟栈内存供代码自上而下执行之前,不仅有变量提升还有其他操作比喻”词法解析“检测当前要执行的代码是否出现语法错误,如果有错误代码不再执行。如下

    console.log(1);
    let a = 12;
    console.log(a);
    let a = 13;
    console.log(a); 
//代码不会输出直接报错

相关文章

  • 1.预解释和作用域链

    预解释 作用域链

  • JS预解释 & 作用域链

    浏览器加载HTML页面时,首先会提供一个供全局JS代码执行的环境称之为全局作用域全局作用域在后台用global 表...

  • * 预解释

    * 预解释 * 什么是预解释 * 关于预解释: 带var和带function不同 * 函数执行四阶段 * 作用域链...

  • JS高级-闭包、沙箱

    作用域,作用域链,预解析 变量:局部变量、全局变量 作用域:变量的使用范围 js中没有块级作用域,一对括号中定义的...

  • 预解释和作用域链

    什么是预解释:在当前作用域下,在JS代码执行之前,浏览器会对带var和带function的进行提前声明或定义; 带...

  • 干货!月薪80k前端大佬面试笔记:JS闭包解析!

    三点注意事项 JS没有块级作用域,只有全局作用域和局部作用域(函数作用域)。 JS中的作用域链,内部的作用域可以访...

  • JS 作用域链、导入导出

    1. JS 的作用域链 作用域在 JS 中表示变量的可访问性和可见性。JS 作用域有 3 种:1. 全局作用域;2...

  • JavaScript进阶教程-1.作用域深入和面向对象

    第一章 作用域深入和面向对象 预解释 当浏览器开始解析js代码的时候,首先看当前运行环境(作用域)内带var和fu...

  • 闭包

    一、理解闭包前js基础1、作用域链(作用域、作用域链中有说)。2、js的内存回收机制。一个函数在执行开始的时候,会...

  • 作用域和作用链

    关键词:作用域作用链 作用域 js中没有块级作用域 全局作用域,函数作用域太简单,就不演示(≧▽≦)/啦啦啦 作用...

网友评论

      本文标题:JS预解释 & 作用域链

      本文链接:https://www.haomeiwen.com/subject/hmuiictx.html