深刻浅出JS - 变量提高(函数声明提高)

前言

在咱们的平常工做中,变量无处不在。更加深刻的去了解它,可以使得本身的JS水平更上一层楼, 从变量提高这个小知识点着手,让咱们一块儿来深刻了解JS吧!
html

变量提高的小栗子

console.log(a) // undefined
var a = 'hello JS' 

/* 在咱们声明a以前为何输出a不会报错呢? 不急,让咱们接着往下看 */

num = 6;
num++;
var num;
console.log(num) // 7 好奇怪,为何给一个尚未声明的变量赋值会不报错呢

function hoistFunction() {
    foo();
    function foo() {        
        console.log('running...')    
    }
}
hoistFunction(); // running... 

/* 最后一个栗子 */

alert(a) //  function a { alert(10) }
a(); // 10
var a = 3;
function a() {    
    alert(10)
};
alert(a) // 3
a = 6;
a(); // throw error复制代码


分析缘由

JS引擎会在正式执行代码以前进行一次”预编译“,预编译简单理解就是在内存中开辟一些空间,存放一些变量和函数。具体步骤以下(browser):segmentfault

  • 页面建立GO全局对象(Global Object)对象(window对象)。
  • 加载第一个脚本文件
  • 脚本加载完毕后,进行语法分析。
  • 开始预编译
    • 查找函数声明,做为GO属性,值赋予函数体(函数声明优先)
    • 查找变量声明,做为GO属性,值赋予undefined
    • GO/window = {
          //页面加载建立GO同时,建立了document、navigator、screen等等属性,此处省略
          a: undefined,
          c: undefined,
          b: function(y){
              var x = 1;
              console.log('so easy');
          }
      }复制代码
  • 解释执行代码(直到执行函数b,该部分也被叫作词法分析
    • 建立AO活动对象(Active Object)
    • 查找形参和变量声明,值赋予undefined
    • 实参值赋给形参
    • 查找函数声明,值赋给函数体
    • 解释执行函数中的代码

      GO/window = {
          //变量随着执行流获得初始化
          a: 1,
          c: function(){
              //...
          },
          b: function(y){
              var x = 1;
              console.log('so easy');
          }
      }
      复制代码
  • 第一个脚本文件执行完毕,加载第二个脚本文件
  • 第二个文件加载完毕后,进行语法分析
  • 开始预编译
    • 重复预编译步骤 ....

预解析机制使得变量提高(Hoisting),从字面上理解就是变量和函数的声明会移动到移动到函数或者全局代码的开头位置。咱们再来分析一下小栗子加深一下理解。bash

console.log(a) // 执行以前,变量提高做为window的属性, 值被设置为undefined
var a = 'hello JS' 

/* JavaScript 仅提高声明,而不提高初始化 */

num = 6;
num++;
var num;
console.log(num) // 变量提高 值为undefined的num赋值为6,再自增 => 7

function hoistFunction() {
    foo();
    function foo() {        
        console.log('running...')    
    }
}
hoistFunction(); // 函数声明提高,能够在函数体以前执行

/* 最后一个栗子 */

alert(a) // 最后的声明为函数声明, 所以a此时为函数体
a(); // 执行 a 函数,输出10
var a = 3; // 3 赋给a
function a() {    
    alert(10)
};
alert(a) // 3
a = 6; // 6赋给a,不是一个函数,故下方执行throw error
a(); // throw error
复制代码

注: JS并不存在真正的预编译,var与function的提高实际是在语法分析阶段就处理好的。并且JS的预编译是以一个脚本文件为块的。一个脚本文件进行一次预编译,而不是全文编译完成再进行”预编译”的。函数


最佳实践 

理解了变量提高和函数提高能够使得咱们在JS上走的更远,可是咱们在开发中,不该该使用这一特性,而是要规范咱们的代码,作到可维护性和可读性。不管是变量仍是函数,都必须先声明后使用。PS:在开发中应该使用let来约束变量提高。
spa


参考资料:  code

https://developer.mozilla.org/zh-CN/docs/Glossary/Hoisting https://www.cnblogs.com/liuhe688/p/5891273.html http://dmitrysoshnikov.com/notes/note-4-two-words-about-hoisting/ https://segmentfault.com/a/1190000010187653htm

相关文章
相关标签/搜索