执行上下文(Execution context)

执行上下文(简称上下文)决定了Js执行过程中可以获取哪些变量、函数、数据,一段程序可能被分割成许多不同的上下文,每一个上下文都会绑定一个变量对象(variable object),它就像一个容器,用来存储当前上下文中所有已定义或可获取的变量、函数等。位于最顶端或最外层的上下文称为全局上下文(global context),全局上下文取决于执行环境,如Node中的global和Browser中的window:
需要注意的是,上下文与作用域(scope)是不同的概念。Js本身是单线程的,每当有function被执行时,就会产生一个新的上下文,这一上下文会被压入Js的上下文堆栈(context stack)中,function执行结束后则被弹出,因此Js解释器总是在栈顶上下文中执行。在生成新的上下文时,首先会绑定该上下文的变量对象,其中包括arguments和该函数中定义的变量;之后会创建属于该上下文的作用域链(scope chain),最后将this赋予这一function所属的Object,这一过程可以通过下图表示:
this
上文提到this被赋予function所属的Object,具体来说,当function是定义在global对中时,this指向global;当function作为Object的方法时,this指向该Object:
var x = 1;
var f = function(){
console.log(this.x);
}
f(); // -> 1
var ff = function(){
this.x = 2;
console.log(this.x);
}
ff(); // -> 2
x // -> 2
var o = {x: "o's x", f: f};
o.f(); // "o's x"
Scope chain
上文提到,在function被执行时生成新的上下文时会先绑定当前上下文的变量对象,再创建作用域链。我们知道function的定义是可以嵌套在其他function所创建的上下文中,也可以并列地定义在同一个上下文中(如global)。作用域链实际上就是自下而上地将所有嵌套定义的上下文所绑定的变量对象串接到一起,使嵌套的function可以“继承”上层上下文的变量,而并列的function之间互不干扰:
var x = 'global';
function a(){
var x = "a's x";
function b(){
var y = "b's y";
console.log(x);
};
b();
}
function c(){
var x = "c's x";
function d(){
console.log(y);
};
d();
}
a(); // -> "a's x"
c(); // -> ReferenceError: y is not defined
x // -> "global"
y // -> ReferenceError: y is not defined
Closure
如果理解了上文中提到的上下文与作用域链的机制,再来看闭包的概念就很清楚了。每个function在调用时会创建新的上下文及作用域链,而作用域链就是将外层(上层)上下文所绑定的变量对象逐一串连起来,使当前function可以获取外层上下文的变量、数据等。如果我们在function中定义新的function,同时将内层function作为值返回,那么内层function所包含的作用域链将会一起返回,即使内层function在其他上下文中执行,其内部的作用域链仍然保持着原有的数据,而当前的上下文可能无法获取原先外层function中的数据,使得function内部的作用域链被保护起来,从而形成“闭包”。
看下面的例子:
var x = 100;
var inc = function(){
var x = 0;
return function(){
console.log(x++);
};
};
var inc1 = inc();
var inc2 = inc();
inc1(); // -> 0
inc1(); // -> 1
inc2(); // -> 0
inc1(); // -> 2
inc2(); // -> 1
x; // -> 100
执行过程如下图所示,inc内部返回的匿名function在创建时生成的作用域链包括了inc中的x,即使后来赋值给inc1和inc2之后,直接在global context下调用,它们的作用域链仍然是由定义中所处的上下文环境决定,而且由于x是在function inc中定义的,无法被外层的global context所改变,从而实现了闭包的效果:
this in closure
我们已经反复提到执行上下文和作用域实际上是通过function创建、分割的,而function中的this与作用域链不同,它是由执行该function时当前所处的Object环境所决定的,这也是this最容易被混淆用错的一点。一般情况下的例子如下:
var name = "global";
var o = {
name: "o",
getName: function(){
return this.name
}
};
o.getName(); // -> "o"
由于执行o.getName()时getName所绑定的this是调用它的o,所以此时this == o;更容易搞混的是在closure条件下:
var name = "global";
var oo = {
name: "oo",
getNameFunc: function(){
return function(){
return this.name;
};
}
}
oo.getNameFunc()(); // -> "global"
此时闭包函数被return后调用相当于:
getName = oo.getNameFunc(); getName(); // -> "global"
换一个更明显的例子:
var ooo = {
name: "ooo",
getName: oo.getNameFunc() // 此时闭包函数的this被绑定到新的Object
};
ooo.getName(); // -> "ooo"
当然,有时候为了避免闭包中的this在执行时被替换,可以采取下面的方法:
var name = "global";
var oooo = {
name: "ox4",
getNameFunc: function(){
var self = this;
return function(){
return self.name;
};
}
};
oooo.getNameFunc()(); // -> "ox4"
或者是在调用时强行定义执行的Object:
var name = "global";
var oo = {
name: "oo",
getNameFunc: function(){
return function(){
return this.name;
};
}
}
oo.getNameFunc()(); // -> "global"
oo.getNameFunc().bind(oo)(); // -> "oo"
总结
Js是一门很有趣的语言,由于它的很多特性是针对HTML中DOM的操作,因而显得随意而略失严谨,但随着前端的不断繁荣发展和Node的兴起,Js已经不再是"toy language"或是jQuery时代的"CSS扩展",本文提到的这些概念无论是对新手还是从传统Web开发中过度过来的Js开发人员来说,都很容易被混淆或误解,希望本文对大家能有所帮助。
# javascript作用域
# javascript执行上下文
# javascript的作用域
# 简单了解JavaScript中的执行上下文和堆栈
# JavaScript 中的执行上下文和执行栈实例讲解
# js 执行上下文和作用域的相关总结
# Javascript执行上下文顺序的深入讲解
# 通过实例了解JS执行上下文运行原理
# 对于Javascript 执行上下文的全面了解
# JS ES6中setTimeout函数的执行上下文示例
# JavaScript实现显示函数调用堆栈的方法
# JavaScript调用堆栈及setTimeout使用方法深入剖析
# 详解JavaScript中的执行上下文及调用堆栈
# 绑定
# 是在
# 这一
# 是由
# 所处
# 的是
# 就会
# 就像
# 将会
# 自下而上
# 是从
# 可以通过
# 都很
# 再来
# 弹出
# 很清楚
# 所示
# 后将
# 更容易
# 后会
相关文章:
如何用已有域名快速搭建网站?
中山网站制作网页,中山新生登记系统登记流程?
北京网站制作公司哪家好一点,北京租房网站有哪些?
头像制作网站在线制作软件,dw网页背景图像怎么设置?
如何用wdcp快速搭建高效网站?
制作证书网站有哪些,全国城建培训中心证书查询官网?
制作网站哪家好,cc、.co、.cm哪个域名更适合做网站?
香港网站服务器数量如何影响SEO优化效果?
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南
如何在腾讯云免费申请建站?
长春网站建设制作公司,长春的网络公司怎么样主要是能做网站的?
子杰智能建站系统|零代码开发与AI生成SEO优化指南
如何在阿里云购买域名并搭建网站?
免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?
广州网站设计制作一条龙,广州巨网网络科技有限公司是干什么的?
零服务器AI建站解决方案:快速部署与云端平台低成本实践
北京建设网站制作公司,北京古代建筑博物馆预约官网?
c++如何打印函数堆栈信息_c++ backtrace函数与符号名解析【方法】
宝塔Windows建站如何避免显示默认IIS页面?
义乌企业网站制作公司,请问义乌比较好的批发小商品的网站是什么?
存储型VPS适合搭建中小型网站吗?
成都网站制作价格表,现在成都广电的单独网络宽带有多少的,资费是什么情况呢?
广东企业建站网站优化与SEO营销核心策略指南
如何配置FTP站点权限与安全设置?
建站之星后台密码遗忘或太弱?如何重置与强化?
制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?
广州营销型建站服务商推荐:技术优势与SEO优化解析
详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)
代购小票制作网站有哪些,购物小票的简要说明?
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
建站主机解析:虚拟主机配置与服务器选择指南
已有域名和空间,如何快速搭建网站?
如何快速搭建FTP站点实现文件共享?
北京网站制作网页,网站升级改版需要多久?
如何在建站主机中优化服务器配置?
清单制作人网站有哪些,近日“兴风作浪的姑奶奶”引起很多人的关注这是什么事情?
制作宣传网站的软件,小红书可以宣传网站吗?
清除minerd进程的简单方法
建站之星会员如何解锁更多建站功能?
如何高效利用亚马逊云主机搭建企业网站?
如何在云主机上快速搭建网站?
网站制作外包价格怎么算,招聘网站上写的“外包”是什么意思?
建站主机选虚拟主机还是云服务器更好?
制作旅游网站html,怎样注册旅游网站?
C++如何编写函数模板?(泛型编程入门)
威客平台建站流程解析:高效搭建教程与设计优化方案
青岛网站建设如何选择本地服务器?
如何规划企业建站流程的关键步骤?
c++怎么使用类型萃取type_traits_c++ 模板元编程类型判断【方法】
*请认真填写需求信息,我们会在24小时内与您取得联系。