Skip to content

Latest commit

 

History

History
211 lines (143 loc) · 7.78 KB

你不知道的JavaScript学习笔记(一).md

File metadata and controls

211 lines (143 loc) · 7.78 KB
name title tags categories info time desc keywords
你不知道的JavaScript学习笔记(一)
《你不知道的JavaScript》学习笔记(一)
技术
学习笔记
你不知道的JavaScript
学习笔记
你不知道的JavaScript 第1章 作用域是什么 第2章 词法作用域 第3章 函数作用域和块作用域
2019/2/26
你不知道的JavaScript, 资料下载, 学习笔记, 第1章 作用域是什么, 第2章 词法作用域, 第3章 函数作用域和块作用域
javascirpt高级程序设计资料下载
前端
你不知道的JavaScript
学习笔记
第1章 作用域是什么
第2章 词法作用域
第3章 函数作用域和块作用域

《你不知道的JavaScript》学习笔记(一)

资料下载地址(pdf压缩文件):

百度网盘

提取码: 7hik

本资料仅用于学习交流,如有能力请到各大销售渠道支持正版 !

本书上中下三卷共有762页正文可读内容

第一部分:作用域和闭包

第1章 作用域是什么

1.1 编译原理

JavaScript虽然被称为"动态"和"解释执行的语言",但它归根结底其实还是一门编译语言,而每一门编译语言在执行之前都会经历三个步骤,统称"编译":

  • 分词/词法分析
  • 解析/语法分析
  • 代码生成

但与其他语言不同的是,JavaScript并不是先编译后执行,而是一边编译一边执行的,一般来说,编译一段语句到执行这段语句之间只有几微秒甚至更短的时间。

1.2 理解作用域

RHS查询:取到变量的值,比如说console.log(a)中的a变量就需要执行一次RHS查询

LHS查询:为一个变量赋值,比如说a = 2语句就需要执行一次LHS查询,找到a变量并将2赋予它。

本节描述了作用域、引擎和编译器在执行语句时的详细流程,emmm,虽然有点gay里gay气但还是蛮好理解的。

1.3 作用域嵌套

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

1.4 异常

当RHS查询无法找到变量时,会抛出ReferenceError错误,提示该变量未定义。

而在执行LHS查询时,若程序执行在严格模式下,对未定义的变量进行赋值也会抛出错误,然而在非严格模式下,该操作会让引擎在全局作用域中创建一个具有该名称的变量。

第2章 词法作用域

作用域一般分为两种,词法作用域和动态作用域。大多数语言包括JavaScript使用的都是前者。

2.1 词法阶段

简单来说,词法作用域就是定义在词法阶段的作用域,也就是说该作用域是由你在写代码时将变量和块作用域卸载哪里来决定的。

作用域查找会在找到第一个匹配的标识符时停止,在多层的嵌套作用域中可以定义同名的标识符,这称为"遮蔽效应"。

2.2 欺骗词法

使用某些特定的方法和机制可以欺骗词法作用域。但这种方式不仅危险,还会导致性能的下降

2.2.1 eval

function foo (str, a) {
    eval(str) // 欺骗
    console.log(a, b)
}
var b = 2
foo('var b = 3', 1)
// 若不进行欺骗,输出本应是 1, 2
// 但此时实际输出为 1, 3

在以上的例子中,eval函数插入了一段声明,使得词法作用域出现了遮蔽效应,外部的变量并没有被访问到,反而是定义了一个本不该被引用的变量b。

eval函数功能相似的还有以下几种:

  • setTimeout()setInterval()方法的第一个参数传入字符串
  • 进行new Function(..)操作

2.2.2 with

使用with操作符可以延展当前作用域,在当前作用域中套用其他任意作用域。但要注意的是,with操作符并不能新增或更改其延展的作用域中没有的属性,因此很容易会造成变量被泄漏到全局作用域中的情况。

function foo (obj) {
    with (obj) {
        a = 2
    }
}
var o1 = {a:3}
var o2 = {b:1}
foo(o1)
o1.a // 2 被with更改
foo(o2)
o2.a // undefined 由于o2中本身并没有a属性,所以无法新增和更改
window.a // 2 反而是将不应该定义的属性定义在了全局作用域上,造成了变量泄漏

2.2.3 性能

过分的使用evalwith不仅会导致代码结构混乱,而且会大大增多查找词法作用域的操作,造成性能的降低。在真正的代码编写时,不要使用它们

第3章 函数作用域和块作用域

本章探讨哪些操作会创建一个作用域。

3.1 函数中的作用域

JavaScript中,每生成一个函数都会生成一个对应的作用域。

3.2 隐藏内部实现

可以把变量和函数包裹在一个函数的作用域中,然后用这个作用域来隐藏它们,更好的符合最小授权原则。

最小授权/最小暴露原则:在软件设计中,应该最小限度地暴露必要内容,将其内部隐藏起来。

3.3 函数作用域

使用一个立即执行的函数表达式((function foo () {})())可以让你达到既不污染全局作用域,又能完成想要做的变量定义和方法执行的目的。

3.3.1 匿名和具名

function () ...没有名称和标识符,称为匿名函数表达式。函数表达式可以是匿名的,而函数声明则不可以(会报错)。

3.3.2 立即执行函数表达式

也称为IIFE形式,具体表现形式为(function foo () {})()(function foo () {}()),这两种形式功能是完全一样的。

改形式除了实现上面所述的用途以外,还可以用来进行倒置代码

var a = 2
(function IIFE (def) {
    def(window)
})(function def (global) {
    var a = 3
    console.log(a) // 3
    console.log(global.a) // 2
})

3.4 块作用域

在ES6之前,JavaScript是没有块作用域的。看似在块级作用域中定义的变量最终还是在函数作用域中执行。

for (var i = 0; i < 5; i++) {console.log(i)}
// 上面这段代码和下面这段代码作用是一毛一样的
var i = 0
for (i = 0; i < 5; i++) {console.log(i)}

这在ES6之前就会导致一个问题,开发者需要检查自己的代码,避免在作用范围外以外地使用(或复用)某些变量,而这是绝大多数其他语言中不会出现的问题。

当然,除了ES6以外,还有一些特殊的方法会产生块作用域,但很难用于正经开发上。

3.4.1 with

with是会产生一个块作用域的。

3.4.2 try/catch

try/catch中的catch语句也会生成一个块作用域。

try {
    throw new Error('异常')
} catch (err) {
    console.log(err)
}
console.lor(err) // 报错

上述代码中的err变量就处于一个块作用域中,外部无法访问。

3.4.3 救世主ES6之 let

  • let 关键字可以将变量绑定到所在的任意作用域中。let为其声明的变量隐式地声明了所在的块作用域。

  • let进行的声明不会再块作用域中进行提升

  • 块作用域还可以用于促进垃圾回收,优化代码,如下

    function process (data) {}
    // 使用这种方式显式声明块作用域,可以告诉引擎对这部分的变量进行垃圾回收
    {
        let someBigData = {}
        process(someBigData)
    }
    var btn = document.getElementById('btn')
    btn.onclick = function click (e) {
        console.log('button clicked')
    }
  • let循环,更常用的循环方式

3.4.4 const

const同样由ES6引入,同样可以用来创建块作用域变量,之后任何试图修改值得操作都会引起错误。