调用栈和堆,调用栈是代码实际执行的地方,使用执行上下文(执行环境来完成;堆是非结构化的内存池,存储了应用程序所需要的所有对象。
执行上下文是什么?
全局执行上下文:代码编译完成后进入调用栈执行首先创建全局执行上下文(整个项目只有一个全局执行上下文),是用来执行顶层代码(函数除外,函数只在被调用的时候执行)。
执行上下文:执行一段JavaScript的环境,存储了一些代码执行所需要的必要信息,比如传递给函数的局部变量或者参数。打个比方:我们点外卖,送来的袋子(执行上下文)不只有外卖(JavaScript代码),还有餐具(代码执行所需要的必要信息)。
执行上下文分为三个部分,依次为变量环境,作用域和this关键字:
变量环境VE
-
函数声明
作用域
作用域(scoping:由JavaScript引擎组织和访问,控制我们程序的变量。
主要分为以下三个
-
函数作用域:只能在函数中访问到的对象具有函数作用域,也称局部作用域。
-
块作用域:ES6新特性,类似于函数作用域,指的是大括号括起来的块,比如if和for循环,块中的变量只能在块中访问,具有块作用域。但只能用let和const,使用var仍能被全局访问。
if (birthYear >= 1981 && birthYear <= 1996 { var millenial = true; const str = `oh,you're a millenial,${firstName}`; console.log(str; function add(a, b { return a + b; } } console.log(str;//str不能被打印 console.log(add(1, 1;//add函数不能被打印 console.log(millenial;//var变量能打印,能被全局访问
全局作用域:在代码中任何地方都能访问到的对象拥有全局作用域。
作用域链:若在当前作用域无法查找到需要变量,则通过作用域链来进行变量查找,子作用域查找使用父作用域的变量。
注:如果子作用域和父作用域存在相同的变量名,则直接查找子作用域的,无需进行变量查找。
this关键字
- 添加self变量=this(ES6之前的旧方法
- 将嵌套函数改成箭头函数
-
常规函数声明 this为undefined
const calcAge = function (birthYear { console.log(2037 - birthYear; console.log(this;//显示undefined }; calcAge(1991;
-
箭头函数 箭头函数没有this关键字,箭头函数的关键词会通过查找父函数的关键词,若没有则为全局的关键词,即指向全局窗口。
console.log(this;//指向全局窗口window const calcAgeArrow = birthYear => { console.log(2037 - birthYear; console.log(this;//指向全局窗口window }; calcAgeArrow(1991; const luki = { fullName: 'lukirence', year: 2002, calcAge: function ( { console.log(this; console.log(2037 - this.year; }, greet: ( => { console.log(`hi,${this.fullName}`; }, }; luki.greet(;//显示hi undefined,因为this指向window对象
方法 this指向调用方法的对象
const luki = {
name : 'lukirence',
year : 2002,
calcAge:function({
return 2037-this.year;
}
};
如果在方法内的函数嵌套一个新的函数,该嵌套函数相当于常规函数,this关键字为undefined
const luki = {
fullName: 'lukirence',
year: 2002,
calcAge: function ( {
console.log(this;
console.log(2037 - this.year;
const isMillenial = function({
console.log(this;//undefined
console.log(this.year >=1981;
}
isMillenial(;
}
};
如何解决嵌套函数能够使用this?
const luki = {
fullName: 'lukirence',
year: 2002,
calcAge: function ( {
console.log(this;
console.log(2037 - this.year;
const self = this;
const isMillenial = function({
console.log(self;
console.log(self.year >=1981;
}
isMillenial(;
}
};
const luki = {
fullName: 'lukirence',
year: 2002,
calcAge: function ( {
console.log(this;
console.log(2037 - this.year;
const isMillenial = (=>{
console.log(this;
console.log(this.year >=1981;
}
isMillenial(;
}
};
- 事件监听 this为事件处理器所添加的DOM元素
注:箭头函数的执行上下文不包括参数对象和this关键字
执行上下文创建
将以以下代码为例,按每一步骤描述执行上下文创建流程:
const name = 'luki';//--------------------------------1
const first =( =>{//---------------------------------2
let a =1;//-------------------------------------2.1
const b =second(1,2;//-------------------------2.2
a=a+b;//----------------------------------------2.3
return a;//-------------------------------------2.4
};
function second(x,y{//-------------------------------3
var c =2;//-------------------------------------3.1
return c;//-------------------------------------3.2
}
const x =first(;//-----------------------------------4
-
执行到序号4开始调用first(函数,并创建first(的执行上下文推入调用栈,准备执行first(内部代码跳转到2.1;
-
运行2.2调用新函数second(,创建second(的执行上下文推入调用栈,停止first(的执行跳转到3.1;
代码被编译后先创建全局执行上下文推入调用栈(call stack;
-
运行3.2return语句表示完成该函数执行,second(的执行上下文将从调用栈中弹出(此处虽然弹出,但是其中变量环境仍可能被使用,涉及到闭包的概念,调用栈重新指向first(,代码重新跳转回2.3;
-
运行2.4,first(的执行上下文将从调用栈中弹出,调用栈重新指向gloal(,代码重新跳转回4,将返回值最终赋值给x;
- 此后调用栈一直保持在这个状态直到我们关闭浏览器来终止程序,最终弹出global(全局上下文;
由此我们可以发现调用栈的执行上下文根据函数调用来出入栈确保了JS引擎能正确执行代码的顺序。