《你不知道的JavaScript》读书笔记(一)

1、编译原理

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

这个过程会将由字符组成的字符串分解成( 对编程语言来说) 有意义的代码块, 这些代码块被称为词法单元( token)。 例如, 考虑程序 var a = 2;。 这段程序通常会被分解成为下面这些词法单元: var、 a、 =、 2 、 ;。 空格是否会被当作词法单元, 取决于空格在这门语言中是否具有意义。



2、理解作用域

当你看到var a=2;这个代码段的时候,你也许只会认为这是一个声明语句,但是事实上,浏览器引擎并不会这么认为!其实浏览器会这么认为:

1、遇到 var a, 编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。 如果是, 编译器会忽略该声明, 继续进行编译; 否则它会要求作用域在当前作用域的集合中声明一个新的变量, 并命名为 a。

2、接下来编译器会为引擎生成运行时所需的代码, 这些代码被用来处理 a = 2 这个赋值操作。 引擎运行时会首先询问作用域, 在当前的作用域集合中是否存在一个叫作 a 的变量。 如果是, 引擎就会使用这个变量; 如果否, 引擎会继续查找该变量。



3、作用域嵌套

当一个块或函数嵌套在另一个块或函数中时, 就发生了作用域的嵌套。 因此, 在当前作用域中无法找到某个变量时, 引擎就会在外层嵌套的作用域中继续查找, 直到找到该变量,或抵达最外层的作用域( 也就是全局作用域) 为止。

function foo(a) {
    console.log(a + b);
}
var b = 3;
foo(3);

遍历嵌套作用域链的规则很简单: 引擎从当前的执行作用域开始查找变量, 如果找不到,就向上一级继续查找。 当抵达最外层的全局作用域时, 无论找到还是没找到, 查找过程都会停止。



4、立即执行函数

var a = 3;
(function IIFE() {
    var a = 4;
    console.log(a);
})();
console.log(a);

由于函数被包含在一对 ( ) 括号内部, 因此成为了一个表达式, 通过在末尾加上另外一个( ) 可以立即执行这个函数, 比如 (function foo(){ .. })()。 第一个 ( ) 将函数变成表达式, 第二个 ( ) 执行了这个函数。

相较于传统的 IIFE 形式, 很多人都更喜欢另一个改进的形式: (function(){ .. }())。 仔
细观察其中的区别。 第一种形式中函数表达式被包含在 ( ) 中, 然后在后面用另一个 () 括号来调用。 第二种形式中用来调用的 () 括号被移进了用来包装的 ( ) 括号中。这两种形式在功能上是一致的。 选择哪个全凭个人喜好。

提升

考虑以下代码:

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

你认为 console.log(..) 声明会输出什么呢?
很多开发者会认为是 undefined, 因为 var a 声明在 a = 2 之后, 他们自然而然地认为变量被重新赋值了, 因此会被赋予默认值 undefined。 但是, 真正的输出结果是 2。

考虑另外一段代码:

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

鉴于上一个代码片段所表现出来的某种非自上而下的行为特点, 你可能会认为这个代码片段也会有同样的行为而输出 2。 还有人可能会认为, 由于变量 a 在使用前没有先进行声明,因此会抛出 ReferenceError 异常。

不幸的是两种猜测都是不对的。 输出来的会是 undefined。

那么到底发生了什么? 看起来我们面对的是一个先有鸡还是先有蛋的问题。 到底是声明( 蛋) 在前, 还是赋值( 鸡) 在前?

正确的思考思路是, 包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。当你看到 var a = 2; 时, 可能会认为这是一个声明。 但 JavaScript 实际上会将其看成两个声明: var a; 和 a = 2;。 第一个定义声明是在编译阶段进行的。 第二个赋值声明会被留在原地等待执行阶段。

我们的第一个代码片段会以如下形式进行处理:

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

其中第一部分是编译, 而第二部分是执行。

类似地, 我们的第二个代码片段实际是按照以下流程处理的:

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

因此, 打个比方, 这个过程就好像变量和函数声明从它们在代码中出现的位置被“ 移动”到了最上面。 这个过程就叫作提升。换句话说, 先有蛋( 声明) 后有鸡( 赋值)。

只有声明本身会被提升, 而赋值或其他运行逻辑会留在原地。 如果提升改变了代码执行的顺序, 会造成非常严重的破坏。



5、函数优先

函数声明和变量声明都会被提升。 但是一个值得注意的细节( 这个细节可以出现在有多个“ 重复” 声明的代码中) 是函数会首先被提升, 然后才是变量。

考虑以下代码:

foo();
var foo;
function foo() {
    console.log(1);
}
foo = function () {
    console.log(2);
};

会输出 1 而不是 2 ! 这个代码片段会被引擎理解为如下形式:

function foo() {
    console.log(1);
}
foo();
foo = function () {
    console.log(2);
};

注意, var foo 尽管出现在 function foo()… 的声明之前, 但它是重复的声明( 因此被忽略了), 因为函数声明会被提升到普通变量之前。

尽管重复的 var 声明会被忽略掉, 但出现在后面的函数声明还是可以覆盖前面的。

foo();//3
function foo() {
    console.log(1);
}
var foo = function () {
    console.log(2);
};
function foo() {
    console.log(3);
}
时间: 2024-09-27 09:14:01

《你不知道的JavaScript》读书笔记(一)的相关文章

《java深度历险》读书笔记(一)

笔记 最近开始看 王森先生的<java深度历险>,从我一借到这本书就爱不释手,书里的内容非常吸引人.可以说这本书有助于我们对java程序运行过程有更深的理解.所以我将记录一些书中的重要结论,供大家参考,也作为自己的笔记.     第一章: 我们的机器里面至少有两套jre,一套在%JAVA_HOME%/jre,一套在%HOME%/Program Files/Java/jre1.x.x,那么当我们在命令行输入java xxx的时候会按照下面的逻辑来寻找适合的jre来执行程序: 1.       当

Android应用开发提高系列(2)——《Practical Java 中文版》读书笔记(下)

声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com   系列 Android应用开发提高系列(1)--<Practical Java 中文版>读书笔记(上)    正文  注意:条目和用语可能与书籍有所出入,但尽量保持原样加一些自己的理解. 一.性能 1. 先把焦点放在设计.数据结构和算法身上 备注:良好的设计.明智的选择数据结构和算法可能比高效代码更重要.   2.  不要依赖编译

Java与XSLT读书笔记(1)

笔记 <Java与XSLT>读书笔记 一,所有的XSLT处理器必须包括四个内置的模版规则,它们的优先级要低于任何其他规则,所以只要编写一个新的模版规则来匹配相同的式样,就可以覆盖它们.理解内置规则的最好方法就是架设它们总是位于后台,如果没有找到其他匹配一个节点的规则,就应用这些内置规则. <xsl:template match="*|/"> <xsl:apply-templates/> </xsl:template> <xsl:te

Java Servlet Programming 读书笔记 - servlet生命周期

servlet|笔记 servlet的生命周期一般为: 1.建立初始化servlet 2.处理从客户端的零个或多个请求 3.销毁servlet,gc回收占用内存 每个server可能在如何支持servlet上有不同的方法,但是上述servlet生命周期却是每个servlet 引擎必须遵守的规则. 实例持久化Instance Persistence: 一个servlet 实例一旦加载,就开始处理对这个servlet的所有请求,换句话说就是一个servlet只生成一个实例.这样的做法对于性能的提高很

《深入理解Java虚拟机》读书笔记

背景 并发处理的广泛应用是使得Amdahl定律代替摩尔定律成为计算机性能发展的源动力的根本原因,也是人类压榨计算机运算能力最有力的武器 Amdahl 定律通过系统中的并行化与串行化的比重来描述多处理器系统能获得的运算加速能力. 摩尔定律则用于描述处理器晶体管数量与运行效率之间的发展关系. 这两个定律的更替代表了近年来硬件发展从追求处理器频率到追求多核心并行处理的发展过程. 高效并发 物理机上的并发解决方案 在当前这个多核处理器时代,"让计算机并发执行若干个运算任务"和"更充分

Android应用开发提高系列(3)——《Effective Java 中文版》读书笔记

声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com   书籍 <Effective Java 中文版> 03版 潘爱民译 本书介绍了57条极具实用价值的经验规则.这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案,通过对Java平台设计专家所使用的技术的全面描述,揭示了应坐什么和不应做什么,才能产生清晰.健壮和高效的代码.   正文  注意:条目和用语可能与书籍有所出入,但尽

《深度探索C++对象模型》读书笔记 最后一记

第6章主要讲述了执行期语意学,主要内容是关于数组的在构建和析构是如何进行的. 第7章主要讲述了有关Template的相关内容. 这两章内容散见于<Effective C++>.<More Effective C++>.<C++Primer><C++Templates中 文版>等书籍,如果感兴趣请阅读对应的书籍. 本读书笔记主要想谈一下对语意的理解. 本人认为C++程序设计可以简单分为三个层次:语法层.语言语意层(就像<深度探索C++对象模型>所讲

《淘宝技术这十年》读书笔记 (二).Java时代的脱胎换骨和坚若磐石

        马云说过"一个好的东西往往是是说不清楚的",姑且不论这句话的对与错.但我真的很佩服<淘宝技术这十年>这本书的作者子柳,能够通过淘宝的一些故事,按照时间顺序和IT发展的各种技术描述清楚,而且过程中读起来非常有意思.         该读书笔记中参杂了很多原文的知识,因为我实在无法割舍,都挺有意思的:同时记录一些有用的知识,通过这本书能介绍些学过的知识或面试中可能出现的题目及作者所思,文章还是非常有趣的,希望对大家有所帮助! 一. Java时代 脱胎换骨    

整理java读书笔记十五之java中的内部类_java

内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同. *内部类可以是静态static的,也可用public,default,protected和private修饰.(而外部顶级类即类名和文件名相同的只能使用public和default). 前言  Java从JDK1.1的时候,就开始引入内部类的概念了,那么小编也通过这篇博客来分享一下Java中有关内部类的一些特性.  什么是内部类?  在很多情况下,类被定义成一个独立的程序单元,但是有时候也会把一个类放在另一个类的内部定义,这个定

《淘宝技术这十年》读书笔记 (四). 分布式时代和中间件

        前面两篇文章介绍了淘宝的发展历程.Java时代的变迁和淘宝开始创新技术:            <淘宝技术这十年>读书笔记 (一).淘宝网技术简介及来源            <淘宝技术这十年>读书笔记 (二).Java时代的脱胎换骨和坚若磐石            <淘宝技术这十年>读书笔记 (三).创造技术TFS和Tair        这篇文章主要讲述分布式时代和中间件相关知识,包括服务化.HSF.Notify和TDDL.同时里面有我们经常遇见的编