理解javaScript中的作用域和上下文Understanding Scope and Context in JavaScript

译者注:一直对于作用域和上下文感到很混乱,无意中看到这篇文章,觉得讲得很好,故翻译来与大家分享。翻译不好之处,请大家多多指教。

原文链接:http://ryanmorr.com/understanding-scope-and-context-in-javascript/

 

前言部分,不做翻译。

Context vs. Scope

    首先我们要明确,上下文和作用域是不同的概念。我注意到许多的开发者(包括我)长久以来都对这二者感到迷惑,错误地将其中一方描述为另一方。公平地说,这些术语已经被混淆很多年了。

    每一次函数调用都有一个作用域以及与之对应的上下文。从根本上讲,作用域是基于函数的而上下文是基于对象的。即,作用域是关于函数被调用时对变量的访问。并且,每一次调用,作用域都是不同的。上下文是关键字 this 的值,即指向“拥有”当前执行代码的对象。

 

Variable Scope

一个变量可以被定义为局部变量或者全局变量,它确立了在运行的时候在不同的作用域中,是否可以访问到它。任何被定义了的全局变量,即那些在函数体外定义的变量,会在整个运行周期中存在,并且在每个作用域中访问和修改。局部变量只存在于定义它的函数中,每一次调用的时候都会有不同的作用域。它被在函数中用于赋值,检索,操作并且无法在该作用域外面被接触到。

ECMAScript6(ES6/ES2015)引入了关键字 let 和 const,它们支持块级作用域。这意味着变量可以被限制在定义它的块级作用域中。let和const的区别是,const,从它的名字可以看出,它是一个只读的值。但这不意味着它的值是不变的只是它的变量不可以重新被定义。(译者注:比如一个引用类型的值,如对象,我们不可以改变它指向的对象,但是可以改变它的对象的属性值)

 

What is “this” Context

    上下文是由函数如何被调用决定的。当一个函数被作为对象的方法调用时,this指向调用这个方法的对象:

var obj = {

foo: function() {

return this;

}};

 

obj.foo() === obj; // true

    同样的准则也适用于当使用 new操作符来创建一个对象实例的时候。使用这个方法调用时,函数作用域中的this会指向新创建的实例。

function foo() {

alert(this);}

 

foo() // windownew foo() // foo

    当一个未被绑定的函数被调用时,this 默认指向全局上下文,在浏览器中指向window对象。当韩式在严格模式下运行时,此时的上下文是undefined。

 

Execution Context
    javaScript是一门单线程语言,意味着每一次它只能执行一个任务。当javascript开始解释代码时,它会默认进入全局execution context。从此刻开始,每一次的函数调用都会创建一个新的执行上下文。

    然而,这就是迷惑产生的地方。“exection context”更多的是涉及作用域,而不是我们之前提到的上下文。这是一个令人遗憾的命名规范。然而,它是由ECMAScript 规范决定的,所以我们只能接受了。

    每当一个新的execution context被创建时,它被添加到execution栈的顶端。浏览器总是会执行当前的在exection stack顶端的execution context。一旦执行完,它会被从栈的顶端移除,控制权会返回给下面的execution context。

    一个execution context 可以被分成创建和执行两个时期。在创建时期,解释器会先创建一个变量对象(variable object)(也叫活动对象)。他由所有的在execution context定义的变量和函数声明,以及函数参数组成。然后,在活动对象中,作用域链被初始化。最后,this确定指向。之后,在执行阶段,代码被解释和执行。(译者注:活动对象无法被我们访问,但是解释器在处理数据是会在后台使用到它.参考《javaScript高级程序设计(第3版)》)

 

The Scope Chain

对于每一个执行上下文,都有一条与之对应的作用域链。作用域链包括了execution stack中每一个execution context的活动对象,它被用来决定去哪获取变量以及标识符的解析。

 

function first() {

second();

function second() {

third();

function third() {

fourth();

function fourth() {

// do something

}

}

}

}

first();

运行前面的代码回导致后面的代码被执行,直到fourth这个函数。此时,作用域链的顶端到底部分别是 fourth, third, second, first, global.。fourth这个函数可以获取全局变量以及first,second,third函数中定义的任何变量,正如这些函数本身一样。

    命名冲突时通过从局部到全局顺着作用域链寻找来解决的。这意味着,在作用域链更顶端的变量拥有更高的优先级。

    简而言之,每次你想要在一个执行上下文中存取变量时,查找的过程总会从当前的变量对象开始。如果变量对象没被找到,那么就会顺着作用域链一直寻找。它会顺着作用域链,搜寻每一个活动对象,来匹配当前的变量名称。

 

Closures

在直接语法作用域之外存取变量称为闭包。换句话说,一个闭包就是在一个函数中定义另一个函数,并且允许里面的函数存取外面的函数的值。返回的嵌套在里面的函数允许你保持存取局部变量,函数参数,以及外层函数的内层函数的权利。这种封装允许我们在暴露一个公共接口时隐藏并且保留外层函数的执行上下文,从而允许进一步的封装。简单的例子如下。

function foo() {

var localVariable = 'private variable';

return function() {

return localVariable;

}}

 

var getLocalVariable = foo();

getLocalVariable() // "private variable"

最受欢迎的闭包类型就是众所周知的模块模式。它允许你去模仿公有的,私有的,以及特权成员。

var Module = (function() {

var privateProperty = 'foo';

 

function privateMethod(args) {

// do something

}

 

return {

 

publicProperty: '',

 

publicMethod: function(args) {

// do something

},

 

privilegedMethod: function(args) {

return privateMethod(args);

}

};})();

这个模块表现得像单例一般,解释器开始解释的时候它就执行了,因为函数后面有圆括号。在闭包的执行上下文外面唯一的变量方法是你的公共方法和返回对象中的那些属性。然而,所有的私有属性和方法会在应用的整个生命周期存在因为执行上下文被保留了。意味着变量会通过共有的方法进行更进一步的交互。

另一种闭包类型是立即执行函数表达式,不外乎就是在window上下文中自调用的并执行的匿名函数。

这个表达式在想要保留全局命名空间中任何在函数中声明的变量的作用余限定在闭包中,但是在应用运行的整个生命周期都存在,会非常好用。这是为应用和框架封装源代码的非常受欢迎的方法,典型的是暴露一个单独的全局接口给交互对象。

 

Call and Apply

所用的函数都有两种自带的方法允许你在任何上下文中执行函数。这带来了难以置信的功能。call函数要求参数是所有列明的参数,而apply允许使用参数数组。

function user(firstName, lastName, age) {

// do something }

 

user.call(window, 'John', 'Doe', 30);

user.apply(window, ['John', 'Doe', 30]);

ES5引入了Function.prototype.bind方法来操作上下文。它返回一个永远绑定在bind()函数第一个参数上的新函数,忽略函数是如何被使用的。这在一个闭包在适当的上下文中需要重定向是非常有用。

if(!('bind' in Function.prototype)){

Function.prototype.bind = function() {

var fn = this;

var context = arguments[0];

var args = Array.prototype.slice.call(arguments, 1);

return function() {

return fn.apply(context, args.concat([].slice.call(arguments)));

}

}}

它被普遍地使用在上下文缺失的情况下,面向对象设计和事件处理中。这很重要,因为结点的addEventListener方法,因为它的回掉函数必须在它绑定的结点的上下文中执行。然而,如果你使用先进的面向对象技术并且要求你的互调函数是一个方法的实例,你必须手动地调整上下文,这就是bind非常便利的地方。

function Widget() {

this.element = document.createElement('div');

this.element.addEventListener('click', this.onClick.bind(this), false);}

 

Widget.prototype.onClick = function(e) {

// do something};

从前面的Function.prototype.bind函数的代码中我们可以看到,Array的slice方法的两种调用方式。

Array.prototype.slice.call(arguments, 1);[].slice.call(arguments);

有趣的是,arguments对象并不是严格意义上的数组,就像nodelist一样,它被描述为类数组的东西。它包括了length属性和索引值,但它不是数组,并且后来也不支持array的自带的方法,如slice和push.但是,由于他们相似的行为,如果你愿意的话,array的方法可以被运用或者强制转换,并且在类似数组上下文的中执行,就想上面提到的那样。

    采用另一种对象方法的技术也可以运用到面向对象中,当我们需要在javaScript中效仿经典的的继承。

SubClass.prototype.init = function(){

// call the superclass init method in the context of the "SubClass" instance

SuperClass.prototype.init.apply(this, arguments);}

 

Conclusion

在开始学习先进的设计模式之前理解这些概念是很重要的,因为context和scope在现代的JavaScript中占据了重要的地位。每当我们谈及闭包,面向对象,继承,以及各种原生实现时,context和scope扮演着举足轻重的地位,如果你有志于钻研javascript语言 ,最好明白它包含了什么,而context和scope应该是最开始要明确学习的东西。

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值