ES6新的声明方式

作用域

很多编程语言就是在变量中存储值,并且能读取和修改此值。
变量该存储在哪,又给如何读取?所以程序制定了一些规则:作用域。
作用域:是可访问变量的集合。
常见的作用域主要分为几个类型:全局作用域(global/window)、函数作用域(function)、块状作用域({})、动态作用域(this)

全局作用域

变量在函数或者代码块 {} 外定义,即为全局作用域。不过,在函数或者代码块 {} 内未定义的变量也是拥有全局作用域的(不推荐)

var name= "es"
// 此处可调用 name变量
function myFunction() {
    // 函数内可调用 name变量
}

name拥有作用域,可以在任意地方被读取获取修改
注意
没有使用 var 关键字,该变量依然为全局变量,因为它将作为 global 或者 window 的属性存在。

function myFunction() {
  name= "es"
}

函数作用域

在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!

function bar() {
    var testValue = 'inner'
}
console.log(testValue) // 报错:ReferenceError: testValue is not defined

如果想读取函数内的变量,必须借助 return 或者闭包
闭包,外层作用域想访问函数内部的作用域(变量)。

return 方式外层作用域访问函数内部的作用域

function bar(value) {
    var testValue = 'inner'

    return testValue + value
}
console.log(bar('fun')) // "innerfun"

闭包方式外层作用域访问函数内部的作用域

function bar(value) {
    var testValue = 'inner'
    var rusult = testValue + value
    function innser() {
        return rusult
    }
    return innser()
}
console.log(bar('fun')) // "innerfun"

return 是函数对外交流的出口,而 return 可以返回的是函数,函数内部的子函数是可以获取函数作用域内的变量的。

问题:如果 inner 函数再嵌套函数呢?
如果 inner 函数再嵌套函数呢?这就涉及到作用域链

2021-02-23_094154.jpg

上图是作用域链,和原型链相似,任何一个作用域链都是一个堆栈,首先先把全局作用域压入栈底,再按照函数的嵌套关系一次压入堆栈。在执行的时候就按照这个作用域链寻找变量。
堆栈:堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
原型链图

2021-02-05_144550.jpg

块状作用域
于什么是块,只要认识 {} 就可以了

if (true) {
    let a = 1
    console.log(a)
}

if 后 {} 就是“块”,这个里面的变量就是拥有这个块状作用域,按照规则, {} 之外是无法访问这个变量的。

动态作用域

window.a = 3
function test() {
    console.log(this.a)
}
test.bind({
    a: 2
})() // 2
test() // 3

test.bind已经把作用域的范围进行了修改指向了 { a: 2 },而 this 指向的是当前作用域对象。

问题
作用域是在代码编写的时候就已经决定了呢,还是在代码执行的过程中才决定的?

var name= " es"
// 此处可调用 name变量
function myFunc() {
    // 函数内可调用 name变量
}

name就是全局作用域,函数内部的用 var 定义的变量就是函数作用域。这个也就是专业术语:词法作用域
变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。
相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域。

新的声明方式

let变量

1. let 声明的全局变量不是全局对象window的属性
let不能通过 window. 变量名 的方式访问这些变量,var 声明的全局变量是 window 的属性,是可以通过 window. 变量名 的方式访问的

var a = 5
console.log(window.a) // 5
let a = 5
console.log(window.a) // undefined

2. 用let定义变量不允许重复声明
使用 var 可以重复定义,使用 let 却不可以

var a = 5
var a = 6
console.log(a) // 5
let a = 5
let a = 6
// VM131:1 Uncaught SyntaxError: Identifier 'a' has already been declared
//   at <anonymous>:1:1

3. let声明的变量不存在变量提升
a 的调用在声明之前,所以它的值是 undefined

function foo() {
    console.log(a)
    var a = 5
}

foo() //undefined
// var 会导致变量提升,上述代码和下面的代码等同
function foo() {
    var a
    console.log(a)
    a = 5
}

foo() //undefined

对于 let 而言,变量的调用是不能先于声明的

function foo() {
    console.log(a)
    let a = 5
}

foo()
// Uncaught ReferenceError: Cannot access 'a' before initialization

4. let声明的变量具有暂时性死区
只要块级作用域内存在 let 命令,它所声明的变量就绑定在了这个区域,不再受外部的影响。
在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

var a = 5
if (true) {
    a = 6
    let a
}

4. let 声明的变量拥有块级作用域

{
    let a = 5
}
console.log(a) // undefined

a 变量是在代码块 {} 中使用 let 定义的,它的作用域是这个代码块内部,外部无法访问

for 循环

for (var i = 0 i < 3 i++) {
    console.log('循环内:' + i) // 0、1、2
}
console.log('循环外:' + i) // 3

改成let声明

for (let i = 0 i < 3 i++) {
    console.log('循环内:' + i) // 0、1、2
}
console.log('循环外:' + i) // ReferenceError: i is not defined

加上setTimeout
https://www.cnblogs.com/planetwithpig/p/12016231.html
babel 网址:https://www.babeljs.cn/repl

 function time(){
           for(var i=0;i<5;i++){
                   setTimeout(function(){
                       console.log('setTimeout的i',i) //5个5
                },0)
          }
    }
  time();

1、setTimeOut() 是一个异步函数,JS遇到异步函数的时候,会把异步函数插入到队列中等待。也就是所谓的插队。
2、 流程:for(i=0) ——> for(i=1) ——> for(i=2) for(i=3) ——> for(i=4) ——> for(i=5)ps:(这段循环都内完成了)——>console.log(5) ——> console.log(5) ——> console.log(5) ——> console.log(5) ——> console.log(5) ——> 执行完成
3、 for循环完成之后,i经过5次循环,开始执行setTimeout方法的时候i的值以及变成了5,因为setTimeout有5次方法调用,所以输出5个5

希望的值是0、1、2,.....也就是每次保存住循环时候 i 的值
方案1:闭包
思路:因为setTimeOut()是异步执行,所以让它立即执行就可以了

      for(var i=0;i<5;i++){
                (function(i){
                    setTimeout(function(){
                       console.log('使用闭包的i',i)   //0,1,2,3,4
                    })
                })(i)
            }

闭包检测到setTimeOut时不再放到队列中进行等待,而是立即运行setTimeOut()
执行流程如下:
for(i=0) ——> console.log(0) ——> for(i=1) ——> console.log(1) ——> for(i=2) ——> console.log(2) ——> for(i=3) ——> console.log(3) ——> for(i=4) ——> console.log(4) ——> for(i=5) ——> 执行结束
内部函数调用外部函数的变量,以确保外部函数的变量不会被释放,内部函数的值引用着外部函数的值

方案2:使用let

   for (let i = 0; i < 5; i++) { 
                setTimeout(function() { 
                    console.log(i); //0,1,2,3,4
                }, 1000 * i); 
           } 

let的作用域是块作用域,所以每次JS检测到setTimeOut,把setTimeOut放到队列的同时,let定义的i的值也会跟随setTimeOut进去队列。所以每次循环后队列里的setTimeOut里的i的值是不一样的
var定义的i是无法进入setTimeOut的。i只能在运行到setTimeOut时才会向外层环境申请i的值,而这个时候i的值已经变成5了。

同步和异步


2021-02-22_164036.jpg

异步执行原理
https://www.cnblogs.com/lvzl/p/14242510.html

const { log } = console;
log(1); // 首先呢,JS代码是从上至下逐行执行,到这里先打印 1
setTimeout(() => { // 到了这里,遇到了异步任务,把异步操作加到异步队列中,然后接着往下执行JS代码
  log(2);
});
new Promise((resolve, reject) => { 
  log(3); // 执行到这里,这里的代码也是同步的,因此打印 3
  resolve(); // resolve 执行以后会进入.then, .then里面也是异步执行, 因此加入异步队列,整个的JS代码第一次就执行完了
}).then(() => {
  log(4);
});
// 现在异步队列中有两个任务, setTimeout,Promise.then. JS在执行下一个宏任务之前会保证微任务队列为空,因此会先打印 4, 再打印 3
// 微任务: Promise.then, process.nextTick(node)
// 宏任务: 整体的JS代码, setTimeout, setInterval
// 正确答案: 1324
2089555-20210106174701043-1416802229.jpg

Const

不能被改变的叫做常量
const 除了具有 let 的块级作用域和不会变量提升外,还有就是它定义的是常量,在用 const 定义变量后,我们就不能修改它了,对变量的修改会抛出异常

const PI = 3.1415
console.log(PI)
PI = 5
console.log(PI)
// Uncaught TypeError: Assignment to constant variable.

这个代码块中因为对 PI 尝试修改,导致浏览器报错,这就说明 const 定义的变量是不能被修改的,它是只读的。

重点

const obj = {
    name: 'xiaozhang',
    age: 34
}
obj.name= 'xiaowu'
console.log(obj)
// {name: "xiaowu", age: 34"}

const 定义的 obj 竟然被改变了...
这个时候就要去了解js的变量是如何存储的
栈: 原始数据类型(Undefined,Null,Boolean,Number、String)
堆: 引用数据类型(对象、数组、函数)
基本数据类型存储在 栈内存 中,引用数据类型存储在 堆内存 中然后在栈内存中保存 引用地址


2021-02-22_171357.jpg

const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

如何让对象或者数组这种引用数据类型也不被改变呢?

Object.freeze(obj)

Object.freeze() 只是浅层冻结,只会对最近一层的对象进行冻结,并不会对深层对象冻结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容