JavaScript的左查询和"右查询"

882 阅读2分钟

标准应该称为JavaScript的LHS和RHS,含义是“赋值操作的左侧和右侧”,并不意味着就是“=赋值操作符的左侧和右侧”。例如console.log(a),对a进行的是RHS。

变量的LHS

严格模式下

示例1

<!DOCTYPE html>
<html>
    <head>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <title>变量的左查询</title>
    </head>
    <body>
      属性的查询测试
    </body>
    <script>
      'use strict'
      var x = 'windowx'
      function hello() {
        x = 'hellox'// 修改的是window里面的x属性,需要查询作用域链,最终找到windows下有x变量
        function hi() {
          console.log(x); // 需要查询作用域链,找不到报异常。
        }
        hi()
      }
      hello()
    </script>
</html>

图片.png


示例2

<!DOCTYPE html>
<html>
    <head>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <title>变量的左查询</title>
    </head>
    <body>
      属性的查询测试
    </body>
    <script>
      'use strict'
      function hello() {
        x = 'hellox'; // 从作用域链中查找属性,左查询严格模式下找不到报异常。
      }
      hello()
    </script>
</html>

图片.png


示例3

var x = 'hellox'
// 等价于
var x
x = 'hellox'

图片.png

小结

在严格模式下,LHS沿着作用域链查询变量。如果查询不到将会抛出错误。其中var x = 'hellox'即进行了声明,也进行了左查询。

非严格模式下

示例1

<!DOCTYPE html>
<html>
    <head>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <title>变量的左查询</title>
    </head>
    <body>
      属性的查询测试
    </body>
    <script>
        var x = 'windowx'
        function hello() {
          x = 'hellox'; // hellox 在window下
          var y = 'helloy'; // helloy 在hello下
          function hi() {
            // 非严格模式下左查询查不到,在作用域链最后一个作用域处添加该属性。
            z = 'hiz' // hiz 在window下
          }
          hi()
        }
        hello()
    </script>
</html>

小结

非严格模式下左查询查不到,在作用域链最后一个作用域处添加该属性。

图片.png

变量的RHS

无论在严格模式还是非严格模式下都是沿着作用域链查找,找不到都抛出异常Uncaught ReferenceError

属性的LHS

示例1

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>属性的设置</title>
  </head>
  <body>
    属性的设置
  </body>
  <script>
    // 'use strict' // 这里无论是否注释,输出的都是'调用了set'
    function hello() {
    }
    Object.defineProperty(hello.prototype'x', {
      getfunction () {
        return 'getx';
      },
      setfunction (value) {
        console.log('调用了set');
      }
    });
    var hi = new hello();
    /**
     * 下面代码是属性的设置
     */
    hi.x = 'hix'// 调用了set 无论是否使用严格模式,在原型链上存在该属性,都将会调用setter。
  </script>
</html>

示例2

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>属性的设置</title>
  </head>
  <body>
    属性的设置
  </body>
  <script>
    'use strict' // 这段代码目前运行在严格模式下,控制台报错。如果注释掉'use strict',控制台不会报错,js引擎自动忽略 hi.y = 'hiy'
    function hello() {
    }
    Object.defineProperty(hello.prototype'x', {
      value'x',
      writabletrue,
    });
    Object.defineProperty(hello.prototype'y', {
        value'y',
        writablefalse
      });
    var hi = new hello();
    /**
     * 属性的设置,向上查询原型链
     */
    hi.x = 'hix'// 如果是可写的,那么赋值过程无任何异样
    hi.y = 'hiy'// Cannot assign to read only property 'y' of object '#<hello>' 如果该段代码运行在非严格模式下将会被忽略
  </script>
</html>

小结

严格模式下当前作用域链不存在该属性,原型链上的同名属性且该属性writable:false,则为objct.x赋值,会抛出错误。

非严格模式下当前作用域链不存在该属性,原型链上的同名属性且该属性writable:false,则为objct.x赋值,不会抛出错误,js引擎自动忽略该赋值操作。

无论在严格模式或者非严格模式下,当前作用域不存在该属性,原型链上存在同名属性,调用同名属性的setter。

属性的RHS

无论在严格模式还是非严格模式下都是沿着原型链查找,找不到都抛出异常。

总结

变量的查询
严格模式非严格模式
声明 var x;在当前作用域中添加该属性同严格模式
LHS x = value作用域链中查找:找不到,报异常找不到,在查找作用域链的顶层作用域添加该属性
RHS x作用域链中查找:找不到报异常同严格模式
属性的查询
严格模式非严格模式
LHSobjct.x = valueobject是使用变量LHS查询x是属性查找后面如果仍然有’.’仍然是按照属性去找当前作用域不存在该属性,原型链上的同名属性writable:false,则为objct.x赋值,将抛出错误。当前作用域不存在该属性,原型链上的同名属性writable:false。Js引擎忽略这条赋值语句。
当前作用域不存在该属性,原型链上存在同名属性,调用同名属性的setter同严格模式
RHSobject.xobject是变量RHSx是属性查找后面如果仍然有’.’仍然是按照属性去找原型链查找,找不到,报异常同严格模式