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();
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容