runtime-JavaScript

别说话,运(wen)行(wo)

wwWorld
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var ILoveU = 'are you kidding?';

function helloWorld(argc, argv){
"use strict";

var BBC = "2B";

function worldCup2018 () {
var no10 = "Messi";
return BBC;
}

worldCup2018();

return null;
}

helloWorld(1, "holy shit!");

人狠话不多,直接放狗,噢……放图。想想之前自己的图,也有很多东西不能确定是否准确,但愿你能找到其中正确的,抛弃错误的。欢迎猛喷,只要你更有理,更有证据、实例。

  1. 这张图,我想做一些简化。

但是,这里有些名字,你是访问不到的,是由JS引擎来使用的。比如浏览器中,全局环境,this===window,但是没有Global(我之前用的Chrome调试器,截图出来,有这个东西,人家调试器就是能访问更多的信息;我就是不行,我就只能调用调用函数、API,抄抄代码)。node中,全局环境,this===global,也没有Global。比如全局环境中JS引擎也维护了一个Scope(或者叫其他名字),你是别指望拿到了。定义的函数的[[Scopes]],除非JS引擎暴露出来,你也别想访问。这个图,我也不知道怎么画才好。比如这篇文章变量对象,就认为:

全局上下文变量对象GlobalContextVO,(VO === this === global)

虽然它讲的很多东西都是基于ECMA-262-3的,但是直到现在很多基础的东西还是没变,有的是概念名字变了,猫叫个咪,有的是语法糖,有的的确是发展出了新的东西。同样,全局环境中,Object.getOwnPropertyDescriptor(this, 'this') === undefined,根本没有this这个属性;this是keywords,我这图里也不知道把它放哪里!我太心虚了,都不知道在解释什么了。

我想了想觉得还是这张图好点。

  1. 啊,不要停……不要停,继续运行,越过首行,一眨眼,就18岁了,不,就到18行了。停!!!

这是越过首行受到的惩罚。

  1. 尝试越过18行,咦,我擦,怎么跑到第6行咧?

注意,现在我们到了helloWorld-ECthis: undefined,如果18行window.helloWorld(1, "holy shit!");this: window,你可以用其他对象也无所谓。Scope是个数组形式,第一个元素是Local,第二个元素是Global。(不知道怎么画数组)。Local中有参数值,变量定义,函数定义,还有arguments(这个对象是一个数组形式,包含实际传入的参数值,参数个数……图中没有画清楚,可以说是错误的。arguments[0]和argc之间又微妙的关系。应该画一个图形来表示这个对象,而且它的原型是Object.prototype)。Scope是由当前环境Local和产生当前环境的函数helloWorld的[[Scopes]](其中恰好含有一个元素Global)构成。

我又把这图改进了一下,arguments的__proto__,函数对象的__proto__,prototype也都没有画出来。
假设我们从helloWorld-EC返回到了global-EC,如果返回值是worldCup2018这个函数,并且我们用一个变量接受此值,(返回其他对象也可以,不过如果你返回arguments,接受它,你就arguments那么大块内存;返回函数的话,它的[[Scopes]]大的超乎你的想象!嘿嘿)那么可以看到,虽然我们从helloWorld-EC返回到了global-EC,虽然helloWorld-EC走了,但是worldCup2018依然活着,它的[[Scopes]]所引用的对象当然也在(这涉及到GarbageColletion,我也不懂),也就是这块内存还在用,也就是可以访问。

  1. 既然来了,那就运行第6行

哎,真详(啰)细(嗦)。

  1. 终于等到你……13多么伟大的数字啊!运行它。又飞到了第9行

注意,现在我们到了worldCup2018-ECthis: undefinedScope是个数组形式。

  • 本地环境Local,我画出了no10, arguments
  • 另一部分是函数worldCup2018的[[Scopes]],其中有两个元素。一个是 helloWorld-ECLocal环境,一个就是Global
  • 可以看到Scope中画出去了三条线,我这里的画法,没能体现它们之间的顺序关系,真是抱歉。

到此为止,往下画也没有意义了。

至于出栈的时候,一般就是返回到上一层环境合适的位置,比如此被调用函数的下一条语句。(调用函数时,要记得返回的位置。当然这个不是我要考虑的事情。)退到上一层环境时候,内环境中的对象怎么办?如果被引用,就不释放。这些对象,都是在堆上分配的;Call Stack也应该是在堆上模拟的。像C语言中的函数调用,用栈传参数值,记录返回地址……小弟只能说,干脆直接简洁。因此也没有JS函数很多特性(不能嵌套定义函数啊,啊,啊,啊……)。但是,学好数据结构,照着ECMA-262,就可以用C实现JS引擎(这也是小弟努力的目标之一)。就扯到这里吧,太阳都快下山了。下次我们粗解一下立即执行函数。每当调用一个函数,就想想,它的[[Scopes]]是什么,prototype是什么之类的。凡事都有例外,比如new Function(...)产生的函数对象,它的[[Scopes]]只含Global,和产生时所处的环境无关;with语句也会在环境中产生对象,那么此对象的原型是什么?暂时此对象放在哪里?;eval也是很奇特。等有空了,我去网上把答案研究(抄)过来。突然想到克罗地亚没夺冠,不好受。