JS执行顺序-函数声明提高、匿名函数、函数表达式

大方向上: JS 是按照 代码块 进行 编译、执行 的。

学习至:html


由于没有好好地分类。可能会比较杂。为了系统地学习,先了解几个概念。

一. <script> 区分的代码块。

JS是按照代码块 编译 和 执行的。代码块间 相互独立,可是 变量和方法 共享。前端

<script>
  alert('代码块一');
</script>
<script>
  alert('代码块二');
</script>
<script>
  var a = 3;
</script>
<script>
  console.log(a); // 3
</script>

二. 关于函数(声明式函数、赋值型函数、匿名函数、自执行函数)

  • 声明式函数 和 赋值型函数函数

    声明函数与赋值函数的区别在于: 在 JS 的预编译期间,声明式函数会被先提取出来,而后才按照顺序执行 JS代码。学习

    • 声明式同窗:
    A();  // 'A '
    function A() {
      console.log('A');
    }
    • 赋值型选手:
    B(); // error, B is not a function
    var B = function() {
      console.log('B');
    }
  • 什么是匿名函数?.net

    没有名字的函数就是匿名函数(忽然有点想笑)。code

    function() {} // 匿名函数
  • 什么是自执行函数htm

    (function() {
      console.log(3);
    })();

    须要注意,下面这样的写法会报错blog

    function() {
      console.log(3);
    }();
    缘由解析以下:
      1. function {}()其实这是一个函数声明。
      1. JS运行的时候,会对函数声明进行预编译,而后在执行其余语句。
      1. 也就是说function(){}先被预编译了。而后JS看见了()。JS一脸懵逼,这不得报错吗。
      1. 而匿名函数其实是一个语句,正常执行。

    还须要知道的是,自执行函数的标识能够是
    !function(){}() (function(){})() ~function(){}() void function(){}()
    自执行函数是能够带参数的,格式是这样的!
    function(num){ console.log(num); }(3); // 3ip


三. 预编译期 和 执行期

事实上,JS的解析分为两个阶段:预编译 和 执行期。作用域

  • 预编译期间:对本代码块中的全部 声明变量 和 函数进行处理(相似于 C语言的编译) ,但须要注意,1.此时处理函数的只是 声明式函数2.变量也只是进行了声明可是没有进行初始化和赋值

  • 编译期间:从上到下 编译 代码块。

接下来,咱们分别结合上面的 第一点(代码块) 和 第二点(函数) 食用一下。

f();  // 我是函数声明2
function f() {
  console.log('我是函数声明1');
}
function f() {
  console.log('我是函数声明2');
}

结论1:都是函数声明的状况下,后来居上的规则 没有变。

f();  // 我是函数声明
function f() {
  console.log('我是函数声明');
}
var f = function() {
  console.log('我是赋值型函数');
}

结论2:函数声明 提早于 赋值函数。

console.log(a);
var a; // undefined
console.log(b); // 程序直接报错,不往下进行

结论3:变量声明 处于 预编译阶段。证实了咱们上面的正确性。

函数下方声明了 a,被提早(提高)了。函数下方没有声明 b(以前也没有),直接报错。

<script>
  f(); // f is not defined.
</script>
<script>
  function f(){};
</script>

结论4:JS引擎是按照代码块的顺序来执行的。!!!对于还未加载的代码,是没有办法进行预处理的。这也是编译核心所在。

console.log(f);  // Function
function f() {
  console.log(1);
}
var f = 3;

结论5:函数声明提高优先级大于 变量声明,函数声明覆盖 变量声明

<script>
  console.log(a);
</script>
<script>
  console.log(3);
</script>

结论6:代码块之间若是报错,其余的代码块若是正确依旧可以正确执行。


四. 开战!!! 五是总结和整理,不想看题目的能够直接跳至 五。


代码一:

f();
var scope = 'out';
function f() {
  console.log(scope); // undefined
  var scope = 'in';
  console.log(scope); // 'in'
}
  • 变量覆盖,后面定义声明的会覆盖前面的。
  • 函数内部:先进行变量声明。

实际运行过程:

var scope = 'out';
function f() {
  var scope;    // 覆盖了外部的。
  console.log(scope);
  scope = 'in';
  console.log(scope);
}
f();

代码二:

var getName = function(){
  console.log(2);
}
function getName (){
  console.log(1);
}
getName();

答案是 2. 这题看懂了前面的话很容易:

  • 函数声明被放在了预编译阶段。
  • 后来的会覆盖前面的。
    实际运行过程
function getName (){
  console.log(1);
}
var getName = function(){
  console.log(2);
}
getName();  // 2

代码三:

getName();     // 1
function getName() {
  console.log(1);
}
var getName = function() {
  console.log(2);
}

依旧很容易,不解释。变量赋值来得太慢,不像龙卷风。


五. 总结和整理

JS的执行顺序以下:

  • 1.读入第一个代码块
  • 2.作语法分析,有错则报语法错误,并跳转到 5
  • 3.对var变量和function作 预编译(永远不会报错,由于只解析正确的)
  • 4.执行代码块,有错则报错
  • 5.若是还有下一个代码块,则读入下一个代码端,重复 2
  • 6.结束

六. 想了想,是否是没有提到变量提高?这个实际上是 JS的一个 乌龙。

ES以前,JS没有变量做用域。只有 函数做用域 和 全局做用域。

{
  var a = 3; // 咱们觉得它只是个局域变量
}
console.log(a); // 3 -- 没想到它在全局中打印出来了,(手动笑哭)

使用了 ES6 的 let 以后, {} 内即为一个 变量做用域。

{
  let a = 3;
}
console.log(a); // error

变量提高 完。


complete.

相关文章
相关标签/搜索