From 1c075a0e7b8e808c70a8aba5f8bc2a6f188578d3 Mon Sep 17 00:00:00 2001 From: qianguyihao Date: Sat, 4 Jul 2020 17:27:59 +0800 Subject: [PATCH] =?UTF-8?q?add:=20call()=E3=80=81apply()=E3=80=81bind()?= =?UTF-8?q?=E7=9A=84=E5=8C=BA=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierrc | 2 +- 04-JavaScript基础/18-函数.md | 85 +++++++ 04-JavaScript基础/21-this指向.md | 109 +++++++++ 04-JavaScript基础/21-执行期上下文.md | 15 -- 04-JavaScript基础/22-call、apply 和 bind.md | 214 ++++++++++++++++++ 04-JavaScript基础/22-this.md | 191 ---------------- 04-JavaScript基础/{24-闭包.md => 23-闭包.md} | 39 ++-- .../{23-面向对象概述.md => 24-面向对象概述.md} | 4 +- 04-JavaScript基础/25-对象的创建&构造函数.md | 6 +- .../{28-原型继承.md => 28-原型链和原型继承.md} | 0 .../{29-构造继承.md => 29-类和构造继承.md} | 0 .../04-ES6:变量let、const和块级作用域.md | 8 +- 06-JavaScript进阶/01-var、let、const的区别.md | 5 +- 13 files changed, 449 insertions(+), 229 deletions(-) create mode 100644 04-JavaScript基础/21-this指向.md delete mode 100644 04-JavaScript基础/21-执行期上下文.md create mode 100644 04-JavaScript基础/22-call、apply 和 bind.md delete mode 100644 04-JavaScript基础/22-this.md rename 04-JavaScript基础/{24-闭包.md => 23-闭包.md} (80%) rename 04-JavaScript基础/{23-面向对象概述.md => 24-面向对象概述.md} (97%) rename 04-JavaScript基础/{28-原型继承.md => 28-原型链和原型继承.md} (100%) rename 04-JavaScript基础/{29-构造继承.md => 29-类和构造继承.md} (100%) diff --git a/.prettierrc b/.prettierrc index b977e0e..b26f6a6 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,6 @@ { "printWidth": 300, - "tabWidth": 2, + "tabWidth": 4, "semi": true, "singleQuote": true, "trailingComma": "es5", diff --git a/04-JavaScript基础/18-函数.md b/04-JavaScript基础/18-函数.md index c1246d6..40cfada 100644 --- a/04-JavaScript基础/18-函数.md +++ b/04-JavaScript基础/18-函数.md @@ -468,6 +468,91 @@ function fn(){ 我们可以这样说,如果直接是`fn()`,那就说明是函数调用。如果是`XX.fn()`的这种形式,那就说明是**方法**调用。 + +## 类数组 arguments + +> 这部分,小白可能看不懂。所以,这一段,暂时可以忽略。 + +在调用函数时,浏览器每次都会传递进两个隐含的参数: + +- 1.函数的上下文对象 this + +- 2.**封装实参的对象** arguments + +例如: + +```javascript +function foo() { + console.log(arguments); + console.log(typeof arguments); +} + +foo(); +``` + +![](http://img.smyhvae.com/20180315_0903.png) + +arguments 是一个类数组对象,它可以通过索引来操作数据,也可以获取长度。 + +**arguments 代表的是实参**。在调用函数时,我们所传递的实参都会在 arguments 中保存。有个讲究的地方是:arguments**只在函数中使用**。 + +### 1、返回函数**实参**的个数:arguments.length + +arguments.length 可以用来获取**实参的长度**。 + +举例: + +```javascript +fn(2, 4); +fn(2, 4, 6); +fn(2, 4, 6, 8); + +function fn(a, b) { + console.log(arguments); + console.log(fn.length); //获取形参的个数 + console.log(arguments.length); //获取实参的个数 + + console.log('----------------'); +} +``` + +打印结果: + +![](http://img.smyhvae.com/20180125_2140.png) + +我们即使不定义形参,也可以通过 arguments 来使用实参(只不过比较麻烦):arguments[0] 表示第一个实参、arguments[1] 表示第二个实参... + +### 2、返回正在执行的函数:arguments.callee + +arguments 里边有一个属性叫做 callee,这个属性对应一个函数对象,就是当前正在指向的函数对象。 + +```javascript +function fun() { + console.log(arguments.callee == fun); //打印结果为true +} + +fun('hello'); +``` + +在使用函数**递归**调用时,推荐使用 arguments.callee 代替函数名本身。 + +### 3、arguments 可以修改元素 + +之所以说 arguments 是伪数组,是因为:**arguments 可以修改元素,但不能改变数组的长短**。举例: + +```javascript +fn(2, 4); +fn(2, 4, 6); +fn(2, 4, 6, 8); + +function fn(a, b) { + arguments[0] = 99; //将实参的第一个数改为99 + arguments.push(8); //此方法不通过,因为无法增加元素 +} +``` + + + ## arguments 的使用 当我们不确定有多少个参数传递的时候,可以用 **arguments** 来获取。在 JavaScript 中,arguments 实际上是当前函数的一个**内置对象**。所有函数都内置了一个 arguments 对象(只有函数才有 arguments 对象),arguments 对象中存储了**传递的所有实参**. diff --git a/04-JavaScript基础/21-this指向.md b/04-JavaScript基础/21-this指向.md new file mode 100644 index 0000000..1476e0c --- /dev/null +++ b/04-JavaScript基础/21-this指向.md @@ -0,0 +1,109 @@ +## 执行期上下文 + +当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。 + +每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。 + +参考链接: + +## this + +解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是 this,this 指向的是一个对象,这个对象我们称为函数执行的 上下文对象。 + +### 函数内 this 的指向【非常重要】 + +我们在《JavaScript 基础/函数.md》这篇文章讲过,函数的调用有**六种**形式。 + +根据函数的调用方式的不同,this 会指向不同的对象: + +- 1.以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。比如`fun();`相当于`window.fun();` + +- 2.以方法的形式调用时,this 指向调用方法的那个对象 + +- 3.以构造函数的形式调用时,this 指向实例对象 + +- 4.以事件绑定函数的形式调用时,this 指向**绑定事件的对象** + +- 5.使用 call 和 apply 调用时,this 指向指定的那个对象 + +**针对第 1 条的举例**: + +```javascript +function fun() { + console.log(this); + console.log(this.name); +} + +var obj1 = { + name: 'smyh', + sayName: fun, +}; + +var obj2 = { + name: 'vae', + sayName: fun, +}; + +var name = '全局的name属性'; + +//以函数形式调用,this是window +fun(); //可以理解成 window.fun() +``` + +打印结果: + +``` + Window + 全局的name属性 +``` + +上面的举例可以看出,this 指向的是 window 对象,所以 this.name 指的是全局的 name。 + +**第 2 条的举例**: + +```javascript +function fun() { + console.log(this); + console.log(this.name); +} + +var obj1 = { + name: 'smyh', + sayName: fun, +}; + +var obj2 = { + name: 'vae', + sayName: fun, +}; + +var name = '全局的name属性'; + +//以方法的形式调用,this是调用方法的对象 +obj2.sayName(); +``` + +打印结果: + +``` + Object + vae +``` + +上面的举例可以看出,this 指向的是 对象 obj2 ,所以 this.name 指的是 obj2.name。 + +### 箭头函数中 this 的指向 + +ES6 中的箭头函数并不会使用上面的准则,而是会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。 + +### 改变函数内部的 this 指向 + +JS 专门为我们提供了一些方法来改变函数内部的 this 指向。详见下一篇文章中的 call()、apply()、bind() 方法。 + +## 我的公众号 + +想学习**代码之外的技能**?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。 + +扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外: + +![](http://img.smyhvae.com/20200101.png) diff --git a/04-JavaScript基础/21-执行期上下文.md b/04-JavaScript基础/21-执行期上下文.md deleted file mode 100644 index 48b9341..0000000 --- a/04-JavaScript基础/21-执行期上下文.md +++ /dev/null @@ -1,15 +0,0 @@ - - -## 执行期上下文 - -当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。 - -每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。 - - - -## 参考链接 - -- - - diff --git a/04-JavaScript基础/22-call、apply 和 bind.md b/04-JavaScript基础/22-call、apply 和 bind.md new file mode 100644 index 0000000..e3413d2 --- /dev/null +++ b/04-JavaScript基础/22-call、apply 和 bind.md @@ -0,0 +1,214 @@ +## 前言 + +JS 专门为我们提供了一些方法来改变函数内部的 this 指向。常见的方法有 call()、apply()、bind() 方法。 + +## call() 方法 + +### call() 方法的作用 + +call() 方法的作用:可以**调用**一个函数,与此同时,它还可以改变这个函数内部的 this 指向。 + +call() 方法的另一个应用:**可以实现继承**。之所以能实现继承,其实是利用了上面的作用。 + +语法: + +```js +fn1.call(想要将this指向哪里, 函数实参1, 函数实参2); +``` + +备注:第一个参数中,如果不需要改变 this 指向,则传 null。 + +### call() 方法举例 + +**举例 1**、通过 call() 调用函数: + +```js +const obj1 = { + nickName: 'qianguyihao', + age: 28, +}; +function fn1() { + console.log(this); + console.log(this.nickName); +} +fn1.call(this); // this的指向并没有被改变,此时相当于 fn1(); +``` + +上方代码的打印结果: + +``` +window +undefined +``` + +上面的代码,跟普通的函数调用 `fn1()` 没有区别。 + +**举例 2**、通过 call() 改变 this 指向: + +```js +var obj1 = { + nickName: 'qianguyihao', + age: 28, +}; + +function fn1(a, b) { + console.log(this); + console.log(this.nickName); + console.log(a + b); +} + +fn1.call(obj1, 2, 4); // 先将 this 指向 obj1,然后执行 fn1() 函数 +``` + +上方代码的打印结果: + +``` +obj1 +qianguyihao +6 +``` + +**举例 3**、通过 call() 实现继承: + +```js +// 给 Father 增加 name 和 age 属性 +function Father(myName, myAge) { + this.name = myName; + this.age = myAge; +} + +function Son(myName, myAge) { + // 【下面这一行,重要代码】 + // 通过这一步,将 father 里面的 this 修改为 Son 里面的 this;另外,给 Son 加上相应的参数,让 Son 自动拥有 Father 里的属性。最终实现继承 + Father.call(this, myName, myAge); +} + +const son1 = new Son('千古壹号', 28); +console.log(JSON.stringify(son1)); +``` + +上方代码中,通过 call() 方法,让 Son 继承了 Father 里面的 name 和 age 属性。 + +打印结果: + +``` +{"myName":"千古壹号","myAge":28} +``` + +## apply() 方法 + +### apply() 方法的作用 + +apply() 方法的作用:可以**调用**一个函数,与此同时,它还可以改变这个函数内部的 this 指向。这一点,和 call()类似。 + +apply() 方法的应用: 由于 apply()需要传递数组,所以它有一些巧妙应用,稍后看接下来的应用举例就知道了。 + +语法: + +```js +fn1.apply(想要将this指向哪里, [函数实参1, 函数实参2]); +``` + +备注:第一个参数中,如果不需要改变 this 指向,则传 null。 + +到这里可以看出, call() 和 apply() 方法的作用是相同的。唯一的区别在于,apply() 里面传入的**实参,必须是数组(或者维数组)**。 + +### apply() 方法举例 + +**举例**、通过 apply() 改变 this 指向: + +```js +var obj1 = { + nickName: 'qianguyihao', + age: 28, +}; + +function fn1(a) { + console.log(this); + console.log(this.nickName); + console.log(a); +} + +fn1.apply(obj1, ['hello']); // 先将 this 指向 obj1,然后执行 fn1() 函数 +``` + +注意,上方代码中,call() 里面传实参时,需要以数组的形式。即便是传一个实参,也需要传数组。 + +打印结果: + +``` +obj1 +qianguyihao +hello +``` + +### apply() 方法的巧妙应用:求数组的最大值 + +我们知道,如果想要求数组中元素的最大值的时候,数组本身是没有自带方法的。那怎么办呢? + +虽然数组里没有获取最大值的方法,但是数值里面有 `Math.max(数字1,数字2,数字3)` 方法,可以获取**多个数值中的最大值**。 另外,由于 apply() 方法在传递实参时,必须要以数组的形式,所以我们可以 通过 Math.max() 和 apply() 曲线救国。 + +**举例**:求数组中多个元素的最大值: + +```js +const arr1 = [3, 7, 10, 8]; + +// 下面这一行代码的目的,无需改变 this 指向,所以:第一个参数填 null,或者填 Math,或者填 this 都可以。严格模式中,不让填null。 +const maxValue = Math.max.apply(Math, arr1); // 求数组 arr1 中元素的最大值 +console.log(maxValue); + +const minValue = Math.min.apply(Math, arr1); // 求数组 arr1 中元素的最小值 +console.log(minValue); +``` + +打印结果: + +``` +10 + +3 +``` + +## bind() 方法 + +### bind() 方法的作用 + +bind() 方法**不会调用函数**,但是可以改变函数内部的 this 指向。 + +把call()、apply()、bind()这三个方法做一下对比,你会发现:实际开发中, bind() 方法使用得最为频繁。如果有些函数,我们不需要立即调用,但是又想改变这个函数内部的this指向,此时用 bind() 是最为合适的。 + + +语法: + +```js +新函数 = fn1.bind(想要将this指向哪里, 函数实参1, 函数实参2); +``` + +参数: + +- 第一个参数:在 fn1 函数运行时,指定 fn1 函数的this 指向。如果不需要改变 this 指向,则传 null。 + +- 其他参数:fn1 函数的实参。 + +解释:它不会调用 fn1 函数,但会返回 由指定this 和指定实参的**原函数拷贝**。可以看出, bind() 方法是有返回值的。 + + + + + + + + + + + + +### call() 方法举例 + +## 我的公众号 + +想学习**代码之外的技能**?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。 + +扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外: + +![](http://img.smyhvae.com/20200101.png) diff --git a/04-JavaScript基础/22-this.md b/04-JavaScript基础/22-this.md deleted file mode 100644 index 2c77735..0000000 --- a/04-JavaScript基础/22-this.md +++ /dev/null @@ -1,191 +0,0 @@ - - -## this - -解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的 上下文对象。 - -根据函数的调用方式的不同,this会指向不同的对象:【重要】 - -- 1.以函数的形式调用时,this永远都是window。比如`fun();`相当于`window.fun();` - -- 2.以方法的形式调用时,this是调用方法的那个对象 - -- 3.以构造函数的形式调用时,this是新创建的那个对象 - -- 4.使用call和apply调用时,this是指定的那个对象 - - -**针对第1条的举例**: - -```javascript - function fun() { - console.log(this); - console.log(this.name); - } - - var obj1 = { - name: "smyh", - sayName: fun - }; - - var obj2 = { - name: "vae", - sayName: fun - }; - - var name = "全局的name属性"; - - //以函数形式调用,this是window - fun(); //可以理解成 window.fun() -``` - - -打印结果: - -``` - Window - 全局的name属性 -``` - - -上面的举例可以看出,this指向的是window对象,所以 this.name 指的是全局的name。 - - -**第2条的举例**: - -```javascript - function fun() { - console.log(this); - console.log(this.name); - } - - var obj1 = { - name: "smyh", - sayName: fun - }; - - var obj2 = { - name: "vae", - sayName: fun - }; - - var name = "全局的name属性"; - - //以方法的形式调用,this是调用方法的对象 - obj2.sayName(); - -``` - -打印结果: - -``` - Object - vae -``` - -上面的举例可以看出,this指向的是 对象 obj2 ,所以 this.name 指的是 obj2.name。 - -**箭头函数中this的指向**: - -ES6中的箭头函数并不会使用上面四条标准的绑定规则,而是会继承外层函数调用的this绑定(无论this绑定到什么)。 - -## 类数组 arguments - -> 这部分,小白可能看不懂。所以,这一段,暂时可以忽略。 - -在调用函数时,浏览器每次都会传递进两个隐含的参数: - -- 1.函数的上下文对象 this - -- 2.**封装实参的对象** arguments - - -例如: - -```javascript - function foo() { - console.log(arguments); - console.log(typeof arguments); - } - - foo(); -``` - -![](http://img.smyhvae.com/20180315_0903.png) - - - -arguments是一个类数组对象,它可以通过索引来操作数据,也可以获取长度。 - -**arguments代表的是实参**。在调用函数时,我们所传递的实参都会在arguments中保存。有个讲究的地方是:arguments**只在函数中使用**。 - - - - -### 1、返回函数**实参**的个数:arguments.length - - -arguments.length可以用来获取**实参的长度**。 - -举例: - -```javascript - fn(2,4); - fn(2,4,6); - fn(2,4,6,8); - - function fn(a,b) { - console.log(arguments); - console.log(fn.length); //获取形参的个数 - console.log(arguments.length); //获取实参的个数 - - console.log("----------------"); - } -``` - -打印结果: - -![](http://img.smyhvae.com/20180125_2140.png) - -我们即使不定义形参,也可以通过arguments来使用实参(只不过比较麻烦):arguments[0] 表示第一个实参、arguments[1] 表示第二个实参... - -### 2、返回正在执行的函数:arguments.callee - -arguments里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数对象。 - -```javascript - function fun() { - - console.log(arguments.callee == fun); //打印结果为true - } - - fun("hello"); -``` - -在使用函数**递归**调用时,推荐使用arguments.callee代替函数名本身。 - -### 3、arguments可以修改元素 - -之所以说arguments是伪数组,是因为:**arguments可以修改元素,但不能改变数组的长短**。举例: - -```javascript - fn(2,4); - fn(2,4,6); - fn(2,4,6,8); - - function fn(a,b) { - arguments[0] = 99; //将实参的第一个数改为99 - arguments.push(8); //此方法不通过,因为无法增加元素 - } - -``` - -## 我的公众号 - -想学习**代码之外的技能**?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。 - -扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外: - -![](http://img.smyhvae.com/2016040102.jpg) - - diff --git a/04-JavaScript基础/24-闭包.md b/04-JavaScript基础/23-闭包.md similarity index 80% rename from 04-JavaScript基础/24-闭包.md rename to 04-JavaScript基础/23-闭包.md index e38081a..2da1999 100644 --- a/04-JavaScript基础/24-闭包.md +++ b/04-JavaScript基础/23-闭包.md @@ -12,7 +12,7 @@ ```js function foo() { - let a = 1; + let a = 1; } foo(); @@ -39,12 +39,12 @@ console.log(a); // 打印报错:Uncaught ReferenceError: a is not defined ```js function fn1() { - let a = 10; + let a = 10; - function fn2() { - console.log(a); - } - fn2(); + function fn2() { + console.log(a); + } + fn2(); } fn1(); @@ -72,22 +72,33 @@ fn1(); ```js function fn1() { - let a = 20; + let a = 20; - function fn2() { - console.log(a); - } - return fn2; // 这一步,是实现闭包的精髓 + function fn2() { + console.log(a); + } + return fn2; } -const foo = fn1(); +const foo = fn1(); // 执行 fn1() 之后,会得到一个返回值。foo 代表的就是 fn2 函数 foo(); ``` -上方代码中,foo 代表的就是整个 fn2 函数。当执行了 `foo()` 语句之后,fn1 函数内就产生了闭包。 +上方代码中,foo 代表的就是整个 fn2 函数。当执行了 `foo()` 语句之后(相当于执行了 ),fn1 函数内就产生了闭包。 一般来说,在 fn1 函数执行完毕后,它里面的变量 a 会立即销毁。但此时由于产生了闭包,所以 **fn1 函数中的变量 a 不会立即销毁,因为 fn2 函数还要继续调用变量 a**。只有等所有函数把变量 a 调用完了,变量 a 才会销毁。 -而且,可以看出, 在执行 `foo()`语句之后,竟然能够打印出 `20`,这就完美通过闭包实现了:全局作用局成功访问到了局部作用域作用的变量 a。 +而且,可以看出, 在执行 `foo()`语句之后,竟然能够打印出 `20`,这就完美通过闭包实现了:全局作用局成功访问到了局部作用域中的变量 a。 因此,我们可以看出,闭包的主要作用就是:延伸了变量的作用范围。 + + + +## 我的公众号 + +想学习**代码之外的技能**?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。 + +扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外: + +![](http://img.smyhvae.com/20200101.png) + diff --git a/04-JavaScript基础/23-面向对象概述.md b/04-JavaScript基础/24-面向对象概述.md similarity index 97% rename from 04-JavaScript基础/23-面向对象概述.md rename to 04-JavaScript基础/24-面向对象概述.md index 722deea..3846575 100644 --- a/04-JavaScript基础/23-面向对象概述.md +++ b/04-JavaScript基础/24-面向对象概述.md @@ -44,4 +44,6 @@ JS 中的面向对象,是基于**原型**的面向对象。 ### 基于原型的面向对象 -JS 中的对象(Object)是依靠构造器(constructor)和原型(prototype)构造出来的。 \ No newline at end of file +JS 中的对象(Object)是依靠构造器(constructor)和原型(prototype)构造出来的。 + + diff --git a/04-JavaScript基础/25-对象的创建&构造函数.md b/04-JavaScript基础/25-对象的创建&构造函数.md index 27a8047..bf52d09 100644 --- a/04-JavaScript基础/25-对象的创建&构造函数.md +++ b/04-JavaScript基础/25-对象的创建&构造函数.md @@ -300,10 +300,12 @@ json 采用 `for...in...`进行遍历,和数组的遍历方式不同。如下 ![](http://img.smyhvae.com/20180203_1518.png) + ## 我的公众号 -想学习**代码之外的技能**?不妨关注我的微信公众号:**千古壹号**。 +想学习**代码之外的技能**?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。 扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外: -![](http://img.smyhvae.com/2016040102.jpg) +![](http://img.smyhvae.com/20200101.png) + diff --git a/04-JavaScript基础/28-原型继承.md b/04-JavaScript基础/28-原型链和原型继承.md similarity index 100% rename from 04-JavaScript基础/28-原型继承.md rename to 04-JavaScript基础/28-原型链和原型继承.md diff --git a/04-JavaScript基础/29-构造继承.md b/04-JavaScript基础/29-类和构造继承.md similarity index 100% rename from 04-JavaScript基础/29-构造继承.md rename to 04-JavaScript基础/29-类和构造继承.md diff --git a/05-JavaScript之ES6语法/04-ES6:变量let、const和块级作用域.md b/05-JavaScript之ES6语法/04-ES6:变量let、const和块级作用域.md index d318fcb..5096d9f 100644 --- a/05-JavaScript之ES6语法/04-ES6:变量let、const和块级作用域.md +++ b/05-JavaScript之ES6语法/04-ES6:变量let、const和块级作用域.md @@ -71,7 +71,7 @@ let 是防止数据污染,我们来看下面这个 **for 循环**的例子, ```javascript for (var i = 0; i < 10; i++) { - console.log('循环体中:' + i); // 每循环一次,就会在 { } 所在的块级作用域中,重新定义一个新的 i + console.log('循环体中:' + i); } console.log('循环体外:' + i); @@ -83,7 +83,7 @@ console.log('循环体外:' + i); ```javascript for (let i = 0; i < 10; i++) { - console.log('循环体中:' + i); + console.log('循环体中:' + i); // // 每循环一次,就会在 { } 所在的块级作用域中,重新定义一个新的 i } console.log('循环体外:' + i); @@ -155,7 +155,9 @@ let 和 const 的作用如下: ![](http://img.smyhvae.com/20190904_1030.gif) -你可能会感到诧异,为何点击任何一个按钮,弹出的内容都是 4 呢?这是因为,我们用 var 定义的变量 i,是在全局作用域声明的。整个代码中,自始至终只有一个变量。当我们还没点击按钮之前,变量 i 已经循环到 4 了。 +你可能会感到诧异,为何点击任何一个按钮,弹出的内容都是 4 呢?这是因为,我们用 var 定义的变量 i,是在全局作用域声明的。整个代码中,自始至终只有一个变量。 + +for 循环是同步代码,而 onclick 点击事件是异步代码。当我们还没点击按钮之前,同步代码已经执行完了,变量 i 已经循环到 4 了。 也就是说,上面的 for 循环,相当于如下代码: diff --git a/06-JavaScript进阶/01-var、let、const的区别.md b/06-JavaScript进阶/01-var、let、const的区别.md index 7c7cf26..88cd566 100644 --- a/06-JavaScript进阶/01-var、let、const的区别.md +++ b/06-JavaScript进阶/01-var、let、const的区别.md @@ -118,7 +118,7 @@ foo(); // 执行函数后,控制台报错:Uncaught ReferenceError: Cannot ac 代码解释:如果在当前块级作用域中使用了变量 name,并且当前块级作用域中通过 let/const 声明了这个变量,那么,**声明语句必须放在使用之前,也就是所谓的 DTC(暂时性死区)**。DTC 其实是一种保护机制,可以让我们养成良好的编程习惯。 -关于”暂时性死区“的更多介绍,详本项目的另一篇文章《ES6:变量》。 +关于”暂时性死区“的更多介绍,详本项目的另一篇文章《JavaScript之ES6语法:变量let、const和块级作用域.md》。 ### 6、const:一旦声明必须赋值;声明后不能再修改 @@ -178,8 +178,9 @@ obj.name = 'vae'; // 对象里的 name 属性可以被修改 详见《JavaScript基础/对象简介.md》。 +## for 循环的经典案例 - +详见《JavaScript之ES6语法:变量let、const和块级作用域.md》。 ## 参考链接