add: 作用域链

This commit is contained in:
qianguyihao 2020-02-06 18:01:18 +08:00
parent a1aaf9e2be
commit 4ce4574135
5 changed files with 148 additions and 73 deletions

View File

@ -44,15 +44,12 @@ function sayHello(){
}
```
## 函数的定义
### 方式一:函数声明(命名函数)
## 函数的定义/声明
### 方式一:利用函数关键字自定义函数(命名函数)
使用`函数声明`来创建一个函数(也就是 function 关键字)。语法:
```javascript
function 函数名([形参1,形参2...形参N]){ // 备注:语法中的中括号,表示“可选”
语句...
@ -84,7 +81,7 @@ PS在有些编辑器中方法写完之后我们在方法的前面输入
使用`函数表达式`来创建一个函数。语法:
```javascript
var 变量名/函数名 = function([形参1,形参2...形参N]){
var 变量名 = function([形参1,形参2...形参N]){
语句....
}
```
@ -97,6 +94,15 @@ var fun2 = function() {
};
```
解释如下:
- 上面的 fun2 是变量名,不是函数名。
- 函数表达式的声明方式跟声明变量类似,只不过变量里面存的是值,而函数表达式里面存的是函数。
- 函数表达式也可以传递参数。
从方式二的举例中可以看出:所谓的“函数表达式”,其实就是将匿名函数赋值给一个变量。
### 方式三:使用构造函数 new Function()
@ -111,7 +117,6 @@ var 变量名/函数名 = new Function('形参1', '形参2', '函数体');
注意Function 里面的参数都必须是**字符串**格式。也就是说,形参也必须放在**字符串**里;函数体也是放在**字符串**里包裹起来,放在 Function 的最后一个参数的位置。
代码举例:
```javascript
@ -147,7 +152,6 @@ fun3(1, 2); // 调用函数
## 函数的调用
### 方式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语句后不跟任何值就相当于返回一个undefined
@ -387,7 +390,6 @@ return 的作用是结束方法。
- return 只能返回一个值。如果用逗号隔开多个值,则以最后一个为准。
## 函数名、函数体和函数加载问题(重要,请记住)
我们要记住:**函数名 == 整个函数**。举例:
@ -420,7 +422,6 @@ function fn(){
- return 1、退出循环。2、返回 return 语句中的值,同时结束当前的函数体内的代码,退出当前函数。
## 立即执行函数
现有匿名函数如下:
@ -469,15 +470,15 @@ function fn(){
## arguments 的使用
当我们不确定有多少个参数传递的时候,可以用 **arguments** 来获取。在 JavaScript 中arguments 实际上是当前函数的一个**内置对象**。所有函数都内置了一个 arguments 对象arguments 对象中存储了**传递的所有实参**.
当我们不确定有多少个参数传递的时候,可以用 **arguments** 来获取。在 JavaScript 中arguments 实际上是当前函数的一个**内置对象**。所有函数都内置了一个 arguments 对象(只有函数才有 arguments 对象)arguments 对象中存储了**传递的所有实参**.
arguments的展示形式是一个**伪数组**。伪数组具有以下特点:
- 可以进行遍历length 属性。
- 可以进行遍历;具有数组的 length 属性。
- 按索引方式存储数据。
- 不具有数组的 push、pop 等方法。
- 不具有数组的 push()、pop() 等方法。
**代码举例**:利用 arguments 求函数实参中的最大值
@ -486,6 +487,7 @@ arguments的展示形式是一个**伪数组**。伪数组具有以下特点:
```javascript
function getMaxValue() {
var max = arguments[0];
// 通过 arguments 遍历实参
for (var i = 0; i < arguments.length; i++) {
if (max < arguments[i]) {
max = arguments[i];

View File

@ -3,21 +3,21 @@
## 作用域Scope的概念
通俗来讲,作用域是一个变量或函数的作用范围。作用域在**函数定义**时,就已经确定了。
- **概念**通俗来讲,作用域是一个变量或函数的作用范围。作用域在**函数定义**时,就已经确定了。
- **目的**:为了提高程序的可靠性,同时减少命名冲突。
### 作用域的分类
js中一共有两种作用域
JS 中一共有两种作用域ES6 之前)
- 全局作用域
- 全局作用域:作用于整个 script 标签内部,或者作用域一个独立的 JS 文件。
- 函数作用域
- 函数作用域(局部作用域):作用于函数内的代码环境。
### js 是函数级别作用域
### 作用域的访问关系
js 是函数级别作用域:在内部作用域中可以访问到外部作用域的变量,在外部作用域中无法访问到内部作用域的变量。
在内部作用域中可以访问到外部作用域的变量,在外部作用域中无法访问到内部作用域的变量。
代码举例:
@ -32,9 +32,31 @@ foo();
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对象的方法保存。
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到。
### 变量的声明提前(变量提升)
使用var关键字声明的变量 比如 `var a = 1`**会在所有的代码执行之前被声明**但是不会赋值但是如果声明变量时不是用var关键字比如直接写`a = 1`),则变量不会被声明提前。
举例1
**举例1**
```javascript
console.log(a);
var a = 123;
```
打印结果undefined。注意打印结果并没有报错而是 undefined说明变量 a 被提前声明了,只是尚未被赋值。
举例2
**举例2**
```javascript
console.log(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
foo();
@ -99,7 +124,7 @@ function foo() {
```
打印结果undefined。注意打印结果并没有报错而是 undefined。这个例子再次说明了变量 i 在函数执行前,就被提前声明了,只是尚未被赋值。
打印结果:
### 函数的声明提前
@ -125,7 +150,7 @@ function foo() {
使用`函数表达式`创建的函数`var foo = function(){}`**不会被声明提前**,所以不能在声明前调用。
很好理解因为此时foo被声明了这里是变量声明且为undefined并没有把 `function(){}` 赋值给 foo。
很好理解因为此时foo被声明了这里是变量声明且为undefined并没有把 `function(){}` 赋值给 foo。
所以说,下面的例子,会报错:
@ -135,11 +160,11 @@ function foo() {
**提醒1**:在函数作用域中,也有声明提前的特性:
- 函数中使用var关键字声明的变量会在函数中所有的代码执行之前被声明
- 函数中使用var关键字声明的变量会在函数中所有的代码执行之前被声明
- 函数中没有var声明的变量都是**全局变量**,而且并不会提前声明。
举例1
举例:
```javascript
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**:定义形参就相当于在函数作用域中声明了变量。
@ -167,6 +192,72 @@ function foo() {
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`)。

View File

@ -1,35 +0,0 @@
## 一些概念
### 作用域和作用域链
**作用域**
每个 JS 函数都是一个对象,对象中有些属性我们可以访问,有些属性我们不可以访问。无法访问的这些属性仅供 JS 引擎存取,[[scope]] 属性就是其中之一。
[[scope]]就是我们所说的作用域,其中存储了执行期上下文的集合。
**作用域链**
[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
### 执行期上下文
当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。
每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。
## 最后一段
备注:本文的内容还有待完善。
## 参考链接
- <https://www.cnblogs.com/chenyingjie1207/p/9966036.html>

View File

@ -0,0 +1,15 @@
## 执行期上下文
当**函数执行**时(准确来说,是在函数发生预编译的前一刻),会创建一个执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境。
每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立且独一无二的。当函数执行完毕,它所产生的执行期上下文会被销毁。
## 参考链接
- <https://www.cnblogs.com/chenyingjie1207/p/9966036.html>

View File

@ -143,6 +143,8 @@ console.log(JSON.stringify(arr));
可以使用`length`属性来获取数组的长度(即“元素的个数”)。
数组的长度是元素个数,不要跟索引号混淆。
语法:
```javascript