重學前端學習筆記(三十)--JavaScript語法的基本規則

筆記說明

重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,天天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的能夠加入winter的專欄學習【原文有winter的語音】,若有侵權請聯繫我,郵箱:kaimo313@foxmail.com。前端

1、腳本和模塊

JavaScript 有兩種源文件,一種叫作腳本,一種叫作模塊。在 ES5 和以前的版本中,就只有一種源文件類型(就只有腳本),ES6 引入了模塊機制。node

1.一、區別

一、腳本:是能夠由瀏覽器或者 node 環境引入執行的;模塊:只能由 JavaScript 代碼用 import 引入執行。瀏覽器

二、從概念上,腳本:具備主動性的 JavaScript 代碼段,是控制宿主完成必定任務的代碼;模塊:是被動性的 JavaScript 代碼段,是等待被調用的庫。app

三、若是要引入模塊,必須給 script 標籤添加 type="module"。若是引入腳本,則不須要 type。異步

<script type="module" src="xxx.js"></script>
複製代碼

JavaScript程序

1.二、import 聲明

import 聲明有兩種用法,一個是直接 import 一個模塊,另外一個是帶 from 的 import,它能引入模塊裏的一些信息。async

import "mod"; // 引入一個模塊
import v from "mod";  // 把模塊默認的導出值放入變量 v
複製代碼

一、帶 from 的 import 細分有三種用法函數

  • import x from "./a.js" 引入模塊中導出的默認值。
  • import {a as x, modify} from "./a.js"; 引入模塊中的變量。
  • import * as x from "./a.js" 把模塊中全部的變量以相似對象屬性的方式引入。

第一種方式能夠跟後兩種組合使用。學習

  • import d, {a as x, modify} from "./a.js"
  • import d, * as x from "./a.js"

二、例子ui

假設有兩個模塊 a 和 b。咱們在模塊 a 中聲明瞭變量和一個修改變量的函數,而且把它們導出。用 b 模塊導入了變量和修改變量的函數。this

模塊 a

export var a = 1;

export function modify(){
    a = 2;
}
複製代碼

模塊 b

import {a, modify} from "./a.js";

console.log(a); // 1

modify();

console.log(a); // 2
複製代碼

1.三、export 聲明

與 import 相對,export 聲明承擔的是導出的任務。模塊中導出變量的方式有兩種,一種是獨立使用 export 聲明,另外一種是直接在聲明型語句前添加 export 關鍵字。

一、獨立使用 export 聲明

一個 export 關鍵字加上變量名列表.

export {a, b, c};
複製代碼

二、export 能夠加在任何聲明性質的語句以前

  • var
  • function (含 async 和 generator)
  • class
  • let
  • const

三、export default 表示導出一個默認變量值,它能夠用於 function 和 class。這裏導出的變量是沒有名稱的,可使用 import x from "./a.js" 這樣的語法,在模塊中引入。

四、export default 還支持一種語法,後面跟一個表達式

// 注意:a 的變化與導出的值就無關,修改變量 a,不會使得其餘模塊中引入的 default 值發生改變。
var a = {};
export default a;
複製代碼

五、在 import 語句前沒法加入 export,可是能夠直接使用 export from 語法。

export a from "a.js"
複製代碼

2、函數體

執行函數的行爲一般是:在 JavaScript 代碼執行時,註冊宿主環境的某些事件觸發的,執行的過程:就是執行函數體(函數的花括號中間的部分)。函數體:其實也是一個語句的列表。跟腳本和模塊比起來,函數體中的語句列表中多了 return 語句能夠用。

2.一、普通函數體

function foo(){
    //Function body
}
複製代碼

2.二、異步函數體

async function foo(){
    //Function body
}
複製代碼

2.三、生成器函數體

function *foo(){
    //Function body
}
複製代碼

2.四、異步生成器函數體

async function *foo(){
    //Function body
}
複製代碼

2.五、上面四種函數體的區別

區別在於:可否使用 await 或者 yield 語句。

區別

3、預處理

JavaScript 執行前,會對腳本、模塊和函數體中的語句進行預處理。預處理過程將會提早處理 var、函數聲明、class、const 和 let 這些語句,以肯定其中變量的意義。

3.一、var 聲明

var 聲明永遠做用於腳本、模塊和函數體這個級別,在預處理階段,不關心賦值的部分,只管在當前做用域聲明這個變量。

3.1.一、例子一

var a = 1;

function foo() {
    console.log(a);
    var a = 2;
}

foo(); // undefined
複製代碼

預處理過程在執行以前,因此有函數體級的變量 a,就不會去訪問外層做用域中的變量 a 了,而函數體級的變量 a 此時尚未賦值,因此是 undefined。

3.1.二、例子二

var a = 1;

function foo() {
    console.log(a);
    if(false) {
        var a = 2;
    }
}

foo(); // undefined
複製代碼

首先 if(false) 中的代碼永遠不會被執行,可是預處理階段並無論這個,var 的做用可以穿透一切語句結構,它只認腳本、模塊和函數體三種語法結構。

3.1.三、例子三

var a = 1;

function foo() {
    var o= {a:3}
    with(o) {
        var a = 2;
    }
    console.log(o.a);
    console.log(a);
}

foo(); // 2 undefine
複製代碼

3.1.四、例子四

早年 JavaScript 沒有 let 和 const,只能用 var,而 var 除了腳本和函數體都會穿透,可用當即執行的函數表達式(IIFE)來產生做用域。

for(var i = 0; i < 20; i ++) {
    void function(i){
        var div = document.createElement("div");
        div.innerHTML = i;
        div.onclick = function(){
            console.log(i);
        }
        document.body.appendChild(div);
    }(i);
}
// 點擊div對應的序號

for(var i = 0; i < 20; i ++) {
    var div = document.createElement("div");
    div.innerHTML = i;
    div.onclick = function(){
        console.log(i);
    }
    document.body.appendChild(div);
}
// 點擊div全是20
複製代碼

3.二、function 聲明

在全局(腳本、模塊和函數體),function 聲明表現跟 var 類似,不一樣之處在於,function 聲明不但在做用域中加入變量,還會給它賦值。

3.2.一、例子一

console.log(foo);
function foo(){

}

// ƒ foo(){}
複製代碼

3.2.二、例子二

console.log(foo);
if(true) {
    function foo(){

    }
}
// undefined
複製代碼

3.三、class 聲明

class 聲明在全局的行爲跟 function 和 var 都不同。

3.3.一、例子一

console.log(c);
class c{

}
// 報錯
複製代碼

3.3.二、例子二

var c = 1;
function foo(){
    console.log(c);
    class c {}
}
foo();

// 仍是報錯,這說明,class 聲明也是會被預處理的,它會在做用域中建立變量,而且要求訪問它時拋出錯誤。
複製代碼

4、指令序言機制

腳本和模塊都支持一種特別的語法,叫作指令序言(Directive Prologs)。最先是爲了 use strict 設計的,它規定了一種給 JavaScript 代碼添加元信息的方式。

"use strict"; // 單引號也不影響它是指令序言。
function f(){
    console.log(this);
};
"use strict"; // 沒有出如今最前,因此不是指令序言。
f.call(null);

// null
複製代碼

我的總結

兩個 JavaScript 語法的全局機制:預處理和指令序言。winter:「這兩個機制對於咱們解釋一些 JavaScript 的語法現象很是重要。不理解預處理機制咱們就沒法理解 var 等聲明類語句的行爲,而不理解指令序言,咱們就沒法解釋嚴格模式。」

相關文章
相關標籤/搜索