Skip to content

Instantly share code, notes, and snippets.

@chinaHewei
Last active January 6, 2018 13:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chinaHewei/77dac985cbf4394f1307288c316bda99 to your computer and use it in GitHub Desktop.
Save chinaHewei/77dac985cbf4394f1307288c316bda99 to your computer and use it in GitHub Desktop.
2018 年一月份学习计划

Docker 基础知识

学习资料:

概念

  • 镜像 (image)
  • 容器 (container)
  • 镜像仓库 (registry)
  • 数据卷 (volumn)

镜像

docker 镜像是一个特殊的文件系统,提供容器运行时所需要的程序、库、资源、配置和配置参数。镜像不包含任何动态数据,镜像的内容在构建之后不会在改变

常用命令:

# 寻找镜像: docker search <name>
$ docker search nginx

# 获取镜像: docker pull [option] [docker registry address[:port]] 仓库名[:tag]
$ docker pull ubuntu:16.04

# 列举本地镜像: docker image ls
$ docker image ls

# 删除本地镜像: docker image rm [option] <image> [<image> <image> ...]
$ docker image rm ubuntu:16.04
$ docker image rm 3f8a4339aadd   # 3f8a4339aadd 为 image id

容器

镜像是静态的定义,容器是镜像运行时的实体(相当于类和对象)。容易可以被创建、启动、停止、删除、暂停等等

容器的实质是进程,但与普通的进程不一样的是,它有自己的命名空间。正因为这样,容器可以有自己的 root 文件系统、网络配置和进程空间。容器内的进程运行在一个隔离的环境里,使用起来,就像一个独立于宿主一样。

容器在运行时,以镜像为基础,在其之上创建一个当前容器的存储层,用来为容器运行时读写做准备。这一层(容器的存储层)的生命周期和容器的不一样,在容器消亡(停止)的时候,容器的存储层随之消失(即不会持久化)。因为这样,在 docker 的最佳实践中,容器不应该向其存储层内写入任何数据,容器的存储层要保持无状态,所有文件的写入操作,应该使用数据卷、或者绑定宿主目录。

启动容器的的两种方式:

  • 基于image新建一个container启动
  • 将一个处在终止状态(stopped)的container启动

常用命令:

查看容器

# 查看当前正在运行的容器
$ docker container ls
# 查看所有的容器
$ docker container ls -a

删除容器

# 删除没有运行的指定容器
$ docker container rm <container_name>
# 删除正在运行的指定容器
$ docker container rm -f <container_name>
# 删除所有处于终止状态的容器
$ docker container prune

创建容器

运行指定命令后停止容器

# 新建并启动
$ docker run ubuntu:latest /bin/echo 'hello world'
hello world
$ docker container ls -a

# 启动已经存在的容器
$ docker container start <container-id>

使用-t-i选项创建一个 bash 终端,进行交互式命令

# 通过镜像创建一个容器并进入容器
$ docker run -t -i ubuntu:latest /bin/bash
root@ddd76b02775c:/# exit
# 查看所有容器
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED .....
ddd76b02775c        ubuntu:latest       "/bin/bash"         2 minutes ago ....
# 启动刚刚创建的容器
$ docker container start ddd76b02775c
ddd76b02775c
# 使用 attach 命令进入刚刚创建的容器
$ docker attach ddd76b02775c
root@ddd76b02775c:/#

使用 -d 参数让容器在后台运行

# 不后台运行
$ docker run -p 80:80 nginx
# 后台运行,返回容器的长id
$ docker run -d -p 80:80 nginx
78cb50390c9b895a6f1e1e9fbe969734acf9368678261ddb612d3e3d0ab81fd8
# 查看容器的内部输出
$ docker container logs 78cb50390c9b895a6f1e1e9fbe969734acf9368678261ddb612d3e3d0ab81fd8

停止、重新启动指定容器

$ docker container ls
CONTAINER ID        IMAGE   .....
78cb50390c9b        nginx   .....
$ docker container stop 78cb50390c9b
78cb50390c9b
$ docker container start 78cb50390c9b
78cb50390c9b
$ docker container restart 78cb50390c9b
78cb50390c9b

进入容器

推荐使用docker exec -it

$ docker attach <docker-id>
$ docker exec -it <docker-id>

导入和导出容器

$ docker export 78cb50390c9b > nginx.tar
$ docker import ...

仓库

存放镜像的地方

docker registry 中包含多个仓库 Repository,每个仓库包含多个标签 Tag

通过 <仓库名>:<标签> 来获取指定版本的镜像,如果不写标签的话,默认为 latest

数据卷

需要注意的问题

  • Docker 镜像应该尽量精简(避免多次继承)
  • 合理选择基础镜像(越简洁越好,轻量级)

JavaScript 中的 this 关键字

在执行 javascript 代码的之前,会创建一个execution context,其中包含了scope chainvariable objectthis,在JavaScript Execution Stack这篇笔记中知道了variable object是如何创建的以及其中包含了什么东西,在JavaScript Scope Chain 中的标识符解析和闭包这篇笔记中知道了scope chain是什么,以及它包含了什么值,那么this的值是如何决定的呢?

this 是如何决定的

javascriptthis 的值在不同的情况下可能不一样,大概有以下几种情况:

全局上下文(非函数)中使用

// global context
this.a = 'global'
console.log(a)

在全局上下文中使用(非函数中),this的值会一直是全局上下文

在函数中使用

首先,函数内部this的值不是静态的,在每一次函数调用的时候,this的值会被重新决定(这个和每一次函数调用都会创建execution context正好是符合的)。那和决定this的规则是怎样的呢?

函数内部的this值实际由函数被调用的父作用域提供,更重要的是,依赖实际函数的语法。

  • 当函数被调用时,我们看紧邻括号“()”的左边。如果在括号的左侧存在一个引用,传递给调用函数的“this”值是引用属于的对象,否则this的值将是全局对象。(如果一个函数被作为一个对象的方法调用,那么this将被指派为这个对象
var anum = 0;

var foo = {
    anum: 10,
    baz: {
        anum: 20,
        bar: function() {
            console.log(this.anum);
        }
    }
}
foo.baz.bar(); // 20 - 因为()的左边是bar,而它被调用时属于baz对象
// <- foo.baz

// 注意这种行为非常“脆弱”,如果你获取一个方法的引用并且调用它
// 那么this的值不会是parent了,而是window全局对象
var hello = foo.baz.bar;
hello(); // 0 - 因为()的左边是hello,而它被调用时属于global对象
// <- global

从调用链中查看被调用函数是一个对象的属性还是它自己,这样就容易分清楚了

  • 被当作构造函数的情况下,当使用new关键字时,this将被指派为被创建的实例对象
function ThisClownCar () {
  console.log(this);
}

new ThisClownCar();
// <- ThisClownCar {}
  • 事件处理程序的 this 总是引用触发事件的元素
  • this 的值也可以通过.call.apply或者.bind手动设置
hello.call(foo) // 10

总结

在看完《你不知道的 JavaScript 上卷》(KYLE SIMPSON 著)中的第二部分关于this的部分后,他总结的更好,于是将规则在添加过来,不过整体和上面的笔记还是一样的。

应用下面的四条规则来判断this的绑定对象

  • new调用?绑定到新创建的对象
  • call或者apply(或者bind)调用?绑定到指定的对象
  • 由上下文对象调用?绑定到那个上下文对象
  • 默认:在严格模式下绑定到undefined,否则绑定到全局对象

ES6的箭头函数(()=>{})并不会使用上面👆这四条规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)

links

JavaScript 事件循环

学习资源

重要概念

macro-task: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering

micro-task: process.nextTick, Promises, Object.observe, MutationObserver

事件循环的顺序是从script开始第一次循环,随后全局上下文进入函数调用栈

碰到 macro-task 就将其交给处理它的模块处理完之后将回调函数放进 macro-task 的队列之中

碰到 micro-task 也是将其回调函数放进 micro-task 的队列之中

直到函数调用栈清空只剩全局执行上下文,然后开始执行所有的micro-task

当所有可执行的 micro-task 执行完毕之后,循环再次执行 macro-task 中的一个任务队列

执行完之后再执行所有的micro-task

就这样一直循环

javascript 执行上下文

原文链接🔗,以下为个人 的学习笔记和理解

执行上下文

当代码在 javascript 运行的时候,它所处在的上下文很重要,代码可以评估为为以下一项

  • 全局代码:代码第一次被执行的时候默认的环境
  • 函数代码:当执行流程进入一个函数体
  • Eval Code:在 internal eval function 中被执行的文本

只有一个全局上下文,可以有任意个函数上下文(没调用一个函数都会生成一个上下文)

执行栈

浏览器中的JavaScript解释器是单线程的。这就意味着在浏览器中一次只能发生一件事,其他的行为或事件会在执行堆栈中排队

关于执行栈需要记住以下几个关键的点:

  • 单线程执行
  • 同步执行
  • 一个全局上下文
  • 多个函数(局部)上下文(Infinite function contexts.)
  • 每一个函数被调用都会创建其执行的函数上下文,包括递归调用

执行上下文细节

  • 创建阶段(函数被调用,但是在执行其内部代码之前)
    • 创建作用域链(scope chain)
    • 创建变量、函数和参数(variable object)
    • 决定this的值
  • 激活(代码执行)阶段
    • 分配值、应用函数和解释(执行)代码

创建变量、函数和参数过程的伪代码

  • 找到调用函数的代码
  • 在执行函数之前,创建 execution context
  • 进入第一阶段(creation stage
    • 初始化 Scope Chain
    • 创建 variable object
      • 创建 arguments 对象,检查参数的上下文,初始化名称和值,并创建一个引用副本
      • 扫描上下文(被调用函数)中的函数声明
        • 每找到一个函数声明,就在 variable object 增加一个属性,属性的名字为函数名字,属性的值为函数的引用
        • 如果 variable object 中的属性已经存在,则覆盖
      • 扫描上下文(被调用函数)中的变量声明
        • 每找到一个变量声明,就在 variable object 增加一个属性,属性的名字为变量名字,属性的值初始化为undefined
        • 如果 variable object 中的属性已经存在,啥也不做直接跳过
  • 决定 execution context 中的 this 的值

JavaScript Scope Chain 中的标识符解析和闭包

links

Identifier Resolution and Closures in the JavaScript Scope Chain

基本

  • Identifier Resolution:标识符解析
  • Closures:闭包

JavaScript Execution Stack 这篇笔记中,可以知道每一个函数在执行的时候就会关联一个 execution context,这个对象包含了一个 variable object [VO]variable object [VO] 这个对象包含了传入函数的参数、函数中的变量声明和函数声明。

每一个 execution contextScope Chain 简单来说就是当前 context[VO]parent’s lexical [VO]s 的集合

Scope = VO + All Parent VOs
Eg: scopeChain = [ [VO] + [VO1] + [VO2] + [VO n+1] ]

决定 Scope Chain 中的 [VO]

Scope Chain 中的第一个 [VO] 属于当前的 execution context,我们可以在 Scope Chain 找到当前 execution context 的父作用域中的 [VO]

function one() {
    var a = 1
    two()
    function two() {
        var b = 2
        three()
        function three() {
            var c = 3
            console.log('I am at function three')
            console.log(a + b + c)
        }
    }
}
one()

但执行到 three() 内部的时候, 此时栈顶对应的 Scope Chain 为:

three() Scope Chain = [ [three() VO] + [two() VO] + [one() VO] + [Global VO] ];

👆结合上面的知识和 execution context 中的 VO 创建过程来分析闭包是如何形成的:

原始代码:

function foo() {
  var a = 'private variable'
  return function bar() {
      console.log(a)
  }
}

var getVarA = foo()
getVarA()

execution context 大致是这样的

// global execution context
global = {
    scopeChain: [global.VO]
    VO: {
        foo: pointer to foo(),
        getVarA: return value of  global.VO.foo
    },
    this: //...
}

// foo execution context
foo = {
    scopeChain: [foo.VO, gobal.VO]
    VO: {
        bar: pointer to bar(),
        a: 'private variable'
    }
    this: //...
}

// bar execution context
bar = {
    scopeChain: [bar.VO, foo.VO, global.VO],
    VO: {},
    this: //
}

但我们调用 getVarA() 的时候,实际是调用 foo() 返回的 bar() 函数,bar() 函数的 execution context 中的 scopeChain[bar.VO, foo.VO, global.VO]

现在要输出a的话,首先从bar.VO中找,没有找到,所以就去下一个VO中找,在foo.VO中成功找到了a,于是输出。

原型链和作用域链 (prototype chain and scope chain)

JavaScript 本质上是基于原型的,除了 nullundefined 之外,都是 object。当我们试图访问对象的属性时,解释器将尝试通过查找对象中属性的存在来解决它。 如果它找不到属性,它将继续查找原型链,这是一个继承的对象链,直到它找到属性,或者遍历到链的末尾。

这导致了一个有趣的问题,解释器是否首先使用作用域链还是原型链来解析对象属性? 它使用两个。 试图解析属性或标识符时,将首先使用作用域链来查找对象。 一旦找到对象,就会遍历该对象的原型链,查找属性名称

var prototype = {
  b: 'prototype'
}
var bar = Object.create(prototype)
function foo() {
  bar.a = 'Set from foo()'
  bar.c = 'Set from foo()'
  return function inner() {
    console.log(bar.a)
    console.log(bar.b)
    console.log(bar.c)
  }
}
foo()()

在运行的时候,当我们调用 inner() 的时候,我们要输出bar.a的时候,通过作用域链我们找到了bar(在global.VO 中找到了 bar),于是在 bar 中寻找属性 a 发现,bar 对象本身就有这个属性,于是直接返回并输出;当我们要输出bar.b的时候,还是通过作用域链找到了bar对象,但是在bar对象自身没有找到b属性,于是去它的原型链中寻找,找到就输出了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment