-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
es6必会之let && const #108
Comments
let
let x = 1;
if(x === 1) {
let x = 2;
console.log(x); // 2
}
console.log(x); // 1
block,statement和expression的区别是什么?这对于作用域的判别很有用。
很多东西自以为会了,然而实际上可能只是走马观花,所以回过头来将基础捡起来,可能比学习高阶技术更重要。 block
var x = 1;
let y = 1;
if (true) {
var x = 2;
let y = 2;
}
console.log(x);
console.log(y); 这里的block指的是 {
StatementList
} 比起block statement syntax,更重要的是Block Scoping Rules: var x = 1;
{
var x = 2;
}
console.log(x); // 2 2.let && const rule: let x = 1;
{
x = 2;
}
console.log(x); // 1
由于block scoping的存在,不会throw出 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 被 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);
Control flow包括 什么是Expressions?
Primary expressions
上面的例子中,this指代input这个DOM对象,它由于具有属性value,因此可以调用validate函数,并且每次输入值发生变化时都触发onChange回调。
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 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,{}。在 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 在 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?。
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;
let不会立即为变量赋undefined初值是好是坏呢?当然是好事!这样将变量的管理更加精细,避免引用重名变量覆盖后出现bug还发现不了的情况。 还有两个temporal dead zone的情况: function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError:Cannot access 'foo' before initialization
}
}
test(); (foo + 55)中的foo在let foo初始化(正在赋值)的过程中访问,也就是在temporal dead zone期间进行访问,因此会抛出引用异常 function go(n) {
console.log(n);
for (let n of n.a) { // ReferenceError,Cannot access 'n' before initialization
console.log(n);
}
}
go({a: [1, 2, 3]}); n.a中的n在let n初始化(正在赋值)的过程中访问,也就是在temporal dead zone期间进行访问,因此会抛出引用异常 |
const其实在let模块已经写了很多关于const的内容,所以在这里就写一些const特有的特性。
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真的很严格!
2018年12月18日01:45更新 es6的const和java的final之间的对比关于 2020年6月4日更新 在定义前引用的话,let和const会报什么错?foo();
let foo = () =>{console.log('foo')};
// const foo = () =>{console.log('foo')}; let: |
for循环中let/var与setTimeout的羁绊!无异步参与时,var,let效果相同for (var i = 0; i<9 ;i++){
console.log(i); // 0, 1, 2, 3, 4, 5, 6, 7, 8
} for (let i = 0; i<9 ;i++){
console.log(i); // 0, 1, 2, 3, 4, 5, 6, 7, 8
} 有异步参与时,var异常,let创建块作用域for (var i = 0; i<9 ;i++){
setTimeout(()=>{
console.log(i);
})
} 输出了9次9。
for (let i = 0; i<9 ;i++){
setTimeout(()=>{
console.log(i);
})
} 依次从0到8输出。 用I don't know js中的一段话来讲就是:>let为每次迭代声明一次变量,后续迭代的初始值是上一次迭代的结束值。 Array.prototype.forEach与setTimeout['foo','bar','baz'].forEach((_, i)=>{
setTimeout(()=>{console.log(i)})
}) 依次输出0,1,2。 所以使用Array.prototype.forEach时根本无需担心setTimeout的问题,没有let/var与setTimeout的困扰,在实际开发中是比较常见的场景。 注意:
关键词:
|
一直以来都有用let和const,看似深入学习过,但其实没有真正完全理解,在模棱两可的用,之前在城西的一次面试就被问的哑口无言,所以我将通过mdn的资料针对性地学习let 和 const,并且会写一些易懂的例子,也会涉及到一些规范里的内容。
The text was updated successfully, but these errors were encountered: