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 相关文章推荐
动态加载外部javascript文件的函数代码分享
Jul 28 Javascript
JQuery-tableDnD 拖拽的基本使用介绍
Jul 04 Javascript
顶部缓冲下拉菜单导航特效的JS代码
Aug 27 Javascript
jQuery中通过ajax的get()函数读取页面的方法
Feb 29 Javascript
JavaScript中闭包的写法和作用详解
Jun 29 Javascript
使用jquery给指定的table动态添加一行、删除一行
Oct 13 Javascript
微信小程序 两种滑动方式(横向滑动,竖向滑动)详细及实例代码
Jan 13 Javascript
jQuery 实现鼠标画框并对框内数据选中的实例代码
Aug 29 jQuery
layui 富文本编辑器和textarea值的相互传递方法
Sep 18 Javascript
vue限制输入框只能输入8位整数和2位小数的代码
Nov 06 Javascript
React中Ref 的使用方法详解
Apr 28 Javascript
TypeScript魔法堂之枚举的超实用手册
Oct 29 Javascript
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
JS实现简单九宫格抽奖
Jun 28 #Javascript
You might like
php session 预定义数组
2009/03/16 PHP
页面乱码问题的根源及其分析
2013/08/09 PHP
PHP 表单提交及处理表单数据详解及实例
2016/12/27 PHP
Ubuntu 16.04下安装PHP 7过程详解
2017/03/28 PHP
由php中字符offset特征造成的绕过漏洞详解
2017/07/07 PHP
js实现跟随鼠标移动且带关闭功能的图片广告实例
2015/02/26 Javascript
基于jQuery仿淘宝产品图片放大镜代码分享
2020/06/23 Javascript
JavaScript中字符串与Unicode编码互相转换的实现方法
2015/12/18 Javascript
原生js配合cookie制作保存路径的拖拽
2015/12/29 Javascript
JavaScript操作class和style样式代码详解
2016/02/13 Javascript
bootstrap3-dialog-master模态框使用详解
2017/08/22 Javascript
node.js的exports、module.exports与ES6的export、export default深入详解
2017/10/26 Javascript
layui.js实现的表单验证功能示例
2017/11/15 Javascript
解决vue页面DOM操作不生效的问题
2018/03/17 Javascript
区别JavaScript函数声明与变量声明
2018/09/12 Javascript
vue中uni-app 实现小程序登录注册功能
2019/10/12 Javascript
详解如何在Javascript和Sass之间共享变量
2019/11/13 Javascript
在nuxt中使用路由重定向的实例
2020/11/06 Javascript
python执行外部程序的常用方法小结
2015/03/21 Python
实例讲解Python中global语句下全局变量的值的修改
2016/06/16 Python
python微信公众号开发简单流程
2018/03/23 Python
解决PyCharm同目录下导入模块会报错的问题
2018/10/13 Python
解决python tkinter界面卡死的问题
2019/07/17 Python
基于python实现从尾到头打印链表
2019/11/02 Python
python使用 cx_Oracle 模块进行查询操作示例
2019/11/28 Python
python批量生成条形码的示例
2020/10/10 Python
python爬虫中采集中遇到的问题整理
2020/11/27 Python
Otticanet澳大利亚:最顶尖的世界名牌眼镜, 能得到打折季的价格
2018/08/23 全球购物
大码女装:Ulla Popken
2019/08/06 全球购物
智乐游戏测试笔试题
2014/05/21 面试题
大学生专科学习生活的自我评价
2013/12/07 职场文书
应届专科生个人的自我评价
2014/01/05 职场文书
上学迟到的检讨书
2014/01/11 职场文书
施工安全保证书
2015/05/09 职场文书
结婚司仪主持词
2015/06/29 职场文书
PHP连接MSSQL数据库案例,PHPWAMP多个PHP版本连接SQL Server数据库
2021/04/16 PHP