JS-ES6(1)

看了一些es6的相关,作下笔记总结.

当我们看到一串JavaScript 代码,它是按照顺序执行的吗?

执行上下文

只有理解了执行上线文,才能更好地理解 JavaScript 语言本身,比如变量提升、作用域和闭包等。

从 JavaScript 代码的顺序执行讲起,一步步了解 JavaScript 是怎么运行的。

showName();console.log(myName);
var myName = 'tokgogogo';
function showName() {    console.log('函数 showName 被执行');}

JavaScript 是按照顺序执行的。如果按照这个逻辑来理解:

  • 当执行到第一行的时候,由于函数 showName 未定义,所以执行应该报错;

  • 同样执行第二行的时候,由于变量 myName 未定义,所以同样也会报错。

image

然而实际的结果,先输出了“函数 showName 被执行”,又输出了 undefined 。

根据上面的结果,函数或者变量可以在定义之前使用。那么,如果使用没有定义的变量或者函数,JavaScript 代码还能继续执行吗?

showName();console.log(myName);
function showName() {    console.log('函数 showName 被执行');}

再次执行这段代码,JavaScript 引擎会报错:myName is not defined 。

由上述两个执行结果来看,可以得到如下三个结论:

  1. 在执行过程中,若使用了未声明的变量,那么 JavaScript 执行会报错。

  2. 在一个变量定义之前使用,不会出错,但是该变量的值会为 undefined ,而不是定义的值。

  3. 在一个函数定义之前使用,不会出错,且函数能正确执行。

变量提升

var myname = 'tokgogogo';

这段代码可以看成两行代码组成:

var myname; // 声明部分
myname = 'tokgogogo'; // 赋值部分

上面是变量的声明和赋值,函数的声明和赋值:

function foo() {    console.log('foo');}
var bar = function() {    console.log('bar');}

第一个函数 foo 是一个完整的函数声明,也就是说没有涉及到赋值操作;

第二个函数是先声明变量 bar ,再把函数体赋值给 bar :

image

所谓的变量提升,是指在 JavaScript 代码执行过程中, JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是 undefined 。

/** * 变量提升部分 */
// 把变量 myName 提升到开头
// 同时给 myName 赋值 undefinedvar 
myName = undefined;// 把函数 showName 提升到开头
function showName() {  console.log('函数 showName 被执行');}
/** * 可执行部分 */
showName();console.log(myName);
// myName 赋值语句myName = 'tokgogogo';

对原来的代码主要做了两处调整:

  • 第一处是把声明的部分都提升到了代码开头,如变量 myName 和函数 showName ,并给变量设置默认值 undefined ;

  • 第二处是移除原本声明的变量和函数,如 var myName = 'tokgogogo' 的语句,移除了 var 声明,整个移除 showName 的函数声明。

通过这两步,就可以实现变量提升的效果。因此,这就是为什么可以在定义之前使用变量或者函数的原因——函数和变量在执行之前都提升到了代码开头

JS 代码的执行流程

实际上,变量和函数在声明在代码里的位置是不会变的,而是在编译阶段被 JavaScript 引擎放入内存中

<u data-slate-mark="true">一段 JavaScript 代码在执行之前需要被 JavaScript 引擎编译,</u><u data-slate-mark="true">编译</u><u data-slate-mark="true">完成后,才会进入</u><u data-slate-mark="true">执行</u><u data-slate-mark="true">阶段。</u>

image

编译阶段

编译阶段和变量提升的关系。

变量提升部分的代码

var myName = undefined;
function showName() {
  console.log('函数 showName 被执行');
}

执行部分的代码

showName();
console.log(myName);
myName = 'tokgogogo';

输入一段代码,经过编译后,会生成两部分内容:执行上下文(Execution context)可执行代码

执行上下文是 JavaScript 执行一段代码时的运行环境,比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间用到的诸如 this、变量、对象以及函数等。

在执行上下文中存在一个变量环境的对象(Viriable Environment),该对象中保存了变量提升的内容,比如上面代码中的 myName 和函数 showName,都保存在该对象中。

该变量环境对象如下结构:

ViriableEnvironment:    
myName -> undefined,  
showName -> function : { console.log(myName) }
showName();
console.log(myName);
var myName = 'tokgogogo';
function showName() {    
console.log('函数 showName 被执行');
}

对于这段代码:

  • 第一行和第二行,由于这两行代码不是声明操作,所以 JavaScript 引擎不会做任何处理;

  • 第三行,由于这行是经过 var 声明的,因此 JavaScript 引擎将在环境对象中创建一个名为 myName 的属性,并使用 undefined 对其初始化;

  • 第四行,JavaScript 引擎发现了一个通过 function 定义的函数,所以它将函数定义存储到堆(HEAP)中,并在环境对象中创建一个 showName 的属性,然后将该属性值指向堆中函数的位置。

这样就生成了变量环境对象。接下来 JavaScript 引擎会把声明以外的代码编译为字节码。

执行阶段

JavaScript 引擎开始执行“可执行代码”,按照顺序一行一行的执行:

  • 当执行到 showName 函数时,JavaScript 引擎变开始在变量环境对象中查找该函数,由于变量环境对象中存在该函数的引用,所以 JavaScript 引擎便开始执行该函数,并输入“函数 showName 被执行”结果。

  • 接下来打印“myName”信息,JavaScript 引擎继续在变量环境对象中查找该对象,由于变量环境存在 myName 变量,值为 undefined ,所以这时候就输出 undefined 。

  • 接下来执行第三行,把“tokgogogo”赋值给 myName 变量,赋值后变量环境中的 myName 属性值改变为“tokgogogo”,变量环境如下:

ViriableEnvironment:   
myName -> "tokgogogo",  
showName -> function : { console.log(myName) }

代码中出现相同的变量或者函数怎么办

function showName() {
  console.log('showName 1');
}
showName();
function showName() {
  console.log('showName 2');
}
showName();

在上面的代码中,先定义了一个 showName 函数,打印 1 ;然后调用 showName ,并定义了一个 showName 函数,打印 2 ;最后接着调用 showName 。

其完整执行流程:

  • 首先是编译阶段。遇到了第一个 showName 函数,会将该函数体存放到变量环境中。接下来是第二个 showName 函数,继续存放至变量环境中,但是变量环境中已经存在一个 showName 函数了。因此,第二个 showName 函数会将第一个 showName 函数覆盖掉。这样变量环境中就只存在第二个 showName 函数了。

  • 接下来是执行阶段。先执行第一个 showName 函数,但由于是从变量环境中查找 showName 函数,而变量环境中只保存了第二个 showName 函数,所以最终调用的是第二个函数,打印的内容是 2 。第二次执行 showName 函数也走同样的流程,所以输出的结果也是 2 。

总结

  • JavaScript 代码执行过程中,需要先做变量提升,而之所以需要实现变量提升,是因为JavaScript 代码在执行之前需要先编译

  • 编译阶段,变量和函数会被存放到变量环境中,变量的默认值被设置为 undefined ;在代码执行阶段,JavaScript 引擎从变量环境中去查找自定义的变量和函数。

  • 如果在编译阶段,存在两个相同的函数,那么最终存放在变量环境中的是最后定义的那个,这是因为后定义的会覆盖掉之前定义的。

JavaScript 的执行机制:先编译,再执行

showName(); // 2
var showName = function() {  console.log(1);}
function showName() {  console.log(2);}
showName(); // 1

编译阶段:

var showName = undefined;
function showName() {  console.log(2);}

执行阶段:

showName();
showName = function() {  console.log(1);}
showName();
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容