10/12
2017

继承结构

Readable

接口

  • _read(n): 产出数据,调用push(chunk or null)产出数据,Readable自带一套buffer的管理。
  • read(n): 当外部调用这个接口时,这个接口优先从buffer里取数据,如果buffer够下一次的read,则直接返回;如果buffer不够,则调用_read(n)要求产生下一段数据chunk

两种模式

Readable其实是有两种模式:flowingpaused

  • flowing: 数据自动从数据源读取,当数据准备好之后,自动的通过EventEmitter接口像外抛出去。
  • paused: stream.read必须显示调用才能吐出数据

一开始是paused模式,可以通过一些方法触发两个模式的切换。

  1. paused切换到flowing:

    1. 添加data event handler.
    2. 调用stream.resume()
    3. 调用stream.pipe()到一个Wriable
  2. flowing切换到paused:

    1. 没有pipe destinations情况下,调用stream.pause()
    2. 如果有pipe destinations情况下,删除所有data事件并且通过unpipe移除所有的pipe destinations
    3. 因为要向后兼容:删除所有的data event hanlders并不会自动切换到paused状态

Note: 只要stream失去了对一个chunk的控制权,就会触发data事件,无论是什么mode下,通过什么方法。

Writable

千万不要陷入误区:Writable只是用于写入,并没有将写入的数据再读出的功能(Transform)A

接口

  • _write: 用来消耗chunk的.(比如打印、写入文件里面等)
  • write: 外部调用接口
    • 当内部所有chunk.length < HWM时,返回true,表示可以继续write
    • 当内部所有chunk.length > HWM是,返回false,表示不能继续write,需要等待drain事件触发之后才能write

Duplex

Duplex仅仅是简单的继承或者实现 Readable & Writable,里面的input数据和ouput数据是否有关系,Duplex本省是不关心的。(input和output有没有关系,需要子类实现_write_read来决定)

Transform

Transform是Duplex的子类,实现了_write_read,ouput的数据是和input的数据有关联的。

the output is computed in some way from the input.

Transform本身不保留数据,只是作为一个过场。

接口

  • _transform: 在_transform里面对chunk进行处理,然后使用readable.push来产出数据

PassThrough

PassThroughTransform的子类,简单的将input的数据ouput出去。

06/07
2017

1. 进程与线程的本质区别:

http://lkml.iu.edu/hypermail/linux/kernel/9608/0191.html

Both threads and processes are really just one thing: a “context of execution”. Trying to artificially distinguish different cases is just self-limiting. A “context of execution”, hereby called COE, is just the conglomerate of all the state of that COE. That state includes things like CPU state (registers etc), MMU state (page mappings), permission state (uid, gid) and various “communication states” (open files, signal handlers etc). Traditionally, the difference between a “thread” and a “process” has been mainly that a threads has CPU state (+ possibly some other minimal state), while all the other context comes from the process.

其实进程和线程在操作系统上都是一个东西,叫做context of execution,进程与线程的区别就是:

  1. 进程:
    • CPU state: registers etc
    • MMU state: page mappings,记录当前进程的虚拟内存地址到物理内存地址的映射的信息。
    • permission state: uid, gid
    • various “communication states”: open files, signal handlers etc
  2. 线程:
    • CPU state: registers
      • possibly some other minial state
    • all others: shared from process

可以看到,进程和线程都是COE,但进程包含所有的信息,包括CPU、内存、分页信息(MMU)等;而线程是包含自己的CPU信息(可以抽象理解为栈信息),内存信息以及地址分页信息等都是共享进程的。

2. 概念:MMU

MMU: 通常是一个CPU附带一个MMU的硬件,应该管理虚拟内存地址到物理内存地址的映射。MMU上面的信息也是当前正在执行的进程的信息一致。

进程:一个程序至少一个进程,在程序初始化的会分配内存空间,包括用户空间里面的代码库、数据块(全局变量、堆内存空间等)、调用栈空间,PCB(process control block) 线程:一个进程至少一个线程,包含调用栈空间、TCB(thread control block),threads exist as subsets of a process

3. 单CPU

线程切换:往往只需要保存老线程的CPU中各个寄存器的值和一些相当少的信息,然后在加载新线程的一些信息到CPU的寄存器中。这些寄存器有:ip、cs、ss、bp、sp等,代表指令地址、调用栈地址等。切换代价较小

进程切换:有很多东西要保存,比如CPU寄存器状态、保存上下文、引起中断等,更重要的是会引起MMU的切页来建立新进程的地址映射,这个代价比价高。

4. 多CPU

多CPU架构中MMU的处理:见https://stackoverflow.com/questions/9929755/do-multi-core-cpus-share-the-mmu-and-page-tables#answer-12347672;可以看到每个CPU中各带有一个MMU,单MMU其实底层是多级TLB来处理的,最终多CPU的MMUs会共同通过DDR3 memory controller来映射内存。

多CPU同一进程多个线程:多线程是共享同一进程的内容的,这种并发上理解并无问题。

多CPU多进程:所有进程的信息事都是保存在内存中的,并且每个CPU上的MMU的也不会重叠,随着每个CPU对应进程的执行,该CPU的MMU映射的地址也多少是该进程的。多CPU的多进程在执行的时候也不会在内存中产生冲突。

5. 其他

linux: - fork、spawn: 新建进程 - pthread_create: 新建线程

node: - child_process: 该模块是新建进程的,是用来管理多进程通信的。node单进程就是单线程架构。

05/11
2017

1. 背景

背景就是写一个h5,大概的结构就是:

--index.html
    -- main.js(一堆js,以main.js做入口)
    -- main.less(一堆less,以main.less做入口)
    -- assets/xxx.jpg|png

要求是:

  1. 将所有的styles打包到一个bundle.css,并计算文件的md5生成[hash].css,自动更新index.html的样式引用
  2. 将所有的js打包到一个bundle.js,并计算文件的md5生成[hash].js,自动更新到index.html的js引用
  3. 将其他所有引用的图片资源等,assets目录下的,计算资源的md5,更新到index.html、main.css的引用中。

    其实也有对更新main.js中assets路径的资源字符串的引用更新的需求,此需求请自写loader,处理这个

  4. 将更新后的index.html也输出

2. 尝试js为entry

main.js为entry,把index.htmlmain.css作为一个引入项加入到main.js中,再使用ExtractTextPlugin去提取index.htmlmain.css,遇到的问题是:

使用ExtractTextPlugin去提取index.html,会无法满足要求1和要求2,更新index.html中的文件的md5路径。原因是这种设计和需求,引出了一个index.htmlmain.js循环依赖的问题,本身程序就无法处理

3. 实现

先看配置项:

{
    entry: [
        './index.html'
    ],
    output: {
        filename: '[name].xxxjs',
        path: path.resolve(__dirname, 'dist'),
        publicPath: ENV_CONFIG.publicPath
    },
    module: {
        rules: [
            {
                test: /index\.html$/,
                use: [
                    {
                        loader: 'file-loader',
                        query: {
                            name: '[name].[ext]'
                        }
                    },
                    'extract-loader',
                    {
                        loader: 'html-loader',
                        query: {
                            attrs: [
                                'img:data-src',
                                'link:href',
                                'script:src'
                            ],
                            minimize: true
                        }
                    },
                ]
            },
            {
                test: /assets/,
                loader: 'file-loader',
                query: {
                    name: '[hash].[ext]'
                }
            },
            {
                test: /\.less$/,
                use: [
                    {
                        loader: 'file-loader',
                        query: {
                            name: '[hash].css'
                        }
                    },
                    'extract-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.js$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[hash].[ext]'
                        }
                    },
                    'uglify-loader',
                    {
                        loader: 'rollup-loader',
                        options: {
                            format: 'iife',
                            external: [],
                            plugins: [
                                RollupPluginNodeResovle(),
                                RollupPluginCommonjs({
                                    include: 'node_modules/**'
                                }),
                                RollupPluginBabel({
                                    exclude: 'node_modules/**',
                                    presets: 'es2015-rollup'
                                })
                            ]
                        }
                    }
                ]
            }
        ]
    },
    resolveLoader: {
        modules: ['lib', 'node_modules']
    }
}

思路:

  1. 如果以index.html为入口,那么就要使用html-loader去解析html,同时配合extract-loader解析index.htmlmain.lessassets/*去触发webpack对lessassets/*的配置module.loader,这样就会完成对less、assets的md5、引用更新的处理。(这是官方推荐)
  2. 但是以相同的思路去处理index.html[script:src](main.js)的时候,就会出错,无论如何也进不了config#module.rule#/.js的匹配项,这个就要通过看源码进行思考,见extract-loader/src/extractLoader#46:

     // If the required file is a JS-file, we just evaluate it with node's require
     // This is necessary because of the css-loader which uses a helper module (css-base.js) to export stuff
     if (/\.js$/i.test(resourcePath)) {
         return require(absPath);
     }
    

    原来extract-loader在遇到js后缀的时候,会直接通过’node#require’进行执行,不走和lessassets同样的逻辑,为什么要这么做,解释里面有,上一步的css-loader会把:

     <link ref="stylesheet" href="main.less">
    

    转换成下面的结果,

     '<link ref="stylesheet" href="' + require('css-loader!main.less') + '">'
    

    可以看到extract-loader要保证css-loader可以正常执行,因此有了这个上面对js的限制。但是css-loader一般只会出现在node_module下,因此可以对extract-loader进行改造,把配置js的正则表达式改成如下,来保证我们自己项目里面的js走config#module#rule的匹配项见pr

     if (/node_modules.*?\.js$/i.test(resourcePath)) {
         return require(absPath);
     }
    

    请自行改动extract-loader,放到自己项目根目录下lib下,(也可以是其他目录,通过config#resolveLoader来解决,优先使用本地的);

  3. 这样就解决了js不经过config#moudle#rule#/.js的匹配项,但是下面又发现,通过extract-loader引起config#module#rule#/.js的配置去解析js,如果只是单纯是使用webpack的默认行为,只使用babel-loader去解析js,就会得到最终输出的bundle.js仅仅只有main.js的内容,猜测可能和webpack处理了js的机制有关,毕竟webpack主要是处理js的。如果解决这种问题的,如果webpack自己的行为无法解决,那么我可以使用其他build-tools的loader来输出一个js bundle,越过webpack处理js的其他过程。比如我这里使用的rollup-loader,也幸亏有这样一个loader。

  4. 整体的流程就这样走通了,每一个rule下面都使用file-loader来处理文件的输出和md5的计算。最终可以使用上面的这些配置来以html为入口,来打包js、less、assets、更改html对文件的引用。

4. 总结

就不做总结了。

05/04
2017

###1. 区别

  • let/const都属于Let and Const Declarations,往execute context的LexicalEnvironment添加identifier,并赋值,它在声明之前是无法访问到的。
  • var属于Variable Statement,往execute context的VariableEnvironment里面添加identifier,并赋值,它的声明是在execute context创建的时候,可以在var statement访问,但是undefined。

通常一个execute context的LexicalEnvironment 和 VariableEnvironment是same value,一次可以经常看到let a = 1; var a = 1;第二个var就会抛错。

###2. Scripts & Modules

  1. scripts的Environment就是global Environment
  2. Modules是Module Environment,它的outer Environment是global Environment。

###3. 疑问之global object

global object是global environment的一个特殊对象,可以用它来访问到global environment上的以下值:

  • 直接在global上面创建的属性
  • Function Declaration in global environment
  • GeneratorDecloaration in global environment
  • VariableDeclaration in global environment
  • some built-ins

Properties may be created directly on a global object. Hence, the object Environment Record component of a global Environment Record may contain both bindings created explicitly by FunctionDeclaration, GeneratorDeclaration, or VariableDeclaration declarations and bindings created implicitly as properties of the global object. - 262 8.1.1.4

这也解释了为什么global environment中执行var statement,可以在global object上访问到,而let 却不可以访问到。

###4. 疑问之const let的效率

js的引擎实现可能还是按照老的var的设计,var的效率较高,可能并没有对let 或者const进行优化,效率可能有些地下。

###5. const 是不是都要优于 let

首先从js标准定义上来讲,若一个primitive type的value或者object的引用之后都不修改,是符合const的条件的,eslint也有prefer-const的规则推荐。

这个问题的争议在于:js引擎中的const是表示值或者引用不可变,而人为理解的const是常量的意思指值、引用、引用指向的对象的值和属性都不可变,并且在代码规范中使用大写字母表示。从这一点上来分析的话,对于值类型的const声明,大家没有争议,争议点在于引用类型(object)的要不要使用const声明,也有人对于primitive type使用const,其他type使用let的。

我的建议是:prefer-const,人为理解不可变值或者对象的使用大写字符表示,因为js引擎中会对const变量有所优化:

const a = require('a');
const CONFIG_A = {
    b: 1,
    c: 2
};
const PI = 3.14;
for (const item of [{}, {}]) {
    // xx
}
for (let i = 0; i < arr.length; i++ ) {
}

像其他的语言:

  • java: 阿里的java开发准则也是推荐任何地方使用final的。
  • c#:const 和 readonly, const是编译时常量,c#只允许值类型的const,在编译的时候,直接拿const的值替换使用这些变量的地方;readonly是运行时常量
  • c++: c++的情况和c#差不多,但const更复杂一些,可以编译时会对值类型的const type做值替换,但对于指针这些const不知道怎么处理的。
04/07
2017

目录

  1. 介绍
  2. instrument
  3. cover
  4. node vs browser