Skip to content

Latest commit

 

History

History
65 lines (44 loc) · 4.2 KB

File metadata and controls

65 lines (44 loc) · 4.2 KB
layout title
default
{"site.name" => nil}

作用域和闭包

作用域

尽管JS被归类为动态或解释型语言,但他仍是一门编译语言。但与传统的编译语言对比,他不是提前编译的,编译结果也不能在分布式系统中进行移植。尽管如此,JS引擎进行编译的过程和传统的编译语言很像,在某些环节比预想的要复杂。

一般的编译过程:

  • 分词/词法分析:就是将字符串变成有意义的代码块,被称为词法单元,分词和此法分析的区别在于在辨认某个词法单元是独立的还是其他语法的一部分的时候,是使用的是有状态的解析规则还是无状态的,如果是有状态的,就是词法分析。
  • 解析/语法分析:就是将词法单元变成代表了程序算法结构的树,也就是传说中的AST了
  • 代码生成:就是将AST变成可执行代码的过程,与语言,目标平台息息相关。

比起那些编译只有3个过程的编译器,JS要复杂的多,能够在语法分析和代码生成阶段对运行性能进行优化,包括对冗余元素进行优化。

LHS和RHS

LHS为left hand side,就是等式的左边,其实就是一个寻找容器的过程;RHS为right hand side,他寻找的就是一个真实的结果。

作用域嘛,就不多提了,层层寻找的嘛。

为什么要区分LHS和RHS呢,因为两者的一些行为不同。RHS查找值,找不到是会报错的。而LHS查找容器,如果找不到的话,是会帮忙创建一个的。当然,严格模式是一样会报错的。

词法作用域

词法作用域这个名词是因为编译阶段最开始的时候就是词法化的过程。所以词法作用域是在写代码的时候将变量和块作用域写在的地方。JS是词法作用域!!!

欺骗语法

就是eval和with。比如eval是会改变当时定下的语法作用域的。但是严格模式下eval会有自己的作用域。但是这种的使用不会很多,毕竟性能原因很重要。with就很简单的理解为加了一层的作用域范围就好了。

函数作用域和块作用域

函数作用域将变量隐藏了起来,目的是减少权利。但是我们时常需要申明一个函数,然后运行它来做到变量的隐藏。于是我们发明了IIFE(立即执行表达式)。

with和try catch的catch部分都会创建一个块级作用域。

这个可以用来模拟块级作用域(let)。Traceur的块级作用域就是用的这个来转换的,但是babel不是的,babel是按一定的规则重命名变量。他们都不能做到阻止变量提升。

提升

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

这个的结果是2,好吧,做题还是要细心啊..就是编译阶段,声明操作会置顶。即使具名的函数表达式,也不能在赋值操作前被使用。类似于下面

var a = function b(){
};
//这句话b在函数内部指向了这个函数,但是在a所在的作用域中没有b这个变量。

函数的提升比较优先,他是会在最上面的。就是如果存在同名函数和变量的提升。函数会生效。然后如果有重复的函数,后面的会替代前面的函数。

作用域闭包

函数能够记住并访问所在的词法作用域。这其实只是一个闭包的一部分。他的真正价值在于他的这个特性导致了他即使不在他的申明的词法作用域运行的时候,他也能够访问到他定义的词法作用域的值。他保存了数据结构。

所以其实差不多只要存在了回调函数,存在了一个函数被抛出申明环境的话,就存在了一个我们可以利用的闭包。还有一种经典的闭包就是在循环的时候我们通过IIFE来创建一个作用域。然后将循环的值保存在这个作用域之中,这样我们在循环内部想访问i的话,就会能访问到我们保存的这个作用域了。但是我们通过使用let可以替代这种IIFE的写法。

闭包的一大功能就是模块,模块基本都是闭包。通过暴露内部的函数来让外界获得访问自己内部作用域的能力。

函数是运行在他申明的地方的。

this

箭头函数能够绑住this值,就跟bind函数的效果