本篇講解第四章講到的關於 表達式 的定義、複雜表達式的計算過程。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))
複製代碼
按照上述過程將複雜表達式拆解後,計算過程就一目瞭然了。