js作用域及作用域链工作引擎


Posted in Javascript onJuly 07, 2022

前言

我们需要先知道的是引擎,引擎的工作简单粗暴,就是负责javascript从头到尾代码的执行。引擎的一个好朋友是编译器,主要负责代码的分析和编译等;引擎的另一个好朋友就是今天的主角--作用域。那么作用域用来干什么呢?作用域链跟作用域又有什么关系呢?

一、作用域(scope)

作用域的定义:作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。

1、作用域的分类

  • 全局作用域
var name="global";
function foo(){
    console.log(name);
}
foo();//global

这里函数foo()内部并没有声明name变量,但是依然打印了name的值,说明函数内部可以访问到全局作用域,读取name变量。再来一个例子:

hobby='music';
function foo(){
    hobby='book';
    console.log(hobby);
}
foo();//book

这里全局作用域和函数foo()内部都没有声明hobby这个变量,为什么不会报错呢?这是因为hobby='music';写在了全局作用域,就算没有var,let,const的声明,也会被挂在window对象上,所以函数foo()不仅可以读取,还可以修改值。也就是说hobby='music';等价于window.hobby='music';

  • 函数体作用域

函数体的作用域是通过隐藏内部实现的。换句话说,就是我们常说的,内层作用域可以访问外层作用域,但是外层作用域不能访问内层。原因,说到作用域链的时候就迎刃而解了。

function foo(){
    var age=19;
    console.log(age);
}
console.log(age);//ReferenceError:age is not defined

很明显,全局作用域下并没有age变量,但是函数foo()内部有,但是外部访问不到,自然而然就会报错了,而函数foo()没有调用,也就不会执行。

  • 块级作用域

块级作用域更是见怪不怪,像我们接触的let作用域,代码块{},for循环用let时的作用域,if,while,switch等等。然而,更深刻理解块级作用域的前提是,我们需要先认识认识这几个名词:

--标识符:能在作用域生效的变量。函数的参数,变量,函数名。需要格外注意的是:函数体内部的标识符外部访问不到

--函数声明:function 函数名(){}

--函数表达式: var 函数名=function(){}

--自执行函数: (function 函数名(){})();自执行函数前面的语句必须有分号,通常用于隐藏作用域。

接下来我们就用一个例子,一口气展示完吧

function foo(sex){
    console.log(sex);
}
var f=function(){
    console.log('hello');
}
var height=180;
(
    function fn(){
        console.log(height);
    }
)();
foo('female');
//依次打印:
//180
//female
//hello

分析一下:标识符:foo,sex,height,fn;函数声明:function foo(sex){};函数表达式:var f=function(){};自执行函数:(function fn(){})();需要注意,自执行函数fn()前面的var height=180;语句,分号不能抛弃。否则,你可以试一下。

二、预编译

说好只是作用域和作用域链的,但是考虑到理解作用域链的必要性,这里还是先聊聊预编译吧。先讨论预编译在不同环境发生的情况下,是如何进行预编译的。

  • 发生在代码执行之前

(1)声明提升

console.log(b);
var b=123;//undefined

这里打印undefined,这不是报错,与Refference:b is not defined不同。这是代码执行之前,预编译的结果,等同于以下代码:

var b;//声明提升
console.log(b);//undefined
b=123;

(2)函数声明整体提升

test();//hello123  调用函数前并没有声明,但是任然打印,是因为函数声明整体提升了
function test(){
    var a=123;
    console.log('hello'+a);
}

2.发生在函数执行之前

理解这个只需要掌握四部曲

(1)创建一个AO(Activation Object)

(2)找形参和变量声明,然后将形参和变量声明作为AO的属性名,属性值为undefined

(3)将实参和形参统一

(4)在函数体内找函数声明,将函数名作为AO对象的属性名,属性值予函数体 那么接下来就放大招了:

var global='window';
function foo(name,sex){
    console.log(name);
    function name(){};
    console.log(name);
    var nums=123;
    function nums(){};
    console.log(nums);
    var fn=function(){};
    console.log(fn);
}
foo('html');

这里的结果是什么呢?分析如下:

//从上到下
//1、创建一个AO(Activation Object)
AO:{
    //2、找形参和变量声明,然后将形参和变量声明作为AO的属性名,属性值为undefined
    name:undefined,
    sex:undefined,
    nums=undefined,
    fn:undefined,
    //3、将实参和形参统一
    name:html,
    sex:undefined,
    nums=123,
    fn:function(){},
    //4、在函数体内找函数声明,将函数名作为AO对象的属性名,属性值予函数体
    name:function(){},
    sex:undefined,
    fn:function(){},
    nums:123//这里不仅存在nums变量声明,也存在nums函数声明,但是取前者的值
    以上步骤得到的值,会按照后面步骤得到的值覆盖前面步骤得到的值
}
//依次打印
//[Function: name]
//[Function: name]
//123
//[Function: fn]

3.发生在全局(内层作用域可以访问外层作用域)

同发生在函数执行前一样,发生在全局的预编译也有自己的三部曲:

(1)创建GO(Global Object)对象

(2)找全局变量声明,将变量声明作为GO的属性名,属性值为undefined

(3)在全局找函数声明,将函数名作为GO对象的属性名,属性值赋予函数体

举个栗子:

var global='window';
function foo(a){
    console.log(a);
    console.log(global);
    var b;
}
var fn=function(){};
console.log(fn);
foo(123);
console.log(b);

这个例子比较简单,一样的步骤和思路,就不在赘述分析了,相信你已经会了。打印结果依次是:

[Function: fn]
123
window
ReferenceError: b is not defined

好啦,进入正轨,我们接着说作用域链。

三、作用域链

作用域链就可以帮我们找到,为什么内层可以访问到外层,而外层访问不到内层?但是同样的,在认识作用域链之前,我们需要见识见识一些更加晦涩抽象的名词。

执行期上下文:当函数执行的时候,会创建一个称为执行期上下文的对象(AO对象),一个执行期上下文定义了一个函数执行时的环境。 函数每次执行时,对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行期上下文,当函数执行完毕,它所产生的执行期上下文会被销毁。

查找变量:从作用域链的顶端依次往下查找。

 [[scope]]:作用域属性,也称为隐式属性,仅支持引擎自己访问。函数作用域,是不可访问的,其中存储了运行期上下文的结合。

我们先看一眼函数的自带属性:

function test(){//函数被创建的那一刻,就携带name,prototype属性
      console.log(123);
}
console.log(test.name);//test
console.log(test.prototype);//{} 原型
// console.log(test[[scope]]);访问不到,作用域属性,也称为隐式属性
// test() --->AO:{}执行完毕会回收
// test() --->AO:{}执行完毕会回收

接下来看看作用域链怎么实现的:

var global='window';
function foo(){
    function fn(){
        var fn=222;
    }
    var foo=111;
    console.log(foo);
}
foo();

分析:

GO:{
    foo:function(){}
}
fooAO:{
    foo:111,
    fn:function(){}
}
fnAO:{
    fn:222
}
// foo定义时 foo.[[scope]]---->0:GO{}
// foo执行时 foo.[[scope]]---->0:AO{}  1:GO{}  后访问的在前面
//fn定义时 fn.[[scope]]---->0:fnAO{} 1:fooAO{}  2:GO{}
fnAO:fn的AO对象;fooAO:foo的AO对象

js作用域及作用域链工作引擎

综上而言:作用域链就是[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。

以上就是js作用域及作用域链工作引擎的详细内容,更多关于js作用域作用域链的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
js常用代码段收集
Oct 28 Javascript
使用js操作cookie的一点小收获分享
Sep 03 Javascript
用js代码改变单选框选中状态的简单实例
Dec 18 Javascript
checkbox勾选判断代码分析
Jun 11 Javascript
JavaScript选取(picking)和反选(rejecting)对象的属性方法
Aug 16 Javascript
微信小程序slider组件使用详解
Jan 31 Javascript
jQuery实现动态加载select下拉列表项功能示例
May 31 jQuery
通过npm或yarn自动生成vue组件的方法示例
Feb 12 Javascript
微信小程序性能优化之checkSession的使用
Mar 06 Javascript
JavaScript实现好看的跟随彩色气泡效果
Feb 06 Javascript
如何利用vue实现波谱拟合详解
Nov 05 Javascript
javascript中导出与导入实现模块化管理教程
Dec 03 Javascript
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
JS实现简单九宫格抽奖
Jun 28 #Javascript
You might like
PHP Parse Error: syntax error, unexpected $end 错误的解决办法
2012/06/05 PHP
PHP向浏览器输出内容的4个函数总结
2014/11/17 PHP
javascript面向对象之二 命名空间
2011/02/08 Javascript
Javascript中的isNaN函数使用说明
2011/11/10 Javascript
JavaScript高级程序设计 阅读笔记(十二) js内置对象Math
2012/08/14 Javascript
javascript轻松实现当鼠标移开时已弹出子菜单自动消失
2013/12/29 Javascript
js实现对table的增加行和删除行的操作方法
2016/10/13 Javascript
微信小程序 摇一摇抽奖简单实例实现代码
2017/01/09 Javascript
prototype.js简单实现ajax功能示例
2017/10/18 Javascript
开发Vue树形组件的示例代码
2017/12/21 Javascript
egg.js的基本使用和调用数据库的方法示例
2019/05/18 Javascript
详解express使用vue-router的history踩坑
2019/06/05 Javascript
微信小程序前端自定义分享的实现方法
2019/06/13 Javascript
简单了解TypeScript中如何继承 Error 类
2019/06/21 Javascript
JS实现导航栏楼层特效
2020/01/01 Javascript
file-loader打包图片文件时路径错误输出为[object-module]的解决方法
2020/01/03 Javascript
js实现筛选功能
2020/11/24 Javascript
NestJs使用Mongoose对MongoDB操作的方法
2021/02/22 Javascript
[54:41]2018DOTA2亚洲邀请赛3月30日 小组赛B组 VGJ.T VS paiN
2018/03/31 DOTA
[01:03:37]Secret vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
wxpython 学习笔记 第一天
2009/03/16 Python
详解Python的单元测试
2015/04/28 Python
使用Python编写一个最基础的代码解释器的要点解析
2016/07/12 Python
详解Python装饰器由浅入深
2016/12/09 Python
基于DataFrame改变列类型的方法
2018/07/25 Python
Python解析Excle文件中的数据方法
2018/10/23 Python
python根据文章标题内容自动生成摘要的实例
2019/02/21 Python
Python实现计算文件MD5和SHA1的方法示例
2019/06/11 Python
什么是python的必选参数
2020/06/21 Python
联想加拿大官方网站:Lenovo Canada
2018/04/05 全球购物
婚前财产公证书
2014/04/10 职场文书
2014年幼儿园个人工作总结
2014/11/10 职场文书
小学生2015教师节演讲稿
2015/03/19 职场文书
社会主义核心价值观主题教育活动总结
2015/05/07 职场文书
中国式结婚:司仪主持词(范文)
2019/07/25 职场文书
详解CSS不定宽溢出文本适配滚动
2021/05/24 HTML / CSS