摘要:副作用,无副作用可执行和关键词。和不能像一样同一个下声明多次和不会像一样变量声明提升原因是,存在因此不能,赋值前使用变量。
关键词:
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一样变量声明提升!
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的区别是什么?这对于作用域的判别很有用。
block curly bracket,circle bracket等等。
statement js由各种语句组成,Control Flow,Function,Iterations等等。
expression 副作用,无副作用;可执行和关键词。
很多东西自以为会了,然而实际上可能只是走马观花,所以回过头来将基础捡起来,可能比学习高阶技术更重要。
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。
var array = [1, 2, 3]; for (i=0; iJavascript 应用就是由符合语法的多个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!"); }Enter a number between 18 and 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 // 9Left-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 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系列的循环控制语句中使用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以后,井水不犯河水,不用担心改写全局中的同名变量,但是一定要明确,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 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创建到初始化完成之间的时间。用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 declaredlet定义的变量赋值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 是 是 否 否 2018年12月18日01:45更新
es6的const和java的final之间的对比关于const不支持reassignment这一点,java中的关键字final与之对应,表示这个常量只能赋值一次。
但是java多了一个类常量的概念,所谓类常量,指的其实就是一个常量在类的多个方法中有调用, 也就是static final foo = "foo";这样的形式。
在js中就没有这种讲究了,const就是代表常量,我才不管你是不是类的还是实例的。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/98166.html
摘要:声明的变量只在其所在的代码块内有效。只要在声明之前使用这些变量,就会报错。在语法上称为暂时性死区有时候,会不经间遇到比较隐蔽的死区,不太容易被发现。不允许重复声明不允许在相同的作用域内声明同一个变量。一旦声明,其值就不能再改变。 let && const let let声明的变量只在其所在的代码块内有效。 { 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声明的,我们要知道这里在全局范围内都有效。我们要知道在每一次循环中,新的...
摘要:所以,最终极的办法是一层一层冻结所有对象。块级作用域使呈现出块级作用域的特征。声明的变量仅存在于当前块级作用域中。在中,严格模式下等价于使用声明,非严格下等价于使用。在中使用声明的变量,为了保持程序的严谨性,不允许被访问。 let和const都是声明变量的新方式。 一般的,由于这两种声明方式的特性,现在项目组的开发规范中都会要求:不使用var而是let或const。 Const co...
今天想为大家讲讲前端项目的package.json文件中相关的字段含义及使用场景。我们先避免一些配置性的错误,提高项目的维护性。 name 我们要知道当项目是需要发版为npm包的,那么name字段是必须的。因为它涉及到npm包的命名。 举个例子 笔者曾发布过开源的npm包,名字是ping-url。 对应的源代码package.json的定义如下: { "name&qu...
摘要:资料参考是阮一峰老师写的教程命令新增的命令,使用来声明变量的,就像一样,但是声明的变量只在所在的代码块中有效。凡是在声明之前使用这个变量都会报错块级作用域中拥有全局作用域和函数作用域,添加了块级作用域。 资料参考是阮一峰老师写的ES6教程 let&const命令 let ES6新增的let命令,使用来声明变量的,就像var一样,但是声明的变量只在let所在的代码块中有效。 e.g { ...
阅读 3714·2023-04-25 18:41
阅读 1149·2021-11-11 16:55
阅读 1772·2021-09-22 15:54
阅读 3023·2021-09-22 15:51
阅读 3520·2019-08-30 15:55
阅读 1910·2019-08-30 14:19
阅读 1256·2019-08-29 10:57
阅读 1680·2019-08-29 10:56