重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,天天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的能夠加入winter的專欄學習【原文有winter的語音】,若有侵權請聯繫我,郵箱:kaimo313@foxmail.com。前端
JavaScript 有兩種源文件,一種叫作腳本,一種叫作模塊。在 ES5 和以前的版本中,就只有一種源文件類型(就只有腳本),ES6 引入了模塊機制。node
一、腳本:是能夠由瀏覽器或者 node 環境引入執行的;模塊:只能由 JavaScript 代碼用 import 引入執行。瀏覽器
二、從概念上,腳本:具備主動性的 JavaScript 代碼段,是控制宿主完成必定任務的代碼;模塊:是被動性的 JavaScript 代碼段,是等待被調用的庫。app
三、若是要引入模塊,必須給 script 標籤添加 type="module"
。若是引入腳本,則不須要 type。異步
<script type="module" src="xxx.js"></script>
複製代碼
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
複製代碼
與 import 相對,export 聲明承擔的是導出的任務。模塊中導出變量的方式有兩種,一種是獨立使用 export 聲明,另外一種是直接在聲明型語句前添加 export 關鍵字。
一、獨立使用 export 聲明
一個 export 關鍵字加上變量名列表.
export {a, b, c};
複製代碼
二、export 能夠加在任何聲明性質的語句以前
三、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"
複製代碼
執行函數的行爲一般是:在 JavaScript 代碼執行時,註冊宿主環境的某些事件觸發的,執行的過程:就是執行函數體(函數的花括號中間的部分)。函數體:其實也是一個語句的列表。跟腳本和模塊比起來,函數體中的語句列表中多了 return 語句能夠用。
function foo(){
//Function body
}
複製代碼
async function foo(){
//Function body
}
複製代碼
function *foo(){
//Function body
}
複製代碼
async function *foo(){
//Function body
}
複製代碼
區別在於:可否使用 await 或者 yield 語句。
JavaScript 執行前,會對腳本、模塊和函數體中的語句進行預處理。預處理過程將會提早處理 var、函數聲明、class、const 和 let 這些語句,以肯定其中變量的意義。
var 聲明永遠做用於腳本、模塊和函數體這個級別,在預處理階段,不關心賦值的部分,只管在當前做用域聲明這個變量。
var a = 1;
function foo() {
console.log(a);
var a = 2;
}
foo(); // undefined
複製代碼
預處理過程在執行以前,因此有函數體級的變量 a,就不會去訪問外層做用域中的變量 a 了,而函數體級的變量 a 此時尚未賦值,因此是 undefined。
var a = 1;
function foo() {
console.log(a);
if(false) {
var a = 2;
}
}
foo(); // undefined
複製代碼
首先 if(false) 中的代碼永遠不會被執行,可是預處理階段並無論這個,var 的做用可以穿透一切語句結構,它只認腳本、模塊和函數體三種語法結構。
var a = 1;
function foo() {
var o= {a:3}
with(o) {
var a = 2;
}
console.log(o.a);
console.log(a);
}
foo(); // 2 undefine
複製代碼
早年 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
複製代碼
在全局(腳本、模塊和函數體),function 聲明表現跟 var 類似,不一樣之處在於,function 聲明不但在做用域中加入變量,還會給它賦值。
console.log(foo);
function foo(){
}
// ƒ foo(){}
複製代碼
console.log(foo);
if(true) {
function foo(){
}
}
// undefined
複製代碼
class 聲明在全局的行爲跟 function 和 var 都不同。
console.log(c);
class c{
}
// 報錯
複製代碼
var c = 1;
function foo(){
console.log(c);
class c {}
}
foo();
// 仍是報錯,這說明,class 聲明也是會被預處理的,它會在做用域中建立變量,而且要求訪問它時拋出錯誤。
複製代碼
腳本和模塊都支持一種特別的語法,叫作
指令序言(Directive Prologs)
。最先是爲了 use strict 設計的,它規定了一種給 JavaScript 代碼添加元信息的方式。
"use strict"; // 單引號也不影響它是指令序言。
function f(){
console.log(this);
};
"use strict"; // 沒有出如今最前,因此不是指令序言。
f.call(null);
// null
複製代碼
兩個 JavaScript 語法的全局機制:預處理和指令序言。winter
:「這兩個機制對於咱們解釋一些 JavaScript 的語法現象很是重要。不理解預處理機制咱們就沒法理解 var 等聲明類語句的行爲,而不理解指令序言,咱們就沒法解釋嚴格模式。」