快速理解 JavaScript 中的 LHS 和 RHS 查询的用法

JavaScript 程序中的一段源代码在执行之前会经历三个步骤,统称为 编译

  • 分词/词法分析
  • 解析/语法分析
  • 代码生成

先看原书对一个赋值操作的拆解说明:
变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。 --- 《你不知道的JavaScript(上卷)》 P7

而要讲的 LHS 和 RHS 就是上面说的对变量的两种查找操作,查找的过程是由作用域(词法作用域)进行协助,在编译的第二步中执行。

LHS 和 RHS

  • 字面意思其实是 Left Hand Side Right Hand Side 即左手边和右手边
  • 一般可以理解为 赋值操作的左侧和右侧

先看个例子一

console.log(a);

这里对 a 是一个 RHS 引用,因为 a 并没有赋予任何值,目的是为了取到 a 的值并打印出来。

a = 2;

这里对 a 是一个 LHS 引用,因为实际上我们不关心当前的值是什么,只是想要把 a 赋值为 2。

再看例子二

function foo(a) {
  console.log(a); // 2
}
foo(2);
  • 最后一行foo(..)函数的调用需要对 foo 进行 RHS 引用,意味着是取到 foo 的值,并且 (..)意味着 foo 需要被执行,因此它最好是一个函数类型的值
  • 其中有一个容易被忽略的隐式赋值操作 a = 2,它发生在 2 被当做实参传入 foo 中时。即实参 2 传给了形参 a,需要一个 LHS 查询
  • console 那一行还有对 a 的一次 RHS 引用(或者叫查询),同时console.log(..)本身也需要一个 RHS 引用,即对 console 对象进行 RHS 查询,并且检查得到的值中是否有一个叫做 log 的方法。
  • 书中有一段引擎和作用域的对话,有助于很好的理解例子二,see YDKJS github

小测验

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

试试找出其中的3处 LHS 查询,4处 RHS 查询

答案:

LHS 查询:

c = ..;
a = 2(隐式变量分配)
b = ..

RHS 查询

foo(2..
= a;
a ..
.. b

小结

参考原书中文版P12,英文版见 github

  • 作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。
  • 如果查找的目的是对变量进行赋值,就会使用 LHS 查询;如果目的是获取变量的值,就会用 RHS 查询。
  • 赋值操作会导致 LHS 查询。 = 操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作, 即都会导致 LHS 查询。
  • JavaScript 引擎首先会在代码执行前对其进行编译,在这个过程中,像var a = 2 这样的声明会被分解成两个独立的步骤:
  • 首先,var a在其作用域中声明新变量。这会在最开始的阶段,也就是代码执行前进行。
  • 接下来,a = 2会查询(LHS查询)变量 a 并对其进行赋值。
  • LHS 和 RHS 查询都会在当前执行作用域中开始,如果有需要(也就是说它们没有找到所需的标识符),就会向上级作用域继续查找目标标识符,这样每次上升一级作用域,最后到达全局作用域,无论找到或没找到都将停止。
  • 不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式地创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛出 ReferenceError 异常(严格模式下)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 快速理解 JavaScript 中的 LHS 和 RHS 查询的用法

    JavaScript 程序中的一段源代码在执行之前会经历三个步骤,统称为 编译 分词/词法分析 解析/语法分析 代码生成 先看原书对一个赋值操作的拆解说明: 变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值. --- <你不知道的JavaScript(上卷)> P7 而要讲的 LHS 和 RHS 就是上面说的对变量的两种查找操作,查找的过程是由作用域(词法作用域)进行协助,在编译的第二步

  • JavaScript中的LHS和RHS分析详情

    目录 进一步理解 编译特点 分析 区分 LHS 和 RHS 的重要性 前言: 对于LHS和RHS,从字面意思来说是​​ Left Hand Side​​和​​ Right Hand Side​​即左手边和右手边,一般可以理解为赋值操作的左侧和右侧,然而不能片面的用​​=​​号左边还说右边去判断是​​LHS​​还是​​RHS​​通俗的理解,​ ​LHS​​是赋值操作即可以看做是在往内存中存储值,而​​RHS​​是取值操作,它是从内存中进行检索.事实上赋值操作还有其他几种形式,因此在概念上最好将其理

  • 带你快速理解javascript中的事件模型

    javascript中有两种事件模型:DOM0,DOM2.而对于这两种的时间模型,我一直不是非常的清楚,现在通过网上查阅资料终于明白了一些. 一.  DOM0级事件模型 DOM0级事件模型是早期的事件模型,所有的浏览器都是支持的,而且其实现也是比较简单.代码如下: <p id = 'click'>click me</p> <script> document.getElementById('click').onclick = function(event){ alert(

  • 深入理解JavaScript中的对象复制(Object Clone)

    JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b.k2 = 4; 如果只想改变b而保持a不变,就需要对对象a进行复制. 用jQuery进行对象复制 在可以使用jQuery的情况下,jQuery自带的extend方法可以用来实现对象的复制. a = {k1:1, k2:2, k3:3}; b = {}; $.extend(b,a); 自定义clone

  • 全面理解JavaScript中的继承(必看)

    JavaScript中我们可以借助原型实现继承. 例如 function baz(){ this.oo=""; } function foo(){ } foo.prototype=new baz(); var myFoo=new foo(); myFoo.oo; 这样我们就可以访问到baz里的属性oo啦.在实际使用中这个样不行滴,由于原型的共享特点(数据保存在了堆上), 所有实例都使用一个原型,一但baz的属性有引用类型就悲剧了,一个实例修改了其他实例也都跟着变了...wuwuwu 自

  • 理解JavaScript中worker事件api

    如果你不是很了解Event事件,建议先这篇文章<理解javascript中DOM事件>. 首先,我们需要实例一个Worker的对象,浏览器会根据新创建的worker对象新开一个接口,此接口会处理客户端与indexedDB数据库之间的通信.这里的数据库是指浏览器数据库.如果,你需要判断浏览器是否支持worker对象,详见如下代码.或者浏览器是否支持indexedDB数据库,详见同下,二者判断最好选择前者.因为IE不支持indexedDB . if(window.Worker){ dosometh

  • 深入理解JavaScript中的浮点数

    js只有一种数值型数据类型,不管是整数还是浮点数,js都把归为数字. typeof 17;   // "number" typeof 98.6; // "number" typeof –2.1; // "number" js中的所有数字都是双精度浮点数.是由IEEE754标准制定的64位编码数字(这个是什么东东,不知道,回头查一下吧) 那么js是如何表达整数的,双精度浮点数可以完美地表示高达53位精度的整数(没有什么概念,没处理过多大的数据,没用

  • AJAX入门之深入理解JavaScript中的函数

    概述 函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解.JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的.通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递.在继续讲述之前,先看一下函数的使用语法: function func1(-){-}var func2=function(-){-};var func3=function func4(-){-};var func5=new Function()

  • 理解 javascript 中的函数表达式与函数声明

    常用闭包的同学肯定很清楚下面一段代码: //通常的闭包写法 (function () { ... }()) 那么我们的问题来了,为什么要在 function () {...}() 之外用圆括号包裹呢?解答这个问题,就需要我们理解 Javascript 中函数表达式与函数声明的概念. 函数定义带来的错误 虽然 function () {...} 看上去像是一个函数声明,但是由于没有函数名,它的本质其实是一个函数表达式.我们看下规范中对于函数声明与函数表达式的定义: 可以看出来,函数声明是必须带有函

  • 深入理解JavaScript中为什么string可以拥有方法

    引子 我们都知道,JavaScript数据类型分两大类,基本类型(或者称原始类型)和引用类型. 基本类型的值是保存在栈内存中的简单数据段,它们是按值访问的.JS中有五种基本类型:Undefined.Null.Boolean.Number和String. 引用类型的值是保存在堆内存中的对象,它的值是按引用访问的.引用类型主要有Object.Array.Function.RegExp.Date. 对象是拥有属性和方法的,所以我们看到下面这段代码一点也不奇怪. var favs=['鸡蛋','莲蓬']

随机推荐