add: 作用域链
This commit is contained in:
parent
a1aaf9e2be
commit
4ce4574135
@ -44,15 +44,12 @@ function sayHello(){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 函数的定义
|
## 函数的定义/声明
|
||||||
|
|
||||||
|
|
||||||
### 方式一:函数声明(命名函数)
|
|
||||||
|
|
||||||
|
### 方式一:利用函数关键字自定义函数(命名函数)
|
||||||
|
|
||||||
使用`函数声明`来创建一个函数(也就是 function 关键字)。语法:
|
使用`函数声明`来创建一个函数(也就是 function 关键字)。语法:
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function 函数名([形参1,形参2...形参N]){ // 备注:语法中的中括号,表示“可选”
|
function 函数名([形参1,形参2...形参N]){ // 备注:语法中的中括号,表示“可选”
|
||||||
语句...
|
语句...
|
||||||
@ -84,7 +81,7 @@ PS:在有些编辑器中,方法写完之后,我们在方法的前面输入
|
|||||||
使用`函数表达式`来创建一个函数。语法:
|
使用`函数表达式`来创建一个函数。语法:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var 变量名/函数名 = function([形参1,形参2...形参N]){
|
var 变量名 = function([形参1,形参2...形参N]){
|
||||||
语句....
|
语句....
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -97,6 +94,15 @@ var fun2 = function() {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
解释如下:
|
||||||
|
|
||||||
|
|
||||||
|
- 上面的 fun2 是变量名,不是函数名。
|
||||||
|
|
||||||
|
- 函数表达式的声明方式跟声明变量类似,只不过变量里面存的是值,而函数表达式里面存的是函数。
|
||||||
|
|
||||||
|
- 函数表达式也可以传递参数。
|
||||||
|
|
||||||
从方式二的举例中可以看出:所谓的“函数表达式”,其实就是将匿名函数赋值给一个变量。
|
从方式二的举例中可以看出:所谓的“函数表达式”,其实就是将匿名函数赋值给一个变量。
|
||||||
|
|
||||||
### 方式三:使用构造函数 new Function()
|
### 方式三:使用构造函数 new Function()
|
||||||
@ -111,7 +117,6 @@ var 变量名/函数名 = new Function('形参1', '形参2', '函数体');
|
|||||||
|
|
||||||
注意,Function 里面的参数都必须是**字符串**格式。也就是说,形参也必须放在**字符串**里;函数体也是放在**字符串**里包裹起来,放在 Function 的最后一个参数的位置。
|
注意,Function 里面的参数都必须是**字符串**格式。也就是说,形参也必须放在**字符串**里;函数体也是放在**字符串**里包裹起来,放在 Function 的最后一个参数的位置。
|
||||||
|
|
||||||
|
|
||||||
代码举例:
|
代码举例:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -147,7 +152,6 @@ fun3(1, 2); // 调用函数
|
|||||||
|
|
||||||
## 函数的调用
|
## 函数的调用
|
||||||
|
|
||||||
|
|
||||||
### 方式1:普通函数的调用
|
### 方式1:普通函数的调用
|
||||||
|
|
||||||
函数调用的语法:
|
函数调用的语法:
|
||||||
@ -281,7 +285,6 @@ new Fun3();
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**形参:**
|
**形参:**
|
||||||
|
|
||||||
- 概念:形式上的参数。定义函数时传递的参数,当时并不知道是什么值。
|
- 概念:形式上的参数。定义函数时传递的参数,当时并不知道是什么值。
|
||||||
@ -332,7 +335,7 @@ helloworld
|
|||||||
|
|
||||||
- 如果实参的数量多余形参的数量,多余实参不会被赋值。
|
- 如果实参的数量多余形参的数量,多余实参不会被赋值。
|
||||||
|
|
||||||
- 如果实参的数量少于形参的数量,多余的形参会被定义为 undefined。表达式的运行结果为 NaN
|
- 如果实参的数量少于形参的数量,多余的形参会被定义为 undefined。表达式的运行结果为 NaN。
|
||||||
|
|
||||||
代码举例:
|
代码举例:
|
||||||
|
|
||||||
@ -377,7 +380,7 @@ return 的作用是结束方法。
|
|||||||
|
|
||||||
- return 的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
|
- return 的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
|
||||||
|
|
||||||
- 在函数中,return后的语句都不会执行(函数在执行完 return 语句之后停止并立即退出)
|
- 在函数中,return后的语句都不会执行(函数在执行完 return 语句之后停止并立即退出函数)
|
||||||
|
|
||||||
- 如果return语句后不跟任何值,就相当于返回一个undefined
|
- 如果return语句后不跟任何值,就相当于返回一个undefined
|
||||||
|
|
||||||
@ -387,7 +390,6 @@ return 的作用是结束方法。
|
|||||||
|
|
||||||
- return 只能返回一个值。如果用逗号隔开多个值,则以最后一个为准。
|
- return 只能返回一个值。如果用逗号隔开多个值,则以最后一个为准。
|
||||||
|
|
||||||
|
|
||||||
## 函数名、函数体和函数加载问题(重要,请记住)
|
## 函数名、函数体和函数加载问题(重要,请记住)
|
||||||
|
|
||||||
我们要记住:**函数名 == 整个函数**。举例:
|
我们要记住:**函数名 == 整个函数**。举例:
|
||||||
@ -420,7 +422,6 @@ function fn(){
|
|||||||
- return :1、退出循环。2、返回 return 语句中的值,同时结束当前的函数体内的代码,退出当前函数。
|
- return :1、退出循环。2、返回 return 语句中的值,同时结束当前的函数体内的代码,退出当前函数。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 立即执行函数
|
## 立即执行函数
|
||||||
|
|
||||||
现有匿名函数如下:
|
现有匿名函数如下:
|
||||||
@ -469,15 +470,15 @@ function fn(){
|
|||||||
|
|
||||||
## arguments 的使用
|
## arguments 的使用
|
||||||
|
|
||||||
当我们不确定有多少个参数传递的时候,可以用 **arguments** 来获取。在 JavaScript 中,arguments 实际上是当前函数的一个**内置对象**。所有函数都内置了一个 arguments 对象,arguments 对象中存储了**传递的所有实参**.
|
当我们不确定有多少个参数传递的时候,可以用 **arguments** 来获取。在 JavaScript 中,arguments 实际上是当前函数的一个**内置对象**。所有函数都内置了一个 arguments 对象(只有函数才有 arguments 对象),arguments 对象中存储了**传递的所有实参**.
|
||||||
|
|
||||||
arguments的展示形式是一个**伪数组**。伪数组具有以下特点:
|
arguments的展示形式是一个**伪数组**。伪数组具有以下特点:
|
||||||
|
|
||||||
- 可以进行遍历;length 属性。
|
- 可以进行遍历;具有数组的 length 属性。
|
||||||
|
|
||||||
- 按索引方式存储数据。
|
- 按索引方式存储数据。
|
||||||
|
|
||||||
- 不具有数组的 push、pop 等方法。
|
- 不具有数组的 push()、pop() 等方法。
|
||||||
|
|
||||||
**代码举例**:利用 arguments 求函数实参中的最大值
|
**代码举例**:利用 arguments 求函数实参中的最大值
|
||||||
|
|
||||||
@ -486,6 +487,7 @@ arguments的展示形式是一个**伪数组**。伪数组具有以下特点:
|
|||||||
```javascript
|
```javascript
|
||||||
function getMaxValue() {
|
function getMaxValue() {
|
||||||
var max = arguments[0];
|
var max = arguments[0];
|
||||||
|
// 通过 arguments 遍历实参
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
for (var i = 0; i < arguments.length; i++) {
|
||||||
if (max < arguments[i]) {
|
if (max < arguments[i]) {
|
||||||
max = arguments[i];
|
max = arguments[i];
|
@ -3,21 +3,21 @@
|
|||||||
|
|
||||||
## 作用域(Scope)的概念
|
## 作用域(Scope)的概念
|
||||||
|
|
||||||
通俗来讲,作用域是一个变量或函数的作用范围。作用域在**函数定义**时,就已经确定了。
|
- **概念**:通俗来讲,作用域是一个变量或函数的作用范围。作用域在**函数定义**时,就已经确定了。
|
||||||
|
|
||||||
|
- **目的**:为了提高程序的可靠性,同时减少命名冲突。
|
||||||
|
|
||||||
### 作用域的分类
|
### 作用域的分类
|
||||||
|
|
||||||
在js中,一共有两种作用域:
|
在 JS 中,一共有两种作用域:(ES6 之前)
|
||||||
|
|
||||||
- 全局作用域
|
- 全局作用域:作用于整个 script 标签内部,或者作用域一个独立的 JS 文件。
|
||||||
|
|
||||||
- 函数作用域
|
- 函数作用域(局部作用域):作用于函数内的代码环境。
|
||||||
|
|
||||||
### js 是函数级别作用域
|
### 作用域的访问关系
|
||||||
|
|
||||||
|
在内部作用域中可以访问到外部作用域的变量,在外部作用域中无法访问到内部作用域的变量。
|
||||||
js 是函数级别作用域:在内部作用域中可以访问到外部作用域的变量,在外部作用域中无法访问到内部作用域的变量。
|
|
||||||
|
|
||||||
代码举例:
|
代码举例:
|
||||||
|
|
||||||
@ -32,9 +32,31 @@ foo();
|
|||||||
console.log(b); // 报错:Uncaught ReferenceError: b is not defined。说明 外层作用域 无法访问 内层作用域 里的变量
|
console.log(b); // 报错:Uncaught ReferenceError: b is not defined。说明 外层作用域 无法访问 内层作用域 里的变量
|
||||||
```
|
```
|
||||||
|
|
||||||
定义在全局作用域的变量,叫「全局变量」。
|
### 变量的作用域
|
||||||
|
|
||||||
定义在函数作用域的变量,叫「局部变量」。
|
根据作用域的不同,变量可以分为两类:全局变量、布局变量。
|
||||||
|
|
||||||
|
**全局变量**:
|
||||||
|
|
||||||
|
- 在全局作用域下声明的变量,叫「全局变量」。在全局作用域的任何一地方,都可以访问这个变量。
|
||||||
|
|
||||||
|
- 在全局作用域下,使用 var 声明的变量是全局变量。
|
||||||
|
|
||||||
|
- 特殊情况:在函数内不使用 var 声明的变量也是全局变量(不建议这么用)。
|
||||||
|
|
||||||
|
**局部变量**:
|
||||||
|
|
||||||
|
- 定义在函数作用域的变量,叫「局部变量」。
|
||||||
|
|
||||||
|
- 在函数内部,使用 var 声明的变量是局部变量。
|
||||||
|
|
||||||
|
- 函数的**形参**也是属于局部变量。
|
||||||
|
|
||||||
|
从执行效率来看全局变量和局部变量:
|
||||||
|
|
||||||
|
- 全局变量:只有浏览器关闭时才会被销毁,比较占内存。
|
||||||
|
|
||||||
|
- 局部变量:当其所在的代码块运行结束后,就会被销毁,比较节约内存空间。
|
||||||
|
|
||||||
### 作用域的上下级关系
|
### 作用域的上下级关系
|
||||||
|
|
||||||
@ -56,36 +78,39 @@ console.log(b); // 报错:Uncaught ReferenceError: b is not defined。说明
|
|||||||
|
|
||||||
- 创建的**函数**都会作为window对象的方法保存。
|
- 创建的**函数**都会作为window对象的方法保存。
|
||||||
|
|
||||||
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到。
|
|
||||||
|
|
||||||
### 变量的声明提前(变量提升)
|
### 变量的声明提前(变量提升)
|
||||||
|
|
||||||
使用var关键字声明的变量( 比如 `var a = 1`),**会在所有的代码执行之前被声明**(但是不会赋值),但是如果声明变量时不是用var关键字(比如直接写`a = 1`),则变量不会被声明提前。
|
使用var关键字声明的变量( 比如 `var a = 1`),**会在所有的代码执行之前被声明**(但是不会赋值),但是如果声明变量时不是用var关键字(比如直接写`a = 1`),则变量不会被声明提前。
|
||||||
|
|
||||||
举例1:
|
**举例1**:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
console.log(a);
|
console.log(a);
|
||||||
var a = 123;
|
var a = 123;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
打印结果:undefined。注意,打印结果并没有报错,而是 undefined,说明变量 a 被提前声明了,只是尚未被赋值。
|
打印结果:undefined。注意,打印结果并没有报错,而是 undefined,说明变量 a 被提前声明了,只是尚未被赋值。
|
||||||
|
|
||||||
|
**举例2**:
|
||||||
举例2:
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
console.log(a);
|
console.log(a);
|
||||||
a = 123; //此时a相当于window.a
|
a = 123; //此时a相当于window.a
|
||||||
```
|
```
|
||||||
|
|
||||||
程序会报错:
|
程序会报错:`Uncaught ReferenceError: a is not defined`。
|
||||||
|
|
||||||
![](http://img.smyhvae.com/20180314_2136.png)
|
**举例3**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
a = 123; //此时a相当于window.a
|
||||||
|
console.log(a);
|
||||||
|
```
|
||||||
|
|
||||||
举例3:
|
打印结果:123。
|
||||||
|
|
||||||
|
**举例4**:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
foo();
|
foo();
|
||||||
@ -99,7 +124,7 @@ function foo() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
打印结果:undefined。注意,打印结果并没有报错,而是 undefined。这个例子,再次说明了:变量 i 在函数执行前,就被提前声明了,只是尚未被赋值。
|
打印结果:undefined。注意,打印结果并没有报错,而是 undefined。这个例子,再次说明了:变量 i 在函数执行前,就被提前声明了,只是尚未被赋值。
|
||||||
打印结果:
|
|
||||||
|
|
||||||
|
|
||||||
### 函数的声明提前
|
### 函数的声明提前
|
||||||
@ -125,7 +150,7 @@ function foo() {
|
|||||||
|
|
||||||
使用`函数表达式`创建的函数`var foo = function(){}`,**不会被声明提前**,所以不能在声明前调用。
|
使用`函数表达式`创建的函数`var foo = function(){}`,**不会被声明提前**,所以不能在声明前调用。
|
||||||
|
|
||||||
很好理解,因为此时foo被声明了(这里是变量声明),且为undefined,并没有把 `function(){}` 赋值给 foo。
|
很好理解,因为此时foo被声明了(这里只是变量声明),且为undefined,并没有把 `function(){}` 赋值给 foo。
|
||||||
|
|
||||||
所以说,下面的例子,会报错:
|
所以说,下面的例子,会报错:
|
||||||
|
|
||||||
@ -135,11 +160,11 @@ function foo() {
|
|||||||
|
|
||||||
**提醒1**:在函数作用域中,也有声明提前的特性:
|
**提醒1**:在函数作用域中,也有声明提前的特性:
|
||||||
|
|
||||||
- 函数中,使用var关键字声明的变量,会在函数中所有的代码执行之前被声明
|
- 函数中,使用var关键字声明的变量,会在函数中所有的代码执行之前被声明。
|
||||||
|
|
||||||
- 函数中,没有var声明的变量都是**全局变量**,而且并不会提前声明。
|
- 函数中,没有var声明的变量都是**全局变量**,而且并不会提前声明。
|
||||||
|
|
||||||
举例1:
|
举例:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var a = 1;
|
var a = 1;
|
||||||
@ -154,7 +179,7 @@ function foo() {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上方代码中,执行foo()后,打印结果是`1`。如果去掉第一行代码,打印结果是`Uncaught ReferenceError: a is not defined`
|
上方代码中,执行foo()后,函数里面的打印结果是`1`。如果去掉第一行代码,执行foo()后,函数里面的打印结果是`Uncaught ReferenceError: a is not defined`。
|
||||||
|
|
||||||
**提醒2**:定义形参就相当于在函数作用域中声明了变量。
|
**提醒2**:定义形参就相当于在函数作用域中声明了变量。
|
||||||
|
|
||||||
@ -167,6 +192,72 @@ function foo() {
|
|||||||
fun6(123);//打印结果为123
|
fun6(123);//打印结果为123
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## JavaScript 没有块级作用域(ES6之前)
|
||||||
|
|
||||||
|
在其他编程语言中(如 Java、C#等),存在块级作用域,由`{}`包括起来。比如在 Java 语言中,if 语句里创建的变量,只能在if语句内部使用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
if(true){
|
||||||
|
int num = 123;
|
||||||
|
system.out.print(num); // 123
|
||||||
|
}
|
||||||
|
system.out.print(num); // 报错
|
||||||
|
```
|
||||||
|
|
||||||
|
但是,在 JS 中没有块级作用域(ES6之前)。举例如下:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if(true){
|
||||||
|
var num = 123;
|
||||||
|
console.log(123); //123
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(123); //123(可以正常打印)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 作用域链
|
||||||
|
|
||||||
|
引入:
|
||||||
|
|
||||||
|
- 只要是代码,就至少有一个作用域
|
||||||
|
|
||||||
|
- 写在函数内部的局部作用域
|
||||||
|
|
||||||
|
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
|
||||||
|
|
||||||
|
基于上面几条内容,我们可以得出作用域链的概念。
|
||||||
|
|
||||||
|
**作用域链**:内部函数访问外部函数的变量,采用的是链式查找的方式来决定取哪个值,这种结构称之为作用域链。查找时,采用的是**就近原则**。
|
||||||
|
|
||||||
|
代码举例:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var num = 10;
|
||||||
|
|
||||||
|
function fn() {
|
||||||
|
// 外部函数
|
||||||
|
var num = 20;
|
||||||
|
|
||||||
|
function fun() {
|
||||||
|
// 内部函数
|
||||||
|
console.log(num);
|
||||||
|
}
|
||||||
|
fun();
|
||||||
|
}
|
||||||
|
fn();
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
打印结果:20。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 我的公众号
|
## 我的公众号
|
||||||
|
|
||||||
想学习<font color=#0000ff>**代码之外的技能**</font>?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。
|
想学习<font color=#0000ff>**代码之外的技能**</font>?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。
|
@ -1,35 +0,0 @@
|
|||||||
|
|
||||||
## 一些概念
|
|
||||||
|
|
||||||
### 作用域和作用域链
|
|
||||||
|
|
||||||
**作用域**:
|
|
||||||
|
|
||||||
每个 JS 函数都是一个对象,对象中有些属性我们可以访问,有些属性我们不可以访问。无法访问的这些属性仅供 JS 引擎存取,[[scope]] 属性就是其中之一。
|
|
||||||
|
|
||||||
[[scope]]就是我们所说的作用域,其中存储了执行期上下文的集合。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**作用域链**:
|
|
||||||
|
|
||||||
[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
|
|
||||||
|
|
||||||
|
|
||||||
### 执行期上下文
|
|
||||||
|
|
||||||
当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。
|
|
||||||
|
|
||||||
每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。
|
|
||||||
|
|
||||||
## 最后一段
|
|
||||||
|
|
||||||
备注:本文的内容还有待完善。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 参考链接
|
|
||||||
|
|
||||||
- <https://www.cnblogs.com/chenyingjie1207/p/9966036.html>
|
|
||||||
|
|
||||||
|
|
15
04-JavaScript基础/20-执行期上下文.md
Normal file
15
04-JavaScript基础/20-执行期上下文.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## 执行期上下文
|
||||||
|
|
||||||
|
当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。
|
||||||
|
|
||||||
|
每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 参考链接
|
||||||
|
|
||||||
|
- <https://www.cnblogs.com/chenyingjie1207/p/9966036.html>
|
||||||
|
|
||||||
|
|
@ -143,6 +143,8 @@ console.log(JSON.stringify(arr));
|
|||||||
|
|
||||||
可以使用`length`属性来获取数组的长度(即“元素的个数”)。
|
可以使用`length`属性来获取数组的长度(即“元素的个数”)。
|
||||||
|
|
||||||
|
数组的长度是元素个数,不要跟索引号混淆。
|
||||||
|
|
||||||
语法:
|
语法:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
Loading…
Reference in New Issue
Block a user