es6必会之let && const
栏目: JavaScript · 发布时间: 5年前
内容简介:一直以来都有用let和const,看似深入学习过,但其实没有真正完全理解,在模棱两可的用,之前在城西的一次面试就被问的哑口无言,所以我将通过mdn的资料针对性地学习let 和const,并且会写一些易懂的例子,也会涉及到一些规范里的内容。
关键词:
let
, const
, blocking scope
, temporal dead zone
, redeclaration
, reassignment
, immutable
, initializer
一直以来都有用let和const,看似深入学习过,但其实没有真正完全理解,在模棱两可的用,之前在城西的一次面试就被问的哑口无言,所以我将通过mdn的资料针对性地学习let 和const,并且会写一些易懂的例子,也会涉及到一些规范里的内容。
- block,statement和expression的区别是什么?
- 为什么选择了'let'作为block-scoped variable declaration?
- let和const不会像var一样绑定值到global 对象!
- let和const不能像var一样同一个scope下声明多次!
- let和const不会像var一样变量声明提升!
let
let
声明了一个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的区别是什么?
这对于作用域的判别很有用。
- block curly bracket,circle bracket等等。
- statement js由各种语句组成,Control Flow,Function,Iterations等等。
- expression 副作用,无副作用;可执行和关键词。
很多东西自以为会了,然而实际上可能只是走马观花,所以回过头来将基础捡起来,可能比学习高阶技术更重要。
block
- group zero or more statements
- block 由 curly bracket包裹(弯曲的支架比花括号更形象)
- blcok statement 在其他语言里通常叫做computed statement,之所以叫复合语句,因为JavaScript会一句一句执行,在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: 一种是全局定义,一种是函数局部定义。局部函数变量的访问会从作用域链由下往上找。但是这种老式的变量命名符不提倡再使用。
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. 复制代码
在{ }中应该用var / let /const声明x,而不是让其变成全局变量导致与外部的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 } 复制代码
更准确一些的说法是,block statement阻止function declaration 被 hoisted(变量提升)
到作用域的顶部。这种定义方式与函数表达式类似。 函数表达式的变量提升规则是下面这样:
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块作用域规则同上:
foo('before'); // TypeError: foo is not a function { function foo(location) { console.log('foo is called' + location); } } foo('after'); // foo is called after 复制代码
与函数表达式不会提升到var foo = function(){}一样;{}内部定义的function,不会提升到{}之前。而这正是function的blocking statement rule。
statement
什么是empty statement?
var array = [1, 2, 3]; for (i=0; i<array.length; array[i++] = 0) /* empty statement */; console.log(array); 复制代码
- Javascript 应用就是由符合语法的多个statement组成的。
- 一条statement可能跨越多行。
- 多条statement也可能在一行中出现,每一句由一个分号标记。
- 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;某些情况下执行并且解析为值,例如3 + 4。
- 还有2种分类,一类是执行后为number,string,boolean型的;一类是关键词类型,这种类型又分为Primary expression和Left-hand-side expressions。
Primary expressions Basic keywords and general expressions in JavaScript,例如this,grouping operator.
- this 当前对象的引用,2种调用对象方法的方式
this['propertyName']
,this.propertyName
。
function validate(obj, lowval, hival) { if ((obj.value < lowval) || (obj.value > hival)) console.log('Invalid Value!'); } <p>Enter a number between 18 and 99:</p> <input type="text" name="age" size=3 onChange="validate(this, 18, 99);"> 复制代码
上面的例子中,this指代input这个DOM对象,它由于具有属性value,因此可以调用validate函数,并且每次输入值发生变化时都触发onChange回调。
- 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 // 9 复制代码
Left-hand-side expressions 左值是赋值的目标,例如 new
, super
, Spread operator
。 new 创建一个用户自定义object类型
var objectName = new objectType([param1, param2, ..., paramN]); 复制代码
super 调用当前object的父object上的函数,在class中常用。
super([arguments]); // 调用parent constructor super.functionOnParent([arguments]); // 调用parent上的方法 复制代码
Spread operator 允许表达式被展开,可以是函数参数处展开,也可以是数组迭代处展开。 数组某处插入数组元素。
var parts = ['shoulders', 'knees']; var lyrics = ['head', ...parts, 'and', 'toes']; 复制代码
一个完整数组作为参数传入函数
function f(x,y,z){} var args = [0,1,2]; f(...args); 复制代码
通过对block,statement,expression的回顾。我们发现,其实块作用域不仅仅是curly bracket,{}。在 for(){}
, for(key in object)
, for(item of array)
等等的 ()
内,其实也属于块作用域,不仅仅是if else的{},for{}中的{}才算是块作用域,let都有效。
let a = 1; for(let a = 2; a<3; a++){ console.log(a); }; console.log(a); // 2 1 复制代码
let 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系列的循环控制语句中使用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 4 复制代码
let item = 4; for(item of [1,2,3]){ console.log(item); } console.log(item); // 1 2 3 3 复制代码
使用let以后,井水不犯河水,不用担心改写全局中的同名变量,但是一定要明确,let不仅仅作用于{},()也同样作用。
为什么选择了'let'作为block-scoped variable declaration?
可以看这个stack overflow上的question: Why was the name 'let' chosen for block-scoped variable declarations in JavaScript? 。 有两点比较重要:
- 参考了scala,F#等语言里比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 undefined 复制代码
let和const不能像var一样同一个scope下声明多次!
let foo = 1; let foo = 2; // Uncaught SyntaxError: Identifier 'foo' has already been declared 复制代码
const foo = 1; const foo = 2; // Uncaught SyntaxError: Identifier 'foo' has already been declared 复制代码
var foo = 1; var foo = 2; // everything is ok 复制代码
let和const不会像var一样变量声明提升!
原因是:const,let存在temporal dead zone!
因此不能let ,const赋值前使用变量。
在说变量提升之前,先了解一个概念,Temporal Dead Zone,指的是从block创建到初始化完成之间的时间。用var不会存在Temporal Dead Zone,因为用var声明的变量,初始值立即默认赋予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的结果为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初值是好是坏呢?当然是好事!这样将变量的管理更加精细,避免引用重名变量覆盖后出现bug还发现不了的情况。
还有两个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模块已经写了很多关于const的内容,所以在这里就写一些const特有的特性。
- const也是block-scoped的,和用let定义的变量类似。
- 不可以修改变量值,也就是不可以reassignment,并不是immutable
- 不可以重新定义
- const foo = [value],value可以是function,而let也可以!
- 必须为const赋一个初值且存在temporal dead zone,比let更加严格!
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 declared 复制代码
let定义的变量赋值function会有什么错误提示呢?
let foo = function(){ console.log('foo'); } foo();// foo 复制代码
不会报错,但是因为let可以reassignment,所以不如const更加安全,因为一般来说,我们创建一个函数以后,不太会再去覆盖这个函数。
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 复制代码
假设修改了原型链上的属性会怎样?
const foo = 'foo'; foo.length = 5; // return 5 console.log(foo.length); // 3 复制代码
我们可以看出,const还是很包容的,即使你试图修改原型链上的属性,也不会报错,他只是一笑而过,并且这种修改不会生效。
const真的很严格!
类型 | 是否必须赋值 | 是否存在temporal dead zone | 是否支持redeclaration | 是否支持reassignment |
---|---|---|---|---|
var | 否 | 否 | 是 | 是 |
let | 否 | 是 | 否 | 是 |
const | 是 | 是 | 否 | 否 |
以上所述就是小编给大家介绍的《es6必会之let && const》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python Data Structures and Algorithms
Benjamin Baka / Packt Publishing / 2017-5-30 / USD 44.99
Key Features A step by step guide, which will provide you with a thorough discussion on the analysis and design of fundamental Python data structures.Get a better understanding of advanced Python c......一起来看看 《Python Data Structures and Algorithms》 这本书的介绍吧!