ESLint

什么是 Linting

Linting 是通过运行一个用于分析源代码的程序,从而发现被分析的源代码中可能存在的错误的过程。

https://stackoverflow.com/questions/8503559/what-is-linting

Linting 是对代码的静态分析,经常被用作发现代码中的容易导致问题的编码模式以及是否违背了某种特定的源代码书写规范或风格。

https://eslint.org/docs/about/

Linting重要性之于JS

JavaScript是动态松散类型编程语言,特别容易出现开发人员错误。其他编译型语言可以把linting过程集成到编译过程中,但JS是解释型语言,没有编译过程。所以为了发现源代码中的语法错误或其他错误,需要将源代码运行。linting工具可以在不运行源代码的情况下,对代码进行静态代码分析,从而在运行之前发现错误。

https://eslint.org/docs/about/

什么是ESLint

ESLint是一个开源的JavaScript linting工具,最初由Nicholas C. Zakas在2013年6月创作。

特点

使用

环境要求

简单来说如果你使用vscode编写js代码,将ESLint配合下面的插件使用,那么你会感觉自己真正的拥有了一个写js代码的IDE,它可以做到在你编写代码的同时,对你的代码进行静态分析,发现你代码中的错误,并友好的进行提示,甚至可以自动修复ESLint指定为可以自动修复的错误。在代码运行之前发现并修复错误,这可以大大提高你的开发效率。绝对是一个开发利器。

ESLint Plugin for vscode

结语

这里仅对ESLint进行了一个粗略的介绍和本人亲身的使用感受,因为官方文档对ESLint的概念使用进行了非常完善的描述,所以就不在赘述。

官方文档

我理解的 JavaScript Event Loop

在学习JavaScript的事件循环(event loop)的过程中,遇到了调用栈(call stack)的概念。阅读了若干资料,对此概念感觉更加清晰了。在此记录并分享一下自己的理解。

强调一下

首先,需要“咬文嚼字”一下,“调用栈”,应该更加精确的描述为“函数调用栈”,因为此概念所涉及的就是关于函数调用过程的,而不其他代码的执行过程,所以强调一下。

解释器对函数调用的大概过程

javascript是单线程 single thread: 简单来说就是,代码只能一行(逻辑行)一行的执行,同一时间只能做一件事情。因为js是单线程的,所以它不能阻塞,因为这等于整个程序都暂停运行。那么在浏览器中,我们是如何去执行并发的去执行任务的呢?并发的任务是交给浏览器去做的,这时就需要事件机制。js可以注册事件回调,这个注册的过程会立即返回,不会阻塞js代码的执行。这个回调函数,会被添加到 “事件表”中。当回调对应的事件被触发后,回调函数会被推入到,解释器的消息队列中(message queue)(FIFO)。当,之前提到过的 call stack 为空后,解释器会从 消息队列中,取出并从队列中移除,队头的函数,放入ß call stack 中,然后执行,执行完毕后,从call stack中移除。然后,每当call stack 为空时,就会检查消息队列中是否有函数,如果有就放入 call stack 执行。

参考资料

[Call stack MDN](https://developer.mozilla.org/en-US/docs/Glossary/Call_stack)

从minipack理解webpack原理

通过阅读 minipack 对于 webpack 打包工具的原理有更好的认识。

模块打包器的功能是将小块独立的代码编制成更大而复杂的可以运行在浏览器中的代码。这些小块的代码就是 一些 JavaScript 文件,其中用模块系统描述了彼此的依赖关系。


    let ID = 0; //设置全局变量 id


    /*
     * 提取单个模块的依赖信息 和 代码 组装成 模块信息对象。
     */
    function createAsset(filename) {
         //1. 读取 filename 指定的文件,获取 文件内容 content
         //2. 通过 babylon 解析 content 为 抽象语法树 ast
         //3. 创建 dependencies 数组,用以保存 此模块 依赖模块的文件的相对路径。
         //4. 通过遍历语法树 ast 找出 所有的  依赖声明(import、require语句)中的 路径,存入 dependencies 数组。
         //5. 给 当前 模块 设置 唯一标识 id = ID++;
         //6. 使用 babel 从当前 语法树 ast 中提取并转义成浏览器可运行的 代码 code
         //7. 组装并返回 模块信息对象
        {
            id,
            filename,
            dependencies,
            code
        }
    }


/*
 * 从 entry 开始,提取每个模块的 依赖信息对象,并 构建 依赖图
 */
function createGraph(entry) {
        //1. 提取 入口文件 entry 的依赖信息,为 主资源 mainAsset
        //2. 创建 数组 queue = [mainAsset]
        //3. 遍历 数组 queue 对每个数组项 asset 执行以下操作:
        //    A. 给 asset  增加 属性 mapping = {}
        //    B. 获取 asset 所在的 目录 dirname
        //    C. 遍历 asset 的 dependencies 数组中的 相对路径 relativePath, 执行以下操作:
        //         a. 通过 dirname 获取 relativePath 的 绝对路径 absolutePath
        //         b. 通过 createAsset 提取 absolutePath 对应文件的 模块信息对象 赋值给 child
        //         c. 在 asset 的 mapping 中 增加一个 键值对:relativePath -> child.id
        //         d. 将 child 添加到 queue 尾部。
        //返回 queue
        // 此时 queue 中的 每个模块对象 数据结构为 
        {
            id,
            filename,
            dependencies,
            code,
            mapping
        }
}


    /*
     * 通过 依赖图 进行打包,把所有模块代码和依赖关系打包成一个 大字符串
     */
    function bundle (graph) {
        // 此处的 graph 就是 上一步中的 queue
        //1. 创建 modules = ''
        //2. 遍历 graph 对 其每一项 mod 执行以操作,最终构建一个 字符串,如:
            `1: [
                    function (require, module, exports) {
                        // codes 
                    },
                    {
                        '../lib/an_module.js':  5,
                        '../another_lib/an_module.js': 8
                    }
             ],`
        //3.构建 自运行函数 
            
    }

自运行函数:bootstrap 函数,我把这叫做 “引线函数” ,标准称作 “引导程序”


    `(function (modules) {
        function require(id) {
            const [fn, mapping] = modules[id];
            function localRequire(name) {
                return require(mapping[name]);
            }
            const module = { exports: {} };
            fn(localRequire, module, module.exports);
            return module.exports;
        }
        require(0);
    })({ ${modules} })`;

require(id) 函数的作用是,从之前生成好的 id -> [ function,dependenciesArray ] 字典中,通过参数id找到指定的 模块(函数,及其依赖信息)。 主要功能是:

1. 把id对应的模块中 “向外暴露的公开字段(包括,属性和函数)”提取出来。
2. 并向模块中注入require函数,使模块具有引入依赖的能力,
3. 以此递归执行,从根到达每个树叶,全都执行。

此外,最终包裹成的这个 自执行 函数,中,手动调用了 require(0), id为0的模块是,构造依赖图时,整个程序的 入口 模块。从这里开启整个程序的执行。

对html发展史的一点理解

html (hyper text markup language), 超文本标记语言,它的产生就是作为一个结构化的标记语言,目的是便于机器和人类的阅读和理解,语义化其实在其产生时就包含在其中。后来在浏览器混乱时期,人们期望这种语言能做更多的表现上的功能。就引入了如 FONT, BIG等专为表现,而产生的标签,其不具有良好的语义化。同时,一些样式属性被加入到某些标签中,如 table ,及其属性。为了良好的展示效果,页面几乎丧失了其结构化,语义化的功能,即不能被机器很好的解析,也不能让人理解。页面变成了一坨混乱不堪的标签。css 出现之后,甚至现在,前端开发一度被称为 DIV + CSS ,其实也基本是为了展示效果。语义化也非常不好。HTML5 引入的一些语义化标签,打破了 DIV+CSS 的模式,兼有 CSS 对样式的控制,也有如 header,footer ,nav 等的语义化的优点。

还是那条船吗?

今天,再读«大犀牛»时,在Chapter 3: Types, Values, and Variables这章,文中说到:

They can also be categorized as mutable and immutable types. A value of a mutable type can change. Objects and arrays are mutable: a JavaScript program can change the values of object properties and array elements. Numbers, booleans, null , and undefined are immutable—it doesn’t even make sense to talk about changing the value of a number, for example. Strings can be thought of as arrays of characters, and you might expect them to be mutable. In Java- Script, however, strings are immutable: you can access the text at any index of a string, but JavaScript provides no way to alter the text of an existing string.

曾经,不太明白,也就不太容易记住,再读的时候,忽然想到一个小故事:

如果有一艘大木船,你一块一块的把组成船身的木板都替换成新的木板,那么当把所有木板都替换完之后,这艘船还是原来那艘吗?

我觉得,如果是把船上的不光是木板,包括船的框架都替换了,那么这就不是原来那条船了.如果只是替换了一部分,那么还是那条船.

类比以下,因为程序中的值在计算机里是被存储在内存里的.如果,在保证不把存储值需要的内存不完全写入新的值的情况下,那么这个值还是原来的那个值,就是说这个值虽然发生改变了,但它还是那个值.

如:字符串,如果要改变它,在js中也就是重写了存储这个字符串的所有内存.数字,和布尔值也是这样.

但是,object类型的值,就不一样,因为object是一个值的集合,它拥有指向其字段的所有引用,如果你改变了这些字段的值,或者增加或者删除了一些字段,那么这改变的只是对象拥有的引用,而并没有完全重写这个对象.也就是说,对象类型的值,就是那个没有被完全替换所有东西的那艘木船.

看不明白吧,其实我也不太明白,所以,写不太明白.