add: call()、apply()、bind()的区别
This commit is contained in:
parent
7d44e5a2c6
commit
1c075a0e7b
@ -1,6 +1,6 @@
|
||||
{
|
||||
"printWidth": 300,
|
||||
"tabWidth": 2,
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
|
@ -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 对象中存储了**传递的所有实参**.
|
||||
|
109
04-JavaScript基础/21-this指向.md
Normal file
109
04-JavaScript基础/21-this指向.md
Normal file
@ -0,0 +1,109 @@
|
||||
## 执行期上下文
|
||||
|
||||
当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。
|
||||
|
||||
每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。
|
||||
|
||||
参考链接:<https://www.cnblogs.com/chenyingjie1207/p/9966036.html>
|
||||
|
||||
## 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)
|
@ -1,15 +0,0 @@
|
||||
|
||||
|
||||
## 执行期上下文
|
||||
|
||||
当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。
|
||||
|
||||
每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。
|
||||
|
||||
|
||||
|
||||
## 参考链接
|
||||
|
||||
- <https://www.cnblogs.com/chenyingjie1207/p/9966036.html>
|
||||
|
||||
|
214
04-JavaScript基础/22-call、apply 和 bind.md
Normal file
214
04-JavaScript基础/22-call、apply 和 bind.md
Normal file
@ -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)
|
@ -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); //此方法不通过,因为无法增加元素
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 我的公众号
|
||||
|
||||
想学习<font color=#0000ff>**代码之外的技能**</font>?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。
|
||||
|
||||
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
|
||||
|
||||
![](http://img.smyhvae.com/2016040102.jpg)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -45,3 +45,5 @@ JS 中的面向对象,是基于**原型**的面向对象。
|
||||
|
||||
|
||||
JS 中的对象(Object)是依靠构造器(constructor)和原型(prototype)构造出来的。
|
||||
|
||||
|
@ -300,10 +300,12 @@ json 采用 `for...in...`进行遍历,和数组的遍历方式不同。如下
|
||||
|
||||
![](http://img.smyhvae.com/20180203_1518.png)
|
||||
|
||||
|
||||
## 我的公众号
|
||||
|
||||
想学习<font color=#0000ff>**代码之外的技能**</font>?不妨关注我的微信公众号:**千古壹号**。
|
||||
想学习**代码之外的技能**?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。
|
||||
|
||||
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
|
||||
|
||||
![](http://img.smyhvae.com/2016040102.jpg)
|
||||
![](http://img.smyhvae.com/20200101.png)
|
||||
|
||||
|
@ -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 循环,相当于如下代码:
|
||||
|
||||
|
@ -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》。
|
||||
|
||||
## 参考链接
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user