成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

javascript中為什么我們不能直接使用export?

URLOS / 2178人閱讀

摘要:我們可以認(rèn)為,宏任務(wù)中還有微任務(wù)這里不再多做解釋可能會執(zhí)行的代碼包括腳本模塊和函數(shù)體。聲明聲明永遠(yuǎn)作用于腳本模塊和函數(shù)體這個級別,在預(yù)處理階段,不關(guān)心賦值的部分,只管在當(dāng)前作用域聲明這個變量。

相信很多人最開始時都有過這樣的疑問
假如我的項(xiàng)目目錄下有一個 index.html, index.js 于是我像這樣寫

在瀏覽器之間打開index.html,發(fā)現(xiàn)

這到底是為什么?為什么連chrome瀏覽器竟然還不完全支持es6的語法?
其實(shí),ES6之前已經(jīng)出現(xiàn)了js模塊加載的方案,最主要的是CommonJS和AMD規(guī)范。commonjs主要應(yīng)用于服務(wù)器,實(shí)現(xiàn)同步加載,如nodejs。AMD規(guī)范應(yīng)用于瀏覽器,如requirejs,為異步加載。同時還有CMD規(guī)范,為同步加載方案如seaJS。
ES6在語言規(guī)格的層面上,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡單,完全可以取代現(xiàn)有的CommonJS和AMD規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。話有回到我們剛才的問題 "為什么chrome瀏覽器竟然還不完全支持es6的語法"

首先,JavaScript有兩種源文件,一種叫做腳本,一種叫做模塊。這個區(qū)分是在ES6引入了模塊機(jī)制開始的,在ES5和之前的版本中,就只有一種源文件類型(就只有腳本)。

腳本是可以由瀏覽器或者node環(huán)境引入執(zhí)行的,而模塊只能由JavaScript代碼用import引入執(zhí)行。

從概念上,我們可以認(rèn)為腳本具有主動性的JavaScript代碼段,是控制宿主完成一定任務(wù)的代碼;而模塊是被動性的JavaScript代碼段,是等待被調(diào)用的庫。

我們對標(biāo)準(zhǔn)中的語法產(chǎn)生式做一些對比,不難發(fā)現(xiàn),實(shí)際上模塊和腳本之間的區(qū)別僅僅在于是否包含import 和 export。

腳本是一種兼容之前的版本的定義,在這個模式下,沒有import就不需要處理加載“.js”文件問題。

現(xiàn)代瀏覽器可以支持用script標(biāo)簽引入模塊或者腳本,如果要引入模塊,必須給script標(biāo)簽添加type=“module”。如果引入腳本,則不需要type。

這樣,就回答了我們標(biāo)題中的問題,script標(biāo)簽如果不加type=“module”,默認(rèn)認(rèn)為我們加載的文件是腳本而非模塊,如果我們在腳本中寫了export,當(dāng)然會拋錯。

其中腳本中可以包含語句。模塊中可以包含三種內(nèi)容:import聲明,export聲明和語句。先來講講import聲明和export聲明。

import聲明
我們首先來介紹一下import聲明,import聲明有兩種用法,一個是直接import一個模塊,另一個是帶from的import,它能引入模塊里的一些信息。

import "mod"; //引入一個模塊
import v from "mod"; //把模塊默認(rèn)的導(dǎo)出值放入變量v
直接import一個模塊,只是保證了這個模塊代碼被執(zhí)行,引用它的模塊是無法獲得它的任何信息的。

帶from的import意思是引入模塊中的一部分信息,可以把它們變成本地的變量。

帶from的import細(xì)分又有三種用法,我們可以分別看下例子:

import x from "./a.js" 引入模塊中導(dǎo)出的默認(rèn)值。
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"

語法要求不帶as的默認(rèn)值永遠(yuǎn)在最前。注意,這里的變量實(shí)際上仍然可以受到原來模塊的控制。
我們看一個例子,假設(shè)有兩個模塊a和b。我們在模塊a中聲明了變量和一個修改變量的函數(shù),并且把它們導(dǎo)出。我們用b模塊導(dǎo)入了變量和修改變量的函數(shù)。

模塊a:

export var a = 1;
export function modify(){
    a = 2;
}

模塊b:

import {a, modify} from "./a.js";
console.log(a);
modify(); 
console.log(a);

當(dāng)我們調(diào)用修改變量的函數(shù)后,b模塊變量也跟著發(fā)生了改變。這說明導(dǎo)入與一般的賦值不同,導(dǎo)入后的變量只是改變了名字,它仍然與原來的變量是同一個。

export聲明
我們再來說說export聲明。與import相對,export聲明承擔(dān)的是導(dǎo)出的任務(wù)。

模塊中導(dǎo)出變量的方式有兩種,一種是獨(dú)立使用export聲明,另一種是直接在聲明型語句前添加export關(guān)鍵字。

獨(dú)立使用export聲明就是一個export關(guān)鍵字加上變量名列表,例如:

export {a, b, c};
我們也可以直接在聲明型語句前添加export關(guān)鍵字,這里的export可以加在任何聲明性質(zhì)的語句之前,整理如下:

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

export還有一種特殊的用法,就是跟default聯(lián)合使用。export default 表示導(dǎo)出一個默認(rèn)變量值,它可以用于function和class。這里導(dǎo)出的變量是沒有名稱的,可以使用import x from "./a.js"這樣的語法,在模塊中引入。

export default 還支持一種語法,后面跟一個表達(dá)式,例如:

var a = {};
export default a;

但是,這里的行為跟導(dǎo)出變量是不一致的,這里導(dǎo)出的是值,導(dǎo)出的就是普通變量a的值,以后a的變化與導(dǎo)出的值就無關(guān)了,修改變量a,不會使得其他模塊中引入的default值發(fā)生改變。

說到這里,就來大致說說export和export default的區(qū)別

1.export與export default均可用于導(dǎo)出常量、函數(shù)、文件、模塊等
2.在一個文件或模塊中,export、import可以有多個,export default僅有一個
3.通過export方式導(dǎo)出,在導(dǎo)入時要加{ },export default則不需要

或者我們可以這樣理解,export default的本質(zhì)其實(shí)就是講后面的值付給default變量,然后你可以為它取你想要的變量

所以
export default 1   // 執(zhí)行
export 1 // 報(bào)錯    

第二行報(bào)錯正式是因?yàn)闆]有指定對外的接口,而第一句指定為default

在import語句前無法加入export,但是我們可以直接使用export from語法。

export a from "a.js"

JavaScript引擎除了執(zhí)行腳本和模塊之外,還可以執(zhí)行函數(shù)。而函數(shù)體跟腳本和模塊有一定的相似之處

再來談一下函數(shù)體

執(zhí)行函數(shù)的行為通常是在JavaScript代碼執(zhí)行時,注冊宿主環(huán)境的某些事件觸發(fā)的,而執(zhí)行的過程,就是執(zhí)行函數(shù)體(函數(shù)的花括號中間的部分)。

先看一個例子,感性地理解一下:

setTimeout(function(){
    console.log("go go go");
}, 10000)

這段代碼通過setTimeout函數(shù)注冊了一個函數(shù)給宿主,當(dāng)一定時間之后,宿主就會執(zhí)行這個函數(shù)。

我們可以認(rèn)為,宏任務(wù)中(還有微任務(wù),這里不再多做解釋)可能會執(zhí)行的代碼包括“腳本(script)”“模塊(module)”和“函數(shù)體(function body)”。正因?yàn)檫@樣的相似性。

函數(shù)體其實(shí)也是一個語句的列表。跟腳本和模塊比起來,函數(shù)體中的語句列表中多了return語句可以用。

函數(shù)體實(shí)際上有四種,下面,分別介紹一下。

普通函數(shù)體,例如:
function foo(){
    //
}
異步函數(shù)體,例如:
async function foo(){
    //
}
生成器函數(shù)體,例如:
function *foo(){
    //
}
異步生成器函數(shù)體,例如:
async function *foo(){
    //
}

上面四種函數(shù)體的區(qū)別在于:能否使用await或者yield語句。

說完了三種語法結(jié)構(gòu),再來介紹下JavaScript語法的全局機(jī)制(非嚴(yán)格模式):預(yù)處理。
這對于我們解釋一些JavaScript的語法現(xiàn)象非常重要。不理解預(yù)處理機(jī)制我們就無法理解var等聲明類語句的行為。
var聲明
var聲明永遠(yuǎn)作用于腳本、模塊和函數(shù)體這個級別,在預(yù)處理階段,不關(guān)心賦值的部分,只管在當(dāng)前作用域聲明這個變量。

還是先舉個例子。

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

這段代碼聲明了一個腳本級別的a,又聲明了foo函數(shù)體級別的a,我們注意到,函數(shù)體級的var出現(xiàn)在console.log語句之后。

但是預(yù)處理過程在執(zhí)行之前,所以有函數(shù)體級的變量a,就不會去訪問外層作用域中的變量a了,而函數(shù)體級的變量a此時還沒有賦值,所以是undefined。再看一個情況:

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

這段代碼比上一段代碼在var a = 2之外多了一段if,我們知道if(false)中的代碼永遠(yuǎn)不會被執(zhí)行,但是預(yù)處理階段并不管這個,var的作用能夠穿透一切語句結(jié)構(gòu),它只認(rèn)腳本、模塊和函數(shù)體三種語法結(jié)構(gòu)。所以這里結(jié)果跟前一段代碼完全一樣,我們會得到undefined。

看下一個例子。

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

在這個例子中,引入了with語句,用with(o)創(chuàng)建了一個作用域,并把o對象加入詞法環(huán)境,在其中使用了var a = 2;語句。
在預(yù)處理階段,只認(rèn)var中聲明的變量,所以同樣為foo的作用域創(chuàng)建了a這個變量,但是沒有賦值。
在執(zhí)行階段,當(dāng)執(zhí)行到var a = 2時,作用域變成了with語句內(nèi),這時候的a被認(rèn)為訪問到了對象o的屬性a,所以最終執(zhí)行的結(jié)果,我們得到了2和undefined。
這個行為是JavaScript公認(rèn)的設(shè)計(jì)失誤之一(類似的還有雙等 ==),一個語句中的a在預(yù)處理階段和執(zhí)行階段被當(dāng)做兩個不同的變量,嚴(yán)重違背了直覺,但是今天,在JavaScript設(shè)計(jì)原則“don’t break the web”之下,已經(jīng)無法修正了,所以這里需要特別的注意。
因?yàn)樵缒闖avaScript沒有l(wèi)et和const,只能用var,又因?yàn)関ar除了腳本和函數(shù)體都會穿透,人民群眾發(fā)明了“立即執(zhí)行的函數(shù)表達(dá)式(IIFE)”這一用法,用來產(chǎn)生作用域,例如:

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);
}

這段代碼很經(jīng)典,常常在實(shí)際開發(fā)中見到,也經(jīng)常被用作面試題,為文檔添加了20個div元素,并且綁定了點(diǎn)擊事件,打印它們的序號。
我們通過IIFE在循環(huán)內(nèi)構(gòu)造了作用域,每次循環(huán)都產(chǎn)生一個新的環(huán)境記錄,這樣,每個div都能訪問到環(huán)境中的i。
如果我們不用IIFE:

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);
}

這段代碼的結(jié)果將會是點(diǎn)每個div都打印20,因?yàn)槿种挥幸粋€i,執(zhí)行完循環(huán)后,i變成了20。

function聲明

function聲明的行為原本跟var非常相似,但是在最新的JavaScript標(biāo)準(zhǔn)中,對它進(jìn)行了一定的修改,這讓情況變得更加復(fù)雜了。
在全局(腳本、模塊和函數(shù)體),function聲明表現(xiàn)跟var相似,不同之處在于,function聲明不但在作用域中加入變量,還會給它賦值。
我們看一下function聲明的例子

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

}

這里聲明了函數(shù)foo,在聲明之前,我們用console.log打印函數(shù)foo,我們可以發(fā)現(xiàn),已經(jīng)是函數(shù)foo的值了。
function聲明出現(xiàn)在if等語句中的情況有點(diǎn)復(fù)雜,它仍然作用于腳本、模塊和函數(shù)體級別,在預(yù)處理階段,仍然會產(chǎn)生變量,它不再被提前賦值:

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

    }
}

這段代碼得到undefined。如果沒有函數(shù)聲明,則會拋出錯誤。
這說明function在預(yù)處理階段仍然發(fā)生了作用,在作用域中產(chǎn)生了變量,沒有產(chǎn)生賦值,賦值行為發(fā)生在了執(zhí)行階段。
出現(xiàn)在if等語句中的function,在if創(chuàng)建的作用域中仍然會被提前,產(chǎn)生賦值效果。

class聲明

class聲明在全局的行為跟function和var都不一樣。
在class聲明之前使用class名,會拋錯:

console.log(c);
class c{

}

這段代碼我們試圖在class前打印變量c,我們得到了個錯誤,這個行為很像是class沒有預(yù)處理,但是實(shí)際上并非如此。

我們看個復(fù)雜一點(diǎn)的例子:

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

這個例子中,我們把class放進(jìn)了一個函數(shù)體中,在外層作用域中有變量c。然后試圖在class之前打印c。

執(zhí)行后,我們看到,仍然拋出了錯誤,如果去掉class聲明,則會正常打印出1,也就是說,出現(xiàn)在后面的class聲明影響了前面語句的結(jié)果。

這說明,class聲明也是會被預(yù)處理的,它會在作用域中創(chuàng)建變量,并且要求訪問它時拋出錯誤。

class的聲明作用不會穿透if等語句結(jié)構(gòu),所以只有寫在全局環(huán)境才會有聲明作用。

這樣的class設(shè)計(jì)比function和var更符合直覺,而且在遇到一些比較奇怪的用法時,傾向于拋出錯誤。

按照現(xiàn)代語言設(shè)計(jì)的評價標(biāo)準(zhǔn),及早拋錯是好事,它能夠幫助我們盡量在開發(fā)階段就發(fā)現(xiàn)代碼的可能問題。

針對以上問題以及一些不嚴(yán)謹(jǐn)?shù)膯栴}和一些引擎難以優(yōu)化的錯誤,出現(xiàn)了嚴(yán)格模式

設(shè)立"嚴(yán)格模式"的目的,主要有以下幾個:

  - 消除Javascript語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;

  - 消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全;

  - 提高編譯器效率,增加運(yùn)行速度;

  - 為未來新版本的Javascript做好鋪墊。

其中 ES6 的模塊自動采用嚴(yán)格模式,不管你有沒有在模塊頭部加上"use strict";

至于平常開發(fā)時我們到底要不要使用嚴(yán)格模式以及包括要不要使用typescript?每個人都有每個人的觀點(diǎn)!那么,在開發(fā)中你是否推薦用嚴(yán)格模式來"約束"你的代碼及風(fēng)格呢?

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109992.html

相關(guān)文章

  • 很全很全的JavaScript的模塊講解

    摘要:該模塊實(shí)現(xiàn)方案主要包含與這兩個關(guān)鍵字,其允許某個模塊對外暴露部分接口并且由其他模塊導(dǎo)入使用。由于在服務(wù)端的流行,的模塊形式被不正確地稱為。以上所描述的模塊載入機(jī)制均定義在中。 最近一直在搞基礎(chǔ)的東西,弄了一個持續(xù)更新的github筆記,可以去看看,誠意之作(本來就是寫給自己看的……)鏈接地址:Front-End-Basics 此篇文章的地址:JavaScript的模塊 基礎(chǔ)筆記...

    lufficc 評論0 收藏0
  • Nodejs模塊加載與ES6模塊加載實(shí)現(xiàn)

    摘要:以后需要引用模塊的變量函數(shù)類就在這個模塊對象的取出,即使再次進(jìn)來模塊也不會重新執(zhí)行,只會從緩存獲取。所以對相同模塊的再次加載都是優(yōu)先緩存方式,核心模塊的緩存檢查依然優(yōu)先于文件模塊。內(nèi)建模塊導(dǎo)出啟動會生成全局變量,提供方法協(xié)助加載內(nèi)建模塊。 原始時代 作為一門語言的引入代碼方式,相較于其他如PHP的include和require,Ruby的require,Python的import機(jī)制,...

    陳江龍 評論0 收藏0
  • JavaScriptAMD和ES6模塊的導(dǎo)入導(dǎo)出對比

    摘要:每個模塊內(nèi)部,變量代表當(dāng)前模塊。這個變量是一個對象,它的屬性即是對外的接口。加載某個模塊,其實(shí)是加載該模塊的屬性。為了方便,為每個模塊提供一個變量,指向。這等同在每個模塊頭部,有一行這樣的命令。 我們前端在開發(fā)過程中經(jīng)常會遇到導(dǎo)入導(dǎo)出功能,在導(dǎo)入時,有時候是require,有時候是import在導(dǎo)出時,有時候是exports,module.exports,有時候是export,expo...

    劉明 評論0 收藏0
  • underscore 系列之如何寫自己的 underscore

    摘要:因?yàn)樵谖⑿判〕绦蛑?,和都是,加上又?qiáng)制使用嚴(yán)格模式,為,掛載就會發(fā)生錯誤,所以就有人又發(fā)了一個,代碼變成了這就是現(xiàn)在的樣子。 前言 在 《JavaScript 專題系列》 中,我們寫了很多的功能函數(shù),比如防抖、節(jié)流、去重、類型判斷、扁平數(shù)組、深淺拷貝、查找數(shù)組元素、通用遍歷、柯里化、函數(shù)組合、函數(shù)記憶、亂序等,可以我們該如何組織這些函數(shù),形成自己的一個工具函數(shù)庫呢?這個時候,我們就要借...

    Invoker 評論0 收藏0
  • 再談JavaScript模塊化

    摘要:應(yīng)用日益復(fù)雜,模塊化已經(jīng)成為一個迫切需求。異步模塊加載機(jī)制。引用的資源列表太長,懶得回調(diào)函數(shù)中寫一一對應(yīng)的相關(guān)參數(shù)假定這里引用的資源有數(shù)十個,回調(diào)函數(shù)的參數(shù)必定非常多這就是傳說中的 簡述 緣起 模塊通常是指編程語言所提供的代碼組織機(jī)制,利用此機(jī)制可將程序拆解為獨(dú)立且通用的代碼單元。 模塊化主要是解決代碼分割、作用域隔離、模塊之間的依賴管理以及發(fā)布到生產(chǎn)環(huán)境時的自動化打包與處理等多個方面...

    MorePainMoreGain 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<