摘要:副作用,無副作用可執(zhí)行和關(guān)鍵詞。和不能像一樣同一個下聲明多次和不會像一樣變量聲明提升原因是,存在因此不能,賦值前使用變量。
關(guān)鍵詞:
let ,const, blocking scope ,temporal dead zone,redeclaration,reassignment,immutable,initializer
一直以來都有用let和const,看似深入學(xué)習(xí)過,但其實沒有真正完全理解,在模棱兩可的用,之前在城西的一次面試就被問的啞口無言,所以我將通過mdn的資料針對性地學(xué)習(xí)let 和 const,并且會寫一些易懂的例子,也會涉及到一些規(guī)范里的內(nèi)容。
block,statement和expression的區(qū)別是什么?
為什么選擇了"let"作為block-scoped variable declaration?
let和const不會像var一樣綁定值到global 對象!
let和const不能像var一樣同一個scope下聲明多次!
let和const不會像var一樣變量聲明提升!
letlet聲明了一個block scope local variable,選擇性為其賦值。
let x = 1; if(x === 1) { let x = 2; console.log(x); // 2 } console.log(x); // 1
let使變量的作用域限制于block,statement,或者expression。
block,statement和expression的區(qū)別是什么?這對于作用域的判別很有用。
block curly bracket,circle bracket等等。
statement js由各種語句組成,Control Flow,F(xiàn)unction,Iterations等等。
expression 副作用,無副作用;可執(zhí)行和關(guān)鍵詞。
很多東西自以為會了,然而實際上可能只是走馬觀花,所以回過頭來將基礎(chǔ)撿起來,可能比學(xué)習(xí)高階技術(shù)更重要。
group zero or more statements
block 由 curly bracket包裹(彎曲的支架比花括號更形象)
blcok statement 在其他語言里通常叫做computed statement,之所以叫復(fù)合語句,因為JavaScript會一句一句執(zhí)行,在block 里可以有多個statement,
var x = 1; let y = 1; if (true) { var x = 2; let y = 2; } console.log(x); console.log(y);
這里的block指的是{var x = 2; let y = 2;},注意:不包括if(true),因為它位于curly bracket之外。
Block Statement Syntax:
{ StatementList }
比起block statement syntax,更重要的是Block Scoping Rules:
1.var rule:
一種是全局定義,一種是函數(shù)局部定義。局部函數(shù)變量的訪問會從作用域鏈由下往上找。但是這種老式的變量命名符不提倡再使用。
var x = 1; { var x = 2; } console.log(x); // 2
2.let && const rule:
let x = 1; { x = 2; } console.log(x); // 2
const x = 1; { x = 2; } console.log(x); // TypeError: Assignment to constant variable.
在{ }中應(yīng)該用var / let /const聲明x,而不是讓其變成全局變量導(dǎo)致與外部的x變量沖突。
3.function rule:
foo("outside"); // TypeError: foo is not a function { function foo(location) { console.log("foo is called" + location); } foo("inside"); // foo is called inside }
更準(zhǔn)確一些的說法是,block statement阻止function declaration 被hoisted(變量提升)到作用域的頂部。這種定義方式與函數(shù)表達式類似。
函數(shù)表達式的變量提升規(guī)則是下面這樣:
foo("before"); // Uncaught TypeError: foo is not a function var foo = function(location) { console.log("foo is called" + location); } foo("after"); // foo is called after
function塊作用域規(guī)則同上:
foo("before"); // TypeError: foo is not a function { function foo(location) { console.log("foo is called" + location); } } foo("after"); // foo is called after
與函數(shù)表達式不會提升到var foo = function(){}一樣;{}內(nèi)部定義的function,不會提升到{}之前。而這正是function的blocking statement rule。
var array = [1, 2, 3]; for (i=0; iJavascript 應(yīng)用就是由符合語法的多個statement組成的。
一條statement可能跨越多行。
多條statement也可能在一行中出現(xiàn),每一句由一個分號標(biāo)記。
statement可以分為Control flow,Declarations,Functions and classed,Iterations,Others。
Control flow包括Block,break,continue,Empty,if...else,switch,throw,try...catch。
Declarations包括var,let,const。
Functions and classed包括function,function *,async function,return,class。
Iterations包括:do...while,for,for each...in,for...in,for...of,while。
Others包括:debugger,export,import,import.meta,label,with。什么是Expressions?
expression指的是任何可以解析為value的代碼單元。
2種有效的expression:有side effect的,例如x = 7;某些情況下執(zhí)行并且解析為值,例如3 + 4。
還有2種分類,一類是執(zhí)行后為number,string,boolean型的;一類是關(guān)鍵詞類型,這種類型又分為Primary expression和Left-hand-side expressions。
Primary expressions
Basic keywords and general expressions in JavaScript,例如this,grouping operator.this
當(dāng)前對象的引用,2種調(diào)用對象方法的方式this["propertyName"],this.propertyName。
function validate(obj, lowval, hival) { if ((obj.value < lowval) || (obj.value > hival)) console.log("Invalid Value!"); }Enter a number between 18 and 99:
上面的例子中,this指代input這個DOM對象,它由于具有屬性value,因此可以調(diào)用validate函數(shù),并且每次輸入值發(fā)生變化時都觸發(fā)onChange回調(diào)。
Grouping operator
()
var a = 1; var b = 2; var c = 3; // default precedence a + b * c // 7 // evaluated by default like this (a + b) * c // 9Left-hand-side expressions
左值是賦值的目標(biāo),例如new,super,Spread operator。
new 創(chuàng)建一個用戶自定義object類型var objectName = new objectType([param1, param2, ..., paramN]);super 調(diào)用當(dāng)前object的父object上的函數(shù),在class中常用。
super([arguments]); // 調(diào)用parent constructor super.functionOnParent([arguments]); // 調(diào)用parent上的方法Spread operator 允許表達式被展開,可以是函數(shù)參數(shù)處展開,也可以是數(shù)組迭代處展開。
數(shù)組某處插入數(shù)組元素。var parts = ["shoulders", "knees"]; var lyrics = ["head", ...parts, "and", "toes"];一個完整數(shù)組作為參數(shù)傳入函數(shù)
function f(x,y,z){} var args = [0,1,2]; f(...args);通過對block,statement,expression的回顧。我們發(fā)現(xiàn),其實塊作用域不僅僅是curly bracket,{}。在for(){},for(key in object),for(item of array)等等的()內(nèi),其實也屬于塊作用域,不僅僅是if else的{},for{}中的{}才算是塊作用域,let都有效。
let a = 1; for(let a = 2; a<3; a++){ console.log(a); }; console.log(a); // 2 1let key = "hello world"; for(let key in {foo:1,bar:2}){ console.log(key); } console.log(key); // foo bar hello world若是不用let,會將全局的key override,所以在for系列的循環(huán)控制語句中使用let很有必要。
let key = "hello world"; for(key in {foo:1,bar:2}){ console.log(key); } console.log(key); // foo bar bar在for(item of array)中也一樣。
let item = 4; for(let item of [1,2,3]){ console.log(item); } console.log(item); // 1 2 3 4let item = 4; for(item of [1,2,3]){ console.log(item); } console.log(item); // 1 2 3 3使用let以后,井水不犯河水,不用擔(dān)心改寫全局中的同名變量,但是一定要明確,let不僅僅作用于{},()也同樣作用。
為什么選擇了"let"作為block-scoped variable declaration?可以看這個stack overflow上的question:Why was the name "let" chosen for block-scoped variable declarations in JavaScript?。
有兩點比較重要:參考了scala,F(xiàn)#等語言里比variable用作更高級抽象的let;
一個很有趣的解釋:let myPet = "dog", let my pet be a dog。
let和const不會像var一樣綁定值到global 對象!眾所周知,var會綁定變量到global對象(不一定是window,global,還可能是Vue instance),但是let和const不會。
var foo = 1; let bar = 2; const baz = 3; console.log(this.foo, this.bar, this.baz); //1 undefined undefinedlet和const不能像var一樣同一個scope下聲明多次!let foo = 1; let foo = 2; // Uncaught SyntaxError: Identifier "foo" has already been declaredconst foo = 1; const foo = 2; // Uncaught SyntaxError: Identifier "foo" has already been declaredvar foo = 1; var foo = 2; // everything is oklet和const不會像var一樣變量聲明提升!原因是:const,let存在temporal dead zone!
因此不能let ,const賦值前使用變量。
在說變量提升之前,先了解一個概念,Temporal Dead Zone,指的是從block創(chuàng)建到初始化完成之間的時間。用var不會存在Temporal Dead Zone,因為用var聲明的變量,初始值立即默認(rèn)賦予undefined,不會像let這樣,存在Temporal Dead Zone,不會立即為其賦undefined,所以會報ReferenceError錯誤。
function do_something() { console.log(bar); // undefined console.log(foo); // ReferenceRrror var bar = 1; let foo = 2; }正是由于let存在temporal dead zone,沒有立即為變量賦初始值為undefined,所以typeof的結(jié)果為ReferenceRrror。
console.log(typeof undeclaredVariable); // undefined console.log(typeof i);// ReferenceError,存在temporal dead zone let i = 10;var,let,const都會變量提升,但是僅僅是對var foo;let foo;const foo的提升,而不是var foo = 1;let foo =1;const foo = 1;整體提升!let不會立即為變量賦undefined初值是好是壞呢?當(dāng)然是好事!這樣將變量的管理更加精細(xì),避免引用重名變量覆蓋后出現(xiàn)bug還發(fā)現(xiàn)不了的情況。
還有兩個temporal dead zone的情況:
function test(){ var foo = 33; if (true) { let foo = (foo + 55); // ReferenceError,let foo 存在temporal dead zone } } test();function go(n) { console.log(n); for (let n of n.a) { // ReferenceError,let n 存在temporal dead zone console.log(n); } } go({a: [1, 2, 3]});const其實在let模塊已經(jīng)寫了很多關(guān)于const的內(nèi)容,所以在這里就寫一些const特有的特性。
const也是block-scoped的,和用let定義的變量類似。
不可以修改變量值,也就是不可以reassignment,并不是immutable
不可以重新定義
const foo = [value],value可以是function,而let也可以!
必須為const賦一個初值且存在temporal dead zone,比let更加嚴(yán)格!
const foo = 1; { const foo =2; }const foo = 1; foo = 2; // Uncaught TypeError: Assignment to constant variable.const foo = 1; const foo = 2; // Uncaught SyntaxError: Identifier "foo" has already been declaredlet定義的變量賦值function會有什么錯誤提示呢?
let foo = function(){ console.log("foo"); } foo();// foo不會報錯,但是因為let可以reassignment,所以不如const更加安全,因為一般來說,我們創(chuàng)建一個函數(shù)以后,不太會再去覆蓋這個函數(shù)。
const不可以reassignment,并不是immutable什么意思?immutable指的是變量的值完全不可改變,例如"hi",{foo:1,bar:2},若這個字符串和對象是immutable的,那么"hi"完全不能被修改,而且對象{foo:1,bar:2}也完全不能修改,也就是說它的屬性foo和bar值都不能修改,但是const只是約束了reassignment,沒有約束mutable。
下面這種寫法是完全OK的:
const obj = { foo: 1, bar: 2, } obj.foo = 3; console.log(obj); // {foo: 3,bar:2}cosnt不賦初值有什么報錯?cosnt foo;// Uncaught SyntaxError: Missing initializer in const declaration假設(shè)修改了原型鏈上的屬性會怎樣?const foo = "foo"; foo.length = 5; // return 5 console.log(foo.length); // 3我們可以看出,const還是很包容的,即使你試圖修改原型鏈上的屬性,也不會報錯,他只是一笑而過,并且這種修改不會生效。
const真的很嚴(yán)格!
類型 是否必須賦值 是否存在temporal dead zone 是否支持redeclaration 是否支持reassignment var 否 否 是 是 let 否 是 否 是 const 是 是 否 否 2018年12月18日01:45更新
es6的const和java的final之間的對比關(guān)于const不支持reassignment這一點,java中的關(guān)鍵字final與之對應(yīng),表示這個常量只能賦值一次。
但是java多了一個類常量的概念,所謂類常量,指的其實就是一個常量在類的多個方法中有調(diào)用, 也就是static final foo = "foo";這樣的形式。
在js中就沒有這種講究了,const就是代表常量,我才不管你是不是類的還是實例的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98166.html
摘要:聲明的變量只在其所在的代碼塊內(nèi)有效。只要在聲明之前使用這些變量,就會報錯。在語法上稱為暫時性死區(qū)有時候,會不經(jīng)間遇到比較隱蔽的死區(qū),不太容易被發(fā)現(xiàn)。不允許重復(fù)聲明不允許在相同的作用域內(nèi)聲明同一個變量。一旦聲明,其值就不能再改變。 let && const let let聲明的變量只在其所在的代碼塊內(nèi)有效。 { let a = 10; } console.log(a) //R...
let和const let和const兩者并不存在變量提升 這里要說明的是變量一定要在聲明后使用,否則報錯。 vara=[]; for(vari=0;i<10;i++){ a[i]=function(){ console.log(i); }; } a[6]();//10 變量i是var聲明的,我們要知道這里在全局范圍內(nèi)都有效。我們要知道在每一次循環(huán)中,新的...
摘要:所以,最終極的辦法是一層一層凍結(jié)所有對象。塊級作用域使呈現(xiàn)出塊級作用域的特征。聲明的變量僅存在于當(dāng)前塊級作用域中。在中,嚴(yán)格模式下等價于使用聲明,非嚴(yán)格下等價于使用。在中使用聲明的變量,為了保持程序的嚴(yán)謹(jǐn)性,不允許被訪問。 let和const都是聲明變量的新方式。 一般的,由于這兩種聲明方式的特性,現(xiàn)在項目組的開發(fā)規(guī)范中都會要求:不使用var而是let或const。 Const co...
今天想為大家講講前端項目的package.json文件中相關(guān)的字段含義及使用場景。我們先避免一些配置性的錯誤,提高項目的維護性。 name 我們要知道當(dāng)項目是需要發(fā)版為npm包的,那么name字段是必須的。因為它涉及到npm包的命名。 舉個例子 筆者曾發(fā)布過開源的npm包,名字是ping-url。 對應(yīng)的源代碼package.json的定義如下: { "name&qu...
摘要:資料參考是阮一峰老師寫的教程命令新增的命令,使用來聲明變量的,就像一樣,但是聲明的變量只在所在的代碼塊中有效。凡是在聲明之前使用這個變量都會報錯塊級作用域中擁有全局作用域和函數(shù)作用域,添加了塊級作用域。 資料參考是阮一峰老師寫的ES6教程 let&const命令 let ES6新增的let命令,使用來聲明變量的,就像var一樣,但是聲明的變量只在let所在的代碼塊中有效。 e.g { ...
閱讀 3736·2023-04-25 18:41
閱讀 1169·2021-11-11 16:55
閱讀 1823·2021-09-22 15:54
閱讀 3069·2021-09-22 15:51
閱讀 3545·2019-08-30 15:55
閱讀 1937·2019-08-30 14:19
閱讀 1277·2019-08-29 10:57
閱讀 1699·2019-08-29 10:56