signed

QiShunwang

“诚信为本、客户至上”

2-44钟静雯_day05

2020/12/29 4:00:37   来源:

Day05作业练习
代码实例
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
1
2
3
4
5
6
7
8
文字描述
在这里插入图片描述

示例代码中全局环境下声明了三个变量和一个函数。当执行脚本时,JavaScript引擎首先创建一个全局执行上下文来执行全局代码,在创建全局执行上下文阶段,JavaScript引擎只是扫描了整个代码并分配了内存,然后再创建词法环境和变量环境,环境记录的类型是Object,环境记录中的变量a和b未初始化,而变量c的值被设置为underfined,外层环境是全局环境,所以对外层环境的引用为null,this绑定为全局对象本身。执行全局执行上下文阶段,变量赋值,执行语句顺序为let a = 20;const b = 30;var c;c = multiply(20, 30);当遇到调用函数multiply(20,30)时,就创建一个新的函数执行上下文来执行该函数代码。环境记录包含一个arguments对象,包含传入的两个参数20、30,变量g为undefined。之后,该执行函数上下文进入执行阶段,也就是说,该函数内的变量赋值已经完成。于是,在执行阶段期间,e、f、g被赋值为20、30、20,函数执行上下文执行完成,返回值存储在c,程序结束。

演变过程
在这里插入图片描述
在这里插入图片描述
Day05知识点总结 JavaScript运行机制
目录
JavaScript引擎

执行上下文和执行栈

提升

作用域与作用域链

PATR 1:JavaScript引擎如何执行JavaScript代码

function greeting() {
return ‘hi’//返回字符串
}
1
2
3
过程
在这里插入图片描述
总结
从网络、浏览器缓存或 service worker 加载脚本为UTF-16编码的字节流。
字节流解码器将字节流解析成 token。
解析器将 token 解析成抽象语法树(abstract syntax tree,AST)。
解释器将抽象语法树解释成字节码。
(可能)将字节码与类型反馈编译成高度优化过的机器码。
PATR 2:执行上下文和执行栈
ECMAScript代码有四种类型
全局代码
函数代码
eval代码
模块代码
function mean (…args) {//函数代码
let sum = 0;
for (let arg of args) {
sum = arg + sum
}
return sum / args.length
}

console.log(mean(1,2,3,4,5))//全局代码

//代码块
{
let b = 2;
let c =3;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
为执行JS代码,并跟踪其运行时求值,ECMAScript规范定义了执行上下文的概念。

执行上下文:表示 JavaScript 代码的执行环境。

执行上下文的四种类型
全局执行上下文: 默认或者基础的执行上下文。只要代码不在函数内,那么就是在全局执行上下文中。(运行创建、唯一)
函数执行上下文:每次调用一个函数时,就会创建一个新的执行上下文。
Eval函数执行上下文:不讨论。
模块执行上下文:讲模块化编程时候再讨论。
执行上下文的创建

在这里插入图片描述
执行栈(调用栈)
一种 LIFO(后进先出)的栈结构,用于存储代码执行期间所创建的执行上下文,维护控制流程和执行顺序。

调用其它上下文的上下文称为调用者(Caller)。
被调用的上下文称为被调用者(Callee)。
全局执行上下文总是在执行栈的底部。
// 运行代码的变量存在执行上下文里
let a = ‘Hello World!’;

function first() {
console.log(‘在 first 函数内’);
second();
console.log(‘再次在 first 函数内’);
}
function second() {
console.log(‘在 second 函数内’);
}

first();
console.log(‘在全局执行上下文中’);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
在这里插入图片描述

PATR 3:提升
console.log(sayHello)
console.log(strMessage)
console.log(sayHi)
function sayHello() {
return ‘Hello, JavaScript!’
}
var strMessage = ‘Hello, Freshman!’
var sayHi = function () {
return ‘Hi, JavaScript’
}

// 全局上下文
function sayHello() {
return ‘Hello, JavaScript!’
}

var strMessage;
var sayHi;
console.log(sayHello);//[Function: sayHello]
console.log(strMessage);//undefined
console.log(sayHi);//undefined
// 函数表达式:函数的定义可以放在函数调用之后。
strMessage = ‘Hello, Freshman!’
sayHi = function () {
return ‘Hi, JavaScript’;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
创建阶段:提升(Hoisting)是一种 JavaScript 解释器的行为,就是将所有变量声明和函数声明移到当前作用域的顶部,不管它们是在哪里定义的。

函数提升:使用函数声明定义的函数被自动提升,也就是说它们可以在它们被定义的位置之前被调用。
变量提升:使用var关键字的变量声明自动移到当前作用域的顶部。变量赋值不提升。
function foo() {
console.log(a);//undefined
var a = 1
console.log(a);//1
}
foo();

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

function foo() {
console.log(a);//暂时性死区
let a = 1;
console.log(a);
}
foo();//引用错误,a未初始化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
如果函数表达式是用 var 声明的,那么声明会被提升,不过实际的函数是不会提升的!

// 变量helloExpression的值为undefined,所以该函数不能被调用
helloExpression(); // 抛出一个错误
// 函数声明可以在它声明之前调用
helloDeclaration(); // 返回 ‘hello’
// 将函数表达式赋值给一个变量
var helloExpression = function() {
console.log(‘hello’)
}
// 声明一个函数声明
function helloDeclaration() {
console.log(‘hello’)
}
// 函数表达式只能在赋值后才能被调用
helloExpression(); // 返回 ‘hello’

// 全局对象方法
function foo() {
console.log(‘hi’);
}
global.foo();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
总结:

在执行代码之前,将函数和变量存储在内存中以用于执行上下文。这称为变量提升(hoisting)。
函数被存储为一个对整个函数的引用,用var关键字声明的变量的值为undefined,而用let和const关键字声明的变量未被初始化。
PATR 4:作用域与作用域链
指变量可见性 、可访问性。变量的作用域是该变量可以被访问的程序区域。

作用域
是通过词法环境实现的。由两个组件组成

环境记录:把变量名映射到变量值。这里是 JS 存储变量的地方。在环境记录中的一个键值条目就称为绑定(binding)。
对外层环境的引用。
类型

全局作用域。
函数作用域。
块作用域。
词法作用域
作用域是在词法分析时(编译时)而不是运行时确定的。与之对应的是动态作用域。

const name = “Lydia”;
const age = 21;
const city = “San Francisco”;

function getPersonInfo() {
const name = “Sarah”;
const age = 22;
return ${name} is ${age} and lives in ${city};
}
console.log(getPersonInfo());
1
2
3
4
5
6
7
8
9
10
块作用域
const age = 21
function checkAge() {
if (age < 21) {
const message = “You cannot drink!”
return message
} else {
const message = “You can drink!”
return message
}
}
1
2
3
4
5
6
7
8
9
10
可以把作用域链看作是对我们可以在当前执行上下文中可以访问的值的一个引用链。

PATR 5:this值的确定
全局作用域下,this 永远是全局对象。

函数中:根据函数调用的形式来确定。

函数调用模式:this 为全局对象
方法调用模式:点运算符前面的那个对象
构造函数模式:new foo() 返回的对象
Apply 模式: foo.call(thisObject)、foo.apply(thisObject): 第一个参数就是this。如果thisObject是null或undefined,那么会变成Global对象。
箭头函数不提供自身的 this 绑定(this 的值将保持为外层词法上下文的值)。

function foo(p1,p2) {
console.log(p1+p2);
console.log(this);
}

foo(1,2);//函数调用形式
foo.call(null, 1,2);