上篇说了对于 JavaScript,预处理 + 执行 = 解释,解释没那么简单,那么究竟什么是解释?
解释解释
你来解释解释什么是解释?
什么,TMD的是解释?
什么是解释
本来打算有空就写的,结果苟了几天,没有写了,感觉自己废了。
主要是这个解释
,难以解释。
黄四郎:好啊!三天之后,一定给县长一个惊喜!
张麻子:汤师爷,他是胡万的恩人,现在又成了你的恩人!你给翻译翻译,什么叫惊喜?翻译翻译,什么叫惊喜?
汤师爷:这还用翻译?都说了…
张麻子:我让你翻译给我听,什么叫惊喜!
黄四郎:不用翻译,就是惊喜啊!难道你听不懂什么叫惊喜?
张麻子:我就想让你翻译翻译,什么叫惊喜!
汤师爷:惊喜嘛……
张麻子:翻译出来给我听,什么他妈的叫惊喜!什么他妈的叫他妈的惊喜!
汤师爷:什么他妈的叫惊喜啊?
黄四郎:惊喜就是三天之后,我出一百八十万,给你们出城剿匪,接上我的腿!明白了吗?
汤师爷:这就是惊喜呀
张麻子:翻译翻译。翻译翻译!
汤师爷:惊喜就是三天之后,给你一百八十万两银子,出城剿匪,接上他的腿!
张麻子:大哥这个是惊喜啊!小弟我愿意等你三天。(拉过汤师爷来)黄老爷,汤师爷是我的至爱,你可不能夺我所爱啊!
黄四郎:了然,了然!
心态炸了,有没有?
解释这玩意怎么解释
解释 = 预处理 + 执行,解释这个东西要怎么解释?
书上没有呀,这时可以拿起另一本书 你不知道的JavaScript(上卷) 来看一看。
预编译过程
来看一个例子:
// eg1
console.log('a', a)
var a = 123
console.log('---')
var b = 123
console.log('b', b)
console.log('---')
console.log('c', c)
/**
* a undefined
* ---
* b 123
* Uncaught ReferenceError: c is not defined at index.js:11
*/
我们知道在 ES6 中可以使用 const 和 let 定义变量,将上边的 var 换成 const 会出现什么的情况?
// eg2
console.log('a', a)
const a = 123
console.log('---')
const b = 123
console.log('b', b)
console.log('---')
console.log('c', c)
/**
* Uncaught ReferenceError: Cannot access 'a' before initialization at index.js:1
*/
直接报错,这是因为 ES6 中的 const 和 let 会形成暂时性死区,这就是另外一个故事了。
回到 var 上面来,令人迷惑的是下面的代码片段:
// eg3
console.log('a', a)
const a = 123
console.log('c', c)
如上面的代码所示,a 和 c 都是在没有被声明时调用了,但 a 得到的是 undefined,而 c 却报错了。
两者的区别在于 a 最后补上了声明,而 c 始终没有声明。a 给人的感觉就是我先用,声明先欠着,等到某天我会还上的。而 c 完全就没有想要补上声明的意思。
看一个例子
再来看一个例子,控制台会输出什么?
// eg4
console.log(a)
function a(a) {
var a = 234
var a = function() {}
a()
}
var a = 123
/**
* ƒ a(a) {
* var a = 234
* var a = function() {}
* a()
* }
* /
有一种刚学会 1+1 就要去做微积分的感觉。
来预编译
再来一题:
// eg5
function fn(a) {
console.log(a) // f a() {}
var a = 123
console.log(a) // 123
function a () {}
console.log(a) // 123
var b = function () {}
console.log(b) // f () {}
function d() {}
}
fn(1)
口说无凭,打个断点调试一下。
打个断点
真刺激。
打个断点
预编译发生在函数执行的前一刻。
预编译四部曲
- 创建 AO 对象 Activation Object 执行期上下文
- 找形参和变量声明,将变量和形参的名挂上,并赋值undefined
- 将形参和实参相统一
- 找函数声明,赋值函数体
下面是例子 eg5 产生 AO 的过程:
1.
AO {
}
2.
AO {
a: undefined,
b: undefined,
}
3.
AO {
a: 1,
b: undefined
}
4.
AO {
a: function a() {},
b: undefined,
d: function d() {}
}
在代码执行时上哪里找a?就是上面的 AO,接下来按顺序就好了。预编译调和各个变量的冲突。在运行过程中,AO 也会动态变化。
走一遍流程:
// eg6
function test(a, b) {
console.log(a)
c = 0
var c
a = 3
b = 2
console.log(b)
function b () {}
function d () {}
console.log(b)
}
test(1)
// 走流程
1. 创建AO对象
AO {
}
2. 找形参和和变量声明并赋值undefined
AO {
a: undefined
b: undefined
c: undefined
}
3. 人剑合一,将形参和实参相统一
AO {
a: 1,
b: undefined
c: undefined
}
4. 找函数体声明赋值函数体,会覆盖第三步
AO {
a: 1
b: function b() {}
c: undefined
d: function d() {}
}
拿着我们的 AO 去和函数对线,打印顺序是122.
我们来提炼一下,在预处理时,赋值函数体在形参和实参统一的后边,变量的赋值是在函数执行时发生的。
练习
ok,来道题练一练,下面的console都会打印什么东西?
// eg7
function test(a, b) {
console.log(a)
console.log(b)
var b = 234
console.log(b)
a = 123
console.log(a)
function a () {}
var a
b = 234
var b = function () {}
console.log(a)
console.log(b)
}
test(1)
不墨迹,直接写AO对象。
AO {
a: function a() {}
b: undefined
}
很简单,接下来拿着 AO 按照顺序执行就好了。
对于全局而言全局生成GO Global Object,再来个复杂的题目。
// eg8
console.log(test)
function test(test) {
console.log(test)
var test = 234
console.log(test)
function test() {}
}
test(1)
var test = 123
直接 AO GO 写起来。
GO {
test: test(test){}
}
AO {
test: test() {}
}
简单。,,这就是解释还没完,有空再写。
网友评论