闰土说JS进阶之作用域链
前言
在前端应聘中,相信会有不少面试官都会问你,说说你理解的js作用域,或者作用域链。显然,这是一道经典的js面试题,对于老司机而言可谓是小菜一碟,而对于前端新人,尤其是js基础不扎实的新人来说,则会说的模棱两可,含糊其辞。当然这样青涩的答案显然不会入面试官的法眼,甚至会让你白白丢掉一次面试机会。那么作用域链究竟好不好理解呢,接下来正文开始。
在说作用域前,我们先来说说什么是执行环境。新人容易把执行环境和作用域这两个概念搞混了,要知道函数的每次调用都有与之相关的作用域和执行环境。从根本上来说,作用域是基于函数的,而执行环境是基于对象的,举个栗子,全局执行环境是window对象。
执行环境,同行们也喜欢叫执行上下文。每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
执行环境分为创建和执行两个阶段。在创建阶段,解析器首先会创建一个变量对象,也叫活动对象,俗称AO。它由定义在执行环境中的变量、函数声明和参数组成。在此期间,作用域链会被初始化,this的值也会被确定。而在执行阶段,代码便会被解释执行。
如果这时面试官问你作用域链的作用是什么?你是不是会瞬间懵逼?别急。且听闰土怎么说。那么这个问题的标准答案就是,作用域链的用途是用于解析标识符。(知道了吧,是不是该用笔记下来,或者划划重点啥的,闰老师说了,这是必考题,嘿嘿~)
标识符的解析是从作用域链的前端开始,沿着作用域链一级一级向后回溯,直至找到标识符为止。(如果找不到,则会报错)。
说了这么久,给大家举个栗子:
var food = '麻辣烫';
function changeFood(){
var anotherFood = '过桥米线';
function swapFood(){
var tempFood = anotherFood;
anotherFood = food;
food = tempFood;
//这里可以访问food、anotherFood、tempFood
}
//这里可以访问food、anotherFood
swapFood();
}
//这里只能访问food
changeFood();
以上代码中,在swapFood()函数的局部环境中有个变量tempFood,这个变量只能在这个环境中访问到。无论全局环境还是changeFood()的局部环境都无权访问tempFood。而在swapFood()内部则可以访问其他两个环境中的所有变量,因为那两个环境是它的父执行环境。
总结一下,内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。
接下来,我们再来讲讲块级作用域。
在ES6没出来之前,javascript是没有块级作用域的。在这一点上,很多从后端转过来的前端人员都比较困惑,因为在C++或者java中,由花括号封闭的代码块都有自己的作用域,用ES自己的话来说,就是它们自己的执行环境。这也是我认为的js的一个缺陷。让我们一起来看看下面这个for循环的栗子:
for(var i=0;i<10;i++){
console.log('i: ' + i);
}
console.log(i); // 10
对于有块级作用域的语言来说,i变量会在for循环执行结束之后销毁,换句话说,它只会存在循环体内。而对于javascript来说,变量i即使在for循环执行结束以后,也依旧会存在于循环体外部的执行环境。所以最后console出来的i 是 10。扩展一下,如果用ES6的let来声明变量i,那么console出来的结果就是 i is not defined。因为ES6中的let声明的变量具有块级作用域的特性。
在javascript编码过程中,不声明而直接初始化一个变量是一个常见的错误做法,因为这样可能会导致意外。所以,想要了解大公司的javascript编码规范,首先要从细节开始,从初始化变量前先声明开始。
小结
每天进步一点点,每天消化掉一个小知识点,才能积少成多,最后量变导致质变。愿大家都能成为更好的前端,做更好的自己。
最后给大家放个彩蛋,看闰土哥在公园玩俯卧撑快闪,请为闰土少年打call!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 卷积神经网络之 - GoogLeNet / Inception-v1
- 卷积神经网络之 - Alexnet
- Linux系列之学会使用Top命令进行系统监控
- 一简单线程同步笔试题分享,欢迎纠错分享更多思路
- 卷积神经网络之-NiN 网络(Network In Network)
- 【注意力机制】空间注意力机制之Spatial Transformer Network
- 【小技巧】用Python给你的视频添加字幕
- 震惊!我三步就搞定了 Tomcat 源码环境搭建!
- 【小技巧】深度学习中的那些效率提升利器(附资源)
- 多个线程为了同个资源打起架来了,操作系统是如何让他们安分的?
- 学习链表,这些题你值得一刷!
- Gophish钓鱼测试
- 【位运算】只出现一次的数字 II,数电的知识终于用上了!
- Swaks伪造邮件发件人绕过SPF
- 某次网站的渗透测试