node测试es6代码
{
"name": "class1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "babel app.js --out-file bundle.js",
"script-name": "babel-node app.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.7.0"
}
}
let
函数作用域,全局作用域。
[[scope]]
变量提升造成变量污染,解决这个问题,es5通过立即执行函数,es6通过let就行了
kiss原则
kiss原则 keep it simple ,stupid 保持简单和傻瓜式的,设计越简单越好,任何没有必要的复杂都要避免
不光编程用到kiss原则,生活中也会用到,在很多地方都能用到。
块级作用域
let块级作用域,举例:一个{}就是一个块,{}之间就是块级作用域
没有let时,只有全局作用域,函数作用域这两种,与GO、AO有关。let增加了新的作用域:块级作用域。
如果在全局中,全局也是一个块,类似于{全局},全局也被{}包裹一样。像一个一个嵌套的盒子,盒子套着盒子,盒子里面还有其它东西,在外面的盒子访问不到在里面盒子装的东西,里面盒子可以访问到外面盒子装的东西
块级作用域从形式上像{},像增加了一个{}
如果let声明在哪个{}中,相当于给这个括号中增加了块级作用域,如果{}之中没有let声明,那么这个{}中没有块级作用域。
如果方法function中没有let声明,那么function中没有块级作用域,但有函数作用域。虽然函数也是function a{},{}包裹起来的,但没有let声明就没有块级作用域。
总结:let所在的地方增加了以前没有的块级作用域。let所在的地方,才有块级作用域,块级作用域的范围由什么划分?由全局、{}、()来划分,let声明在它们中,它们中就有了块级作用域
let在全局,全局就是一个块
let在函数,在函数的那个{}下面,哪个{}下就有块级作用域
let在表达式(){}中,(){}中有块级作用域
if (1) {
// 块级作用域
}
for (var i = 0; i < 10; i++) {
// 块级作用域
}
for (; 1;) {
// 块级作用域
break;
}
{
// 块级作用域
}
let不能重复声明
let同一作用域下不能重复声明,var可以
同一块级作用域下,let声明的变量,不允许重复声明
同一块级作用域下,let不允许声明已有变量(计算AO中的变量)
var GO、AO中提取,let不提取,其实是在let之前的,即使var声明代码写在let后面,但它预编译时被提取,其实是在let前面的,已有变量名,let不能重复定义
let在GO、AO中不会被提升,不会被提前提取,直接进入代码运行那个步骤,随着代码运行存入AO、GO中
var在全局中,可以重复声明
示例解析:在全局作用域下,var可以重复声明不报错,后面覆盖前面,var声明的变量可以重复定义,并在预编译时被提取
var a = 1;
var a = 2;
let全局中,不能重复声明
示例解析:
在全局作用域下,let不可以重复声明,会报错,
let a=1;
let a=2;
node运行
let函数中,不能重复声明
示例解析:
函数内部let定义重复变量,出错
function test(){
let a=1;
let a=2;
}
let与var重复声明,冲突
把一个let改成var依然报错
let声明,{}中有了块级作用域,块级作用域特性,let声明的变量不允许被重复声明
function test(){
let a=1;
var a=2;//Identifier 'a' has already been declared
}
test()
运行结果 node app.js运行
ctrl+shift+f10运行,与node运行结果一样
解析
function test(){
/*AO={a:undefined->2}
* AO先提取var a,不提取let a,变成var a=undefined与a=2*/
let a=1;
var a=2;//Identifier 'a' has already been declared
}
test()
同如下代码
function test(){
/*AO={a:undefined->2}
* AO先提取var a,不提取let a,变成var a=undefined与a=2,
* a已经被定义了,let不能定义已有变量,var会被声明提升,不要只是看代码写的位置关系,代码前后
* 要走预编译,AO,GO之后,再看代码关系,代码前后,写在后面的代码不一定运行在后面*/
var a=undefined;
let a=1;//Identifier 'a' has already been declared
a=2;
}
test()
let与函数中形参重名,冲突
function test(a){
let a=10;
console.log(10);
}
test()//Uncaught SyntaxError: Identifier 'a' has already been declared
一样会出错
a在预编译时已经被定义了,let又重复定义a,也会报错
function test(a){
{
let a=10;
console.log(a);//10
}
console.log(a);//undefined
}
test();
解析
function test(a){
{
let a=10;
console.log(a);//10
/*let声明,有了块级作用域1,这两个作用域声明的变量不互相影响,可以重复声明
这不是同一个·作用域,可以重复声明变量a,*/
}
console.log(a);//undefined
/*没有let声明,没有块级作用域,里面的a是形参,不受块级作用域1影响,因为在内部定义的变量,不能被外部的使用
* 所以打印形参,为undefined*/
}
test();
function test(){
let a=10;
{
console.log(a);//10
}
console.log(a);//10
}
test();
不能声明提升
let不会提升,会产生一个暂时性死区
var会声明提升
声明提升
var会在预编译时被提前提取,提取但不赋值,之后再赋值
console.log(a);//undefined
var a=10;
/*
Go={
a:undefined->函数运行console->10
}
在预编译中,先提取函数声明,再运行代码
*/
let不能声明提升,会产生暂时性死区
在这个例子中let之前会产生一个死区,let a之前都不能使用a,使用就会报错。写代码时,必须先定义,再使用。
let不存在函数提升,说明它不会在预编译的时候被提取,参与预编译,但不会被提取
console.log(a);// Cannot access 'a' before initialization
let a=10;
上面代码不等同于下面代码
let a;
// let a=undefined;
console.log(a);//undefined
a=10;
let在函数中也不能声明提升
function test() {
console.log(a);
let a = 10;
}
test();/*报错,a未定义,再函数中,与在全局中一样*/
let b=b的情况,会报错
a被AO提升了,b不被提升,定义赋值为b时,b还是未定义状态
var a=a;
console.log(a);//undefined
let b=b;
console.log(b)//Cannot access 'b' before initialization
不等价于以下代码
let b;
b=b;
console.log(b)//undefined
let c;
console.log(c);//undefined
let与函数参数默认值的例子
function test(x=y,y=2){
console.log(x,y);//ReferenceError: Cannot access 'y' before initialization
}
test();
这些例子,都要练习,这都是非常经典的例子。
这个依然是个死区,因为x赋值,赋值为y,这个时候y并没有在之前被定义,之后才定义y=2。x=y,x调用y在y=2之前,x调用y是在死区调用的。,所以报错。
可以先不看x=y,先看y=2,有被定义,把y被定义前划分为死区,把死区圈起来,圈出来,看看x=y是不是在死区中调用。或者把不是死区的圈出来,看看x=y是否在不是死区中调用,是否在活区调用。
暂时性死区
function test(x=2,y=x){
console.log(x,y);//2 2
}
test();
这样改就对了
let会导致typeod可能报错
这里a其实是在全局中声明的,被AO提前提取
console.log(typeof a);//undefined
console.log(typeof a);// Cannot access 'a' before initialization
let a;
let导致暂时性死区,导致typeof a报错,不像之前typeof是安全的,不会报错
这是个坑
如果a是用let声明的,并且在typeof使用前声明的,所以会报错
let只能在当前作用域下生效
外部得不到块级作用域内部的内容
{
let a=2;
}
console.log(a);// a is not defined
块级作用域定义的a,全局不能访问,不在同一个作用域下
if(1){
let a=2;
}
console.log(a);// a is not defined
死循环
for (;1;){
let a=1;
}
console.log(a);
死循环:不会报错,因为for代码一直执行,走不到下面的代码,因为nodejs是单核所以只会占用一个cpu,让它一直在那转,电脑也不会卡。如果在浏览器中一直运行,浏览器有可能就崩溃了。
注意循环到底有没有结束,什么时候结束
for (;1;){
let a=1;
break;
}
console.log(a);//a is not defined
for表达式中的let变量,全局外部访问不到
for (let i = 0; i < 10; i++) {
}
console.log(i);// i is not defined
for数组赋值(综合题目)
for var数组赋值
time48.25
var arr=[];
for (var i = 0; i < 10; i++) {
arr[i]=function (){
console.log(i)
}
// arr[i]();//0-9
}
for (var i = 0; i < 10; i++) {
arr[i]();//0-9
}
知识都学过,怎么样用就有讲究了
time50.00
for let var小题目
for(let i=0;i<10;i++){
i='a';
console.log(i);//a
}
for(var i=0;i<10;i++){
var i='a';
console.log(i);//a
}
多加 var i=’a’;没有影响,因为 var i=’a’;,var i被提升了到了全局
for(let i=0;i<10;i++){
i='a';
console.log(i);//a
}
貌似在同一块级作用域下,其实不在
for(let i=0;i<10;i++){
var i='a';
/*
Identifier 'i' has already been declared
这是i=a语句报的错,不是console打印的
*/
console.log(i);
}
for(let i=0;i<10;i++){
/*let声明,产生块级作用域1*/
{
/*没有let声明,var声明,没有产生新的块级作用域,还是块级作用域1
* 在块级作用域1里面,重复定义了i*/
var i='a';// Identifier 'i' has already been declared
console.log(i);
}
}
因为i已经被定义
for(let i=0;i<10;i++){
let i='a';
console.log(i);//10个a
}
相当于新建了一个块级作用域,把之前{}内的内容包了起来,一切包了起来
for(let i=0;i<10;i++){
{
let i='a';
console.log(i);// 10个a
}
}
而不是这样
for(let i=0;i<10;i++){
{
let i='a';
}
console.log(i);//0-9
}
综合起来,如下代码
for (let i = 0; i < 10; i++) {
/*
块级作用域1,let声明产生块级作用域
*/
{
let i = 'a';
/*let声明,产生块级作用域2,i的两次定义不是在同一个块级作用域定义的*/
console.log(i);// 10个a
}
console.log(i);//0-9
}
相当于如下代码
if (1) {
let a=1;
{
let a=10;
/*这两个a,在内存里是不同的地方存储的,是不一样的值
* 相当于新定义了一个a*/
console.log(a);//10
}
}
babel转义成es5代码,如下
if (1) {
var a = 1;
{
var _a = 10;
/*又重新声明了一个_a,与a不一样*/
console.log(_a); //10
}
}
let受到var声明提升的过程的影响
if (1) {
let a=1;
{
var a=10;//Identifier 'a' has already been declared
console.log(a);
}
}
这样写为什么报错?
var a=undefined;
if (1) {
let a=1;
{
a=10;
console.log(a);//10
}
}
因为var参与预编译,被提升了,提升的过程中影响了上面的let a,提升到全局,也可以提升到块级作用域的内部,经过下面的变形
if (1) {
var a = undefined;//SyntaxError: Identifier 'a' has already been declared
let a = 1;
{
a = 10;
console.log(a);//10
}
}
f (1) {
let a=1;
{
/*没有let相当于更改了以前变量,更改之前变量,变成10*/
a=10;
console.log(a);//10
}
console.log(a);//10
}
if (1) {
let a = 1;
/*let声明,产生块级作用域1*/
{
/*块级作用域2,console调用a,从最近的块级作用域取*/
let a = 10;
console.log(a);//10
}
console.log(a);//1
}
var a=undefined;
if (1) {
let a=1;
{
a=10;
var b=11;
{
let b=1;
{
var b=2;
}
}
console.log(a);//10
}
console.log(a);//1
}
只有里面的var b影响了外面的let b,里面的let b不影响外面的var b
总结
1.let本质上就是为了js增加了一个块级作用域;let所在的地方就是块级作用域所在的地方
if (1) {
let a = 1;
(function () {
a = 10;
console.log(a);//10
})();
console.log(a);//10
}
if (1) {
let a = 1;
(function () {
let a = 10;
console.log(a);//10
})();
console.log(a);//1
}
块级作用域很像立即执行函数,但本身并不一样
函数声明:建议用函数表达式的方式
函数只能在最外层(全局),或者函数作用域中声明,这样是合法的
function test(){
function test1(){
}
}
es5中不合法,在块级作用域中声明function是不合法的
es6合法了
{
function test(){
}
}
es6合法
if (1) {
function test() {
}
}
try {
function test() {
}
} catch (e) {
function test1() {
}
}
不推荐,这不是友好的方式
因为虽然es5做出了兼容,但兼容性不好,不友好对于es5
建议这样写,建议用函数表达式的方式
try {
var test1 = function () {
}
} catch (e) {
var test1 = function () {
}
}
总结
不建议在块级作用域当中,用函数声明的方式来声明函数,而用函数表达式的方式
块级作用域没有返回值
if (1) {
return a;
}
//错误 Unexpected token 'for'
var a=for(;1;){
return
}
{
return
}
没有参数让其接收
草案让其有返回值,但没有被采用
do{
return
}
块级作用域等于函数的立即调用?
错误,立即执行函数有返回值,块级作用域没有,块级作用域是作用域,另一个是函数执行,本质是两个完全不同的东西,虽然效果一样,部分效果一样。
虽然可以用函数立即执行模拟块级作用域,但它们确实不一样。