本篇讲解第四章讲到的关于 表达式 的定义、复杂表达式的计算过程。express
function Class() {
this.a = 1;
return {a: 2};
}
new Class();
复制代码
var a = {i: 1};
var b = a;
a.j = a = {k: 2};
复制代码
犀牛书中对 表达式 的定义为 能够计算出结果的短语,简单来讲就是 有返回值的代码。数组
MDN 上对表达式的定义是:bash
An expression is any valid unit of code that resolves to a value.函数
表达式是一组代码的集合,它返回一个值。post
按照这些定义理解,只要有返回值的代码均可以认为是表达式。学习
然而不肯定理解是否有误,毕竟就好像函数声明,也有返回值,为啥就和函数表达式区分开,查了好久也没找到确切的定义。猜想定义应该是有操做符参与计算的才算表达式,毕竟函数表达式和声明写法上差的就是赋值,并且表达式和运算符通常都是一块儿出现的。ui
看下常见的表达式类型this
原始表达式是表达式的 最小单位,常量、直接量、关键字、变量spa
null
undefined
true
false
this
someVar
1.2
'string'
复制代码
犀牛书这里有注明,null 是关键字,可是 undefined 是全局变量。确实是这样,能够试试给 null 和 undefined 赋值,但为啥这样设计呢,不懂。 prototype
[]
[1, 2, 1+3]
{a: 1, b: 2}
复制代码
var func = function(x) {
return x * x;
}
复制代码
a.x
a[x]
复制代码
这里犀牛书有说了很重要的一点:无论使用哪一种形式的属性访问表达式,在 . 和 [ 以前的表达式老是会首先计算。本文最下方有关于这个的验证。
fun(x)
Math.max(1, 2)
复制代码
调用表达式前的表达式是一个属性访问表达式时这个调用叫作 方法调用,方法调用会将方法中的 this 指向调用的对象。
非方法调用表达式非严格模式会使用全局对象做为 this 关键字的值,严格模式下为 undefined。
new Object()
new String
复制代码
对象建立表达式的建立步骤:
1 + 2
's' + 'a'
1 - null
++a
12 | 1
12 ^ 4
~12
12 << 1
12 >> 1
-12 >>> 1
复制代码
null == undefined
a = 1
a === b
a != 1
a !== 1
'a' < 'b'
'a' > 'b'
'a' <= 'b'
'a' >= 'b'
'x' in y
a instanceof Date
复制代码
x == 0 && y == 0
x == 0 || y == 0
!x
复制代码
x = 1
a.b = 1
a++
b--
--a
++b
a += b
b *= a
复制代码
eval('1 + 2')
复制代码
a ? 1 : 2
typeof true
delete a.b
void a++
a++, b++
复制代码
将简单表达式链接在一块儿就能够构成复杂表达式。
var a = b = c + 1 / (1 + 2) ? 1 : 2 * 100 - typeof '123'
复制代码
这里结合上一篇的运算符的相关知识对一些复杂表达式进行解析,看看复杂表达式究竟是如何计算的。主要关系运算符的优先级、结合性、左值等概念,不太了解的能够看下上一篇。
先来个网红题目简单解析一下
var a = {i: 1};
var b = a;
a.j = a = {k: 2};
复制代码
为何要先取出 a.j 的内存地址:
验证一下第一个说法是否正确:
var b = {i: 1};
var a = {i: 1, a: b};
a.a = 2, a.a.i = 3;
复制代码
按照犀牛书的说法 a.a.i 中的 a.a 会优先计算为 b,而后 b 的 i 被赋值为 3,不过经验证这个说法并不正确。 因此其实并无这个额外的规则,只是单纯的运算符优先级和左值的问题。
再来个复杂一点的例子,看下表达式计算时如何推断优先级。
var k = {
get a() {
console.log('a');
return 'a';
},
get b() {
console.log('b');
return 'b';
},
get c() {
console.log('c');
return 'c';
}
};
var b = { b: 1 };
var a = { a: 1, b: b, c: 2 };
var c = { c: 1 };
var v = 2;
a[k.a] = b[k.b] = (v * 10 + c[k.c] / 10 + v++ - ++v) | 1;
// a
// b
// c
// { a: 19, b: { b: 19 }, c: 2 } { b: 19 } 4
console.log(a, b, v);
复制代码
按照运算符的 优先级从低到高 分解成最基础的表达式和运算符,按照 结合性结合相同优先级 的运算符,而后按照 从左往右 的顺序计算子表达式,深度优先 计算表达式。
因此顺序是
a[k.a]
b[k.b]
v * 10
c[k.c]
(c[k.c]) / 10
v++
++v
(v * 10) + ((c[k.c]) / 10) + (v++) - (++v)
((v * 10) + ((c[k.c]) / 10) + (v++) - (++v)) | 1
b[k.b] = (((v * 10) + ((c[k.c]) / 10) + (v++) - (++v)) | 1)
a[k.a] = (b[k.b] = (((v * 10) + ((c[k.c]) / 10) + (v++) - (++v)) | 1))
复制代码
按照上述过程将复杂表达式拆解后,计算过程就一目了然了。