你不知道的javascript(上)

Posted by hdj on December 12, 2017

前言

最近双十一买了一大堆前端书籍,都是关于javascript方面的,大致翻阅了一下,决定从《你不知道的javascript》上卷开始读,目前已经读完,然而好记 性不如烂笔头,数天之后就忘记了,特意在博客中记录一下自己的心得体会。

第一章 作用域是什么

1.1 编译原理

javascript 是一门编译语言,编译语言在代码执行之前回执行三个步骤统称为编译:

  1. 分词/词法分析(Tokenizing/Lexing)

    这个过程会将由字符组成的字符串分解成有意义的代码块,这些代码块被称为词法单元,

    例如:var a = 2;这段程序通常会被分解成如下面这些词法单元: var, a, = ,2,空格是否会被当作词法单元取决于空格仔这门语言中是否具有意义。‘

  2. 解析/语法分析(Parsing)

    这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序与法结构的树,这个树被称为’抽象语法树‘(Abstract Syntax Tree, AST)

    例如:var a = 2;的抽象语法树中可能会有一个叫做 VariableDeclaration的顶级节点,接下来的是一个叫做Idenifier(他的值是a)的子节点,以及一个叫做AssignmentExpression的子节点。AssignmentExpression节点有一个叫做Numbericliteral(它的值是2)的子节点。

  3. 代码生成

    将AST转换为可执行代码的过程被称为代码生成。这个过程与语言,目标与平台等息息相关。

    例如:var a = 2; 的AST转化为一组机器指令用来创建一个叫做a的变量(包括分配内存等),并将一个值存储在a中。

1.2 理解作用域

1.2.1 从var = 2; 理解编译器执行

编译器会做如下处理:首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就赋值,找不到就报错。

1.2.2 LHS 和 RHS

LHS和RHS的含义是‘赋值操作的左侧或右侧’应理解为‘赋值操作的目标是谁(LHS)’以及谁是赋值操作的源头(RHS)‘

测验 找到其中所有的LHS查询(3处)和RHS查询(四处)

function foo(a){
    var b = a;
    return a+b;
}
var c = foo(2);

解析

 var c = foo(2); RLS   foo(2)RHS

  function foo(a){
       // 隐式调用 a = 2; LHS
		    var b = a; // LHS RHS
		    return a+b;// RHS RHS
  }

1.3 作用域嵌套

LHS和RHS查询都会从当前作用域中开始,如果当前作用域中不存在则向上级作用域继续查找,直到全局作用域停止。

区别在于
 非严格模式下
 不成功的RHS 会抛出ReferenceError 不成功的LHS会隐式创建一个全局变量;
 严格模式下都会报错

第二章 词法作用域

2.1 词法阶段

词法作用域就是定义在词法阶段的作用域,换句话说就是由你在写代码事将变量和块作用域写在哪里来决定的。因此词法分析器处理代码时会保持作用域不变。

无论函数在哪里被调用,也无论它如何被调用,她的词法作用域都只有函数被声明时所处的位置决定。

2.2 欺骗词法 (with,eval)

使用evalu 和with 可以欺骗词法作用域,:eval(‘var a = 2’) with(obj){}.eval通过包含一个或者多个声明的代码字符串尽心演算,并借此习惯已经存在的词法作用域。后者本质上是通过一个对象的引用当作作用域来处理,从而床技了一个新的词法作用域。

这两个方法副作用是引擎无法子编译时对作用域进行优化,导致代码变慢。不要使用!不要使用!不要使用!

第三章 词法作用域

3.1函数中的作用域

函数作用域是指:这个函数的全部变量都可以在整个函数范围内使用及复用。能够充分利用javascript变量可以根据需要改变值类型的‘动态’特性。

3.2隐藏内部实现

类似于java中的私有变量和属性,将属性和变量私有化,通过暴露方法修改值。

同时可以避免同名标识符,规避三方库的全局命名冲突问题,模块管理。

3.3函数作用域

区分环视声明和函数表达式:

function关键字出现在声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。

3.3.1 匿名和具名函数

setTimeout(function(){ 
   // do something
},1000)

匿名函数表达式:没有名称标识符。缺点如下

  1. 在栈追踪中不会显示出有意义的函数名,调试困难;
  2. 可读性差。

3.3.2 立即执行函数表达式(IIFE)

	  (function jQuery(w,undefined){
	   		console.log('函数自执行‘);
	  })(window)

3.4 块级作用域

{} 中的变量只能{} 中使用

经典问题

	  for(var i =0 ;i<10;i++){
	    setTimeout(function(){
	     console.log(i); 输出为10
	    },100);
	  }

解决方式:

    for(var i =0 ;i<10;i++){
        (function(i){
            setTimeout(function(){
            console.log(i)
            },100);
        })(i)
    }

3.4.1 with. try/catch

with 也是块作用域的一种形式

catch 中声明的变量只在catch中有效

3.4.2 ES6 let,const ,{}

在ES6中 let,const ,{}都有会计作用域。