javascript深入系列一览表之【执行上下文,作用域】

本片文章旨在巩固javascript基础,执行上下文,作用域,原型,this等等。
关于js,其实我们主要也是函数式编程,你看react就将函数式编程运用到极致。所以这里咱们就从聊聊js的执行开始。毕竟javascript需要执行之后才是有意义的:

var foo = function () {
    console.log('foo1');
}
foo();  // foo1
var foo = function () {
    console.log('foo2');
}
foo(); // foo2
function foo() {
    console.log('foo1');
}
foo();  // foo2
function foo() {
    console.log('foo2');
}
foo(); // foo2

大家看这两段代码,之所以会有不同的打印,就是和js的执行顺序有关。你要问js的执行顺序是什么?众所周知, JavaScript 引擎并非一行一行地分析和执行程序,而是一段一段地分析执行当执行一段代码的时候,会进行一个“准备工作”,比如第一个例子中的变量提升,和第二个例子中的函数提升
代码执行就得遇到可执行的代码才能执行?那什么是可执行的代码呢?
当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。

对于每个执行上下文,都有三个重要属性:

变量对象(Variable object,VO)
作用域链(Scope chain)
this

1.变量对象:
变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。

因为不同执行上下文下的变量对象稍有不同,所以我们来聊聊全局上下文下的变量对象和函数上下文下的变量对象。
全局上下文:
全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。
全局上下文中的变量对象即为全局对象。
函数上下文:
在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象,即函数执行的变量对象 = AO对象
活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。

当进入执行上下文时,这时候还没有执行代码,

变量对象会包括:

1.函数的所有形参 (如果是函数上下文)

由名称和对应值组成的一个变量对象的属性被创建 没有实参,属性值设为 undefined 函数声明

2.由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建,如果变量对象已经存在相同名称的属性,则完全替换这个属性 变量声明

3.由名称和对应值(undefined)组成一个变量对象的属性被创建;
如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性

2.作用域链:
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链
说到作用域链,其实得先说是javascript的作用域。众所周知,javascript是词法作用域,函数的作用域在函数定义的时候就决定了。。立即执行函数也可以形成单独的词法作用域。
什么是词法作用域?看下面的代码:

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();//1

//静态作用域:
//执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。

//假设JavaScript采用动态作用域,让我们分析下执行过程:
//执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。

由此可见,javascript采用的便是词法作用域。而作用域这个概念其实就可以看成是一个圈,用图示范:
在这里插入图片描述

由图可见,内层作用域可以访问外层作用域的变量,按照这个走上去,就会形成一条作用域链。
执行过程如下:
1.far函数和bar被创建,保存作用域链到 内部属性[[scope]]。
far.[[scope]] = [
globalContext.VO
];
bar.[[scope]] = [
globalContext.VO
];
2.bar函数执行,创建 bar函数执行上下文,bar函数执行上下文被压入执行上下文栈。
ECStack = [
barContext,
globalContext
];
3.bar函数执行之前,也做了上面说的一系列准备工作,复制函数[[scope]]属性创建作用域链,用 arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明,将活动对象压入 bar作用域链顶端。因为far函数是在全局作用域下声明,所以不作为bar函数的活动对象。
barContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: [AO, [[Scope]]]
}
但是因为bar函数执行时又有far函数执行,所以这时要将far函数入栈。
ECStack = [
farContext,
barContext,
globalContext
];
far函数也会和bar函数做上述准备工作,再执行,再出栈,bar函数出栈,最后全局出栈。
额外附添:
闭包:
其实就是函数内部得作用域中得局部变量时不能被外部获取得,因此会借助我们得作用域链,在函数内部再创建函数执行,来获取函数内部得变量。
由此可见,闭包得作用:

  1.使得函数内部的变量能够安全的被外部获取

  2.使得函数在执行后,内部变量依旧被保存在内存中,不会被自动销毁(使用后要用null销毁,否则会占用内存)

  3.可以将函数内部的变量私有化

立即执行函数:
自我形成一块作用域。不会污染外部得变量或者全局变量,相当于形成块级作用域。

var b = 10
(function a(){
	var b = 20
	console.log(b) // 20
})()
console.log(b) // 10

块级作用域:
在es6之前,都是没有块级作用域得,会以立即执行函数来模仿块级作用域。es6之后就可以使用let,const来实现。
3.this
关于this,其实this,其实很多文章或者书中提到是在调用的时候才确定,但是这样并不是特别的正确,要讲这个,其实还是得从ECMASciript5 规范将起,附上大神得一篇文章,JavaScript深入之从ECMAScript规范解读this
,可以阅读下大神得系列,我这篇文章参考得大多也是这个大神得文章,来进行自己的总结罢了。

以上就是关于对js的执行,作用域等理解,后续还会出关于原型,关于事件循环。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值