作用域

LHSRHS引用

LHSRHS的含义是“赋值操作的左侧或右侧”并不一定意味着就是"= 赋值操作符的左侧或右侧"。赋值操作还有其他几种形式,因此在概念上最好将其理解为"赋值操作的目标是谁(LHS)"以及"谁是赋值操作的源头(RHS)"。

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用LHS查询;如果目的是获取变量的值,就会使用RHS查询。
赋值操作符会导致LHS查询。=操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作。

function foo(a) {
    var b = a;
    return a + b;
}
var c = foo( 2 );

/*
  LHS查询(3处) --> c = ...、a = 2(传参隐式变量分配)、b = ...
  RHS查询(4处) --> foo(2..、 = a、函数内部return的a、b
*/

作用域嵌套与异常

当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。

作用域链
  • LHSRHS引用都会在当前作用域进行查找,如果没有找到,就会坐电梯似的前往上一层作用域,如果还是没有找到就继续向上
  • 一旦抵达全局作用域,可能找到所需变量,也可能没找到,但是不管结果如何,查找过程到此停止
  • RHS在作用域中找不到所需的变量(未声明),引擎就会抛出ReferenceError异常
  • LHS在作用域中找不到所需的变量,非严格模式会在全局作用域中创建一个该名称的变量返回给引擎,严格模式回抛出同RHS查询类似的ReferenceError异常
  • 如果RHS查询到了所需变量,但是尝试对变量的值进行不合理的操作,比如对非函数就行函数调用,或者引用null或者undefined类型值的属性,引擎会抛出TypeError异常
  • var a
    var number = 1
    var object = null
    
    function fn1(){    
      var b = 1
      console.log(b) // 1 在当前作用域找到了变量
    }
        
    function fn2(){
      console.log(a) // undefined 在当前作用域没找到变量,但是在全局作用域找到了对应变量
    }
        
    function fn3(){
      console.log(d) // ReferenceError
    }
    
    function fn4(){
      console.log(number()) // TypeError 能找到number变量,但是number不是函数类型
      console.log(a.x) // TypeError a的值为undefined
      console.log(object.x)  // TypeError object的值为null
    }
    
  • ReferenceError表示作用域判别失败相关,TypeError则表示作用域判别成了了,但是对结果的操作是非法的

词法作用域

遮蔽效应

作用域共有两种主要的工作模型。第一种是最为普遍的,被大多数编程语言所采用的词法作用域,还有一种叫动态作用域(Bash,Perl),动态作用域只关心函数在何处调用,也就是动态作用域链是基于调用栈的,而不是代码中的作用域嵌套。

作用域在查找到第一个匹配的标识符时停止。在嵌套的作用中,可以定义同名的标识符,这叫做"遮蔽效应",抛开"遮蔽效应",作用域查找都是从运行时所处的最内部作用域开始,逐级向上查找,直到找到匹配的第一个标识符为止

全局变量会自动成为全局对象的属性(浏览器中的window对象),全局对象可以访问被遮蔽的全局变量,非全局变量被遮蔽了,外部是访问不到的

var a = 1
function fn1(){
  var a = 2
  console.log(a) // 2 全局作用域的变量a被当前作用域的变量a遮蔽了
  function fn2(){
    var a = 3
    console.log(a) // 3 同上
  }
  fn2()
}

function fn3(){
  b = 4
}

fn1()
fn3()
console.log(b) 或者 console.log(window.b) // 4 虽然变量b是在fn3的内部,但是它是全局变量(window的属性),所以,可以在外部访问

欺骗词法与性能

evalwith可以实现作用域欺骗(运行时来修改作用域),严格模式下eval被部分禁用,with被完全禁用。不推荐使用

// 非严格模式
function foo(str, a) {
  eval(str) // 欺骗!
  console.log( a, b )
}
var b = 2
foo( 'var b = 3', 1 ) // 1, 3

evalwith的使用,使JavaScript引擎没办法做词法的静态分析,无法预先确定变量和函数的定义位置,所以,JavaScript引擎的编译优化,对于evalwith来说,就变得毫无意义,因此,大量使用evalwith,程序运行都会变慢,影响性能

函数作用域与块作用域

函数作用域

函数都具有自己的作用域,外部作用域对函数内部作用域的变量进行访问会报错

  • 匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难。
  • 如果没有函数名,当函数需要引用自身时只能使用已经过期的arguments.callee引用。
  • 匿名函数省略了对于代码可读性 / 可理解性很重要的函数名

块作用域

除了函数作用域之外,还有一种块作用域,使创建的作用域仅作用在当前块中,避免使其泄漏到外部作用域中

withtry/catchletconst都可以将变量绑定到所在的块作用域中(通常是 { .. } 内部)

try{
  undefined() // 执行一个非法操作
}catch(error){
  console.log(error) // 正常执行
}
console.log(error) // ReferenceError

var foo = true
if(foo){
  // const同理,同时const创建的是固定的常量,任何修改都会引起错误
  let bar = !foo  
  console.log(bar) // false
}
console.log(bar) // ReferenceError

提升

引擎会 在解释 JavaScript 代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。因此,包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。

console.log(a) // undefined
var a = 2

上面代码等同于下面的流程

var a
console.log( a)
a = 2
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343