前言
最近双十一买了一大堆前端书籍,都是关于javascript方面的,大致翻阅了一下,决定从《你不知道的javascript》上卷开始读,目前已经读完,然而好记 性不如烂笔头,数天之后就忘记了,特意在博客中记录一下自己的心得体会。
第一章 作用域是什么
1.1 编译原理
javascript 是一门编译语言,编译语言在代码执行之前回执行三个步骤统称为编译:
-
分词/词法分析(Tokenizing/Lexing)
这个过程会将由字符组成的字符串分解成有意义的代码块,这些代码块被称为词法单元,
例如:var a = 2;这段程序通常会被分解成如下面这些词法单元: var, a, = ,2,空格是否会被当作词法单元取决于空格仔这门语言中是否具有意义。‘
-
解析/语法分析(Parsing)
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序与法结构的树,这个树被称为’抽象语法树‘(Abstract Syntax Tree, AST)
例如:var a = 2;的抽象语法树中可能会有一个叫做 VariableDeclaration的顶级节点,接下来的是一个叫做Idenifier(他的值是a)的子节点,以及一个叫做AssignmentExpression的子节点。AssignmentExpression节点有一个叫做Numbericliteral(它的值是2)的子节点。
-
代码生成
将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)
匿名函数表达式:没有名称标识符。缺点如下
- 在栈追踪中不会显示出有意义的函数名,调试困难;
- 可读性差。
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 ,{}都有会计作用域。