看完《你不知道的javascript》上,对作用域的新的理解(2018-9-25更)

一、学习笔记:

  1、javascript中的作用域和作用域链

+  每个函数在被调用时都会创建一个自己的执行环境(作用域),javascript中一切皆为对象,函数也是一个对象;函数对象和其他对象一样,拥有通过代码访问的属性和仅供javascript引擎访问的内部属性。其中一个内部属性就是[[scope]],它是函数在被定义时就创建的,它包含了

函数被创建的作用域中对象的集合。这个集合被称为函数的作用域链,作用域链决定了哪些数据可以被函数访问到。

作用域链的前端始终都是当前执行的代码所在的环境中的变量对象(作用域链的前端是一个活动对象,当函数被调用时会创建这个活动对象,这个活动对象由函数中的局部变量、命名参数、参数集合、this组成),在函数中查询标识符时都是从这个作用域的前端开始逆向查找。

  2、 javascript的预编译

+ Js执行过程是一次翻译执行的过程,Js中也有编译的过程,JS在执行每一段JS代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式).也就是说会将它们的声明提前,但是函数表达式的声明是不能提前的,函数表达式是在执行的时候才计算的。

3、减少全局变量的使用

因为全局变量总是在作用域链的最末端,所以查找起来是最慢的。一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使 用.(例如:在dom操作中经常要使用的document,可以先将document存在一个局部变量里面)

4、 javascript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.

看完《你不知道javascript》上卷,对很多以前不懂的js细节问题顿时恍然大悟的感觉,以前对js基础知识的理解看来都只是停留在表面,并没有真正了解到其实质,这大概也就是为什么以前一遇到一个js问题总是不能灵活变通的原因吧。

清除掉之前的理解,用自己的理解来总结什么是js中的作用域。

1、对js作用域的理解

    js作用域可以视为维护(查找)和存储变量的一套规则,作用域决定了当前执行的代码对变量的访问权限。

(试着想一下,js中是如何来存储申明的变量?程序又是如何来找到存储的变量的呢?作用域确定了这套规则)

2、知道为什么js中会有所谓的变量提升吗?

    a) 你需要理解的js中的编译原理

        js在执行时分为两个阶段:编译阶段、运行阶段。

编译器负责编译js代码,js引擎负责运行编译器编译生成的可执行代码。

js编译阶段又分三个阶段:

1、分词/词法分析:将字符串分解成词法单元,也就是对编程语言来说有意义的代码块(这里其实我不太理解到底是啥意思)

2、解析/词法分析:将词法单元转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树,即ast,抽象语法树。

3、代码生成:最后将ast抽象语法树转换成机器指令,即可执行的代码

js在编译时的词法分析阶段,当遇到变量声明的时候,首先会去询问作用域集合当前声明的变量是否已经存在,如果存在就会忽略声明,继续编译;

否则就会在作用域中去创建变量。

js在运行编译生成的可执行的代码的时候,当遇到变量申明,会对变量进行RHS(右手边查询)或LHS(左手边查询),对于进行右手边查询的变量

如果作用域集合中没有存储该变量,就会报refrenceerror错误,即变量没有定义的错误;而对于左手边查询如果在最顶层的全局作用域中都没有找到

变量,就会在全局作用域中创建一个具有该名称的变量。

example1: 

        这也是为什么不通过var或let等标识符,声明一个赋值了的变量可以输出得到正确结果,但是直接输出一个没有赋值的变量就会报错的原因: 

c=2;
console.log(c)//2 c是LHS查询
console.log(b)//ReferenceError b是RHS查询

    如何区分RHS查询和LHS查询:

操作符例如“=”的左边的变量就是LHS查询,右边的变量就是RHS查询

在调用函数,并给函数参数赋值时,函数参数在js运行的时候进行的是LHS查询

console.log(b)在js执行阶段执行,b进行的RHS查询

return a+b,return一个表达式时会进行一次RHS查询

    b) 为什么会有变量提升呢?

       example2:

console.log(a);
var a = 2;
//2

example3:  

console.log(a);
var a = 2;
function a () {console.log(b)}
//ƒ a () {console.log(b)}

  这是因为js在执行代码时分编译和运行两个阶段,在js编译阶段就预先将声明的变量定义在了作用域中,在js运行的阶段,当遇到变量的时候就可以直接在作用域集合中读取到该变量,对于声明了但还没有被赋值的变量,默认为undefined,当js执行到该变量的赋值语句的时候,就会给这个变量赋值。

        

后面的可以不用看啦,知其然不知其所以然的解释。

只有知其然并知其所以然,也就是真正的理解一个点,这样才会真正的将知识刻在脑子里。

二、 代码验证

var name = "youyi";
function getName() {
//函数在被执行之前会先创建一个活动对象,
//js先会预编译var关键字,也就是说会将name变量声明提前
alert(name);//undefined
var name = "hello";
}
getName(); //函数运行在函数被定义时的作用域里,而不是运行时的作用域里
function factory() {
var username = 'laruence';
var intro = function(){
alert('I am ' + username);// I am laruence
}
return intro;
} function app(para){
var username = para;
var func = factory();
func();
} app("hahaha");

 参考资料:

《javascript高级程序设计》

《你不知道的javascript》上

最后编辑时间:

2018-9-25

Javascript作用域学习笔记(三)的更多相关文章

  1. JavaScript作用域学习笔记

    重点知识点摘要 一 函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性 其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部 ...

  2. angular学习笔记(三十)-指令(10)-require和controller

    本篇介绍指令的最后两个属性,require和controller 当一个指令需要和父元素指令进行通信的时候,它们就会用到这两个属性,什么意思还是要看栗子: html: <outer‐direct ...

  3. angular学习笔记(三十)-指令(7)-compile和link(2)

    继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...

  4. angular学习笔记(三十)-指令(6)-transclude()方法(又称linker()方法)-模拟ng-repeat指令

    在angular学习笔记(三十)-指令(4)-transclude文章的末尾提到了,如果在指令中需要反复使用被嵌套的那一坨,需要使用transclude()方法. 在angular学习笔记(三十)-指 ...

  5. angular学习笔记(三十)-指令(5)-link

    这篇主要介绍angular指令中的link属性: link:function(scope,iEle,iAttrs,ctrl,linker){ .... } link属性值为一个函数,这个函数有五个参数 ...

  6. javascript正则表达式 - 学习笔记

    JavaScript 正则表达式 学习笔记 标签(空格分隔): 基础 JavaScript 正则表达式是用于匹配字符串中字符组合的模式.在javascript中,正则表达式也是对象.这些模式被用于Re ...

  7. amazeui学习笔记三(你来我往1)--常见问题FAQs

    amazeui学习笔记三(你来我往1)--常见问题FAQs 一.总结 1.DOM事件失败:记得加上初始化代码,例如 图片轮播 $('#my-slider').flexslider(); 2.jquer ...

  8. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  9. 深入理解javascript作用域系列第三篇——声明提升(hoisting)

    × 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...

随机推荐

  1. zoj3732&& hdu4797 Graph Reconstruction

    Graph Reconstruction Time Limit: 2 Seconds      Memory Limit: 65536 KB      Special Judge Let there ...

  2. System.out.println(i++); System.out.println(++i);的区别

    之前一直对i++和++i很模糊,这次通过两个小demo来探究下. 例1: public static void main(String[] args) { int i=2; System.out.pr ...

  3. L243 词汇题2009

    The applications of genetic engineering are abundant (plentiful) and choosing one appropriate for th ...

  4. java开发的23中设计模式

    本文转自  Java开发中的23种设计模式详解(转)   设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓 ...

  5. 务实java基础之集合总结

    Java 提供了容纳对象(或者对象的句柄)的多种方式.其中内建的类型是数组,此外, Java 的工具库提供了一些 "集合类",利用这些集合类,我们可以容纳乃至操纵自己的对象. 声明 ...

  6. Linux环境下 多线程下载 (Python 实现版)

    本文是多年前学习编程时参照一个网友程序的基础之上改写的, 采用Python语音编写, 多线程下载功能, 可以有效提高Linux下原有下载工具中的一些不足,以下给出具体代码. #!/usr/bin/py ...

  7. chapter02 svm对手写体数字的数码图像进行识别

    #coding=utf8 # 从sklearn.datasets里导入手写体数字加载器. from sklearn.datasets import load_digits # 从sklearn.cro ...

  8. python3 获取当前调用函数名

    import sys funcName = sys._getframe().f_back.f_code.co_name #获取调用函数名lineNumber = sys._getframe().f_b ...

  9. 《DSP using MATLAB》Problem 4.18

    代码: %% ------------------------------------------------------------------------ %% Output Info about ...

  10. js循环总结

    js原生的循环有两种,一般的for循环和for...in循环.还有一种常用jQuery.each()循环. 一. js原生循环 a. for循环,代码如下: var myArray = [1,2,3] ...