- ECMAScript 6 语法
- JavaScript 运行机制
- Docker 基本概念及使用
- Bootstrap 使用
- Java 并发基本知识
-
-
Save chinaHewei/77dac985cbf4394f1307288c316bda99 to your computer and use it in GitHub Desktop.
学习资料:
- 镜像 (
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
代码的之前,会创建一个execution context
,其中包含了scope chain
、variable object
和this
,在JavaScript Execution Stack
这篇笔记中知道了variable object
是如何创建的以及其中包含了什么东西,在JavaScript Scope Chain 中的标识符解析和闭包
这篇笔记中知道了scope chain
是什么,以及它包含了什么值,那么this
的值是如何决定的呢?
在 javascript
中 this
的值在不同的情况下可能不一样,大概有以下几种情况:
// 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
绑定到什么)
- JavaScript’s ‘this’ Keyword
- JavaScript中的this关键字 译
- 揭秘JavaScript中谜一样的this 译
- 你不知道的 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 运行的时候,它所处在的上下文很重要,代码可以评估为为以下一项
- 全局代码:代码第一次被执行的时候默认的环境
- 函数代码:当执行流程进入一个函数体
- 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
的值
Identifier Resolution and Closures in the JavaScript Scope Chain
- Identifier Resolution:标识符解析
- Closures:闭包
在 JavaScript Execution Stack
这篇笔记中,可以知道每一个函数在执行的时候就会关联一个 execution context
,这个对象包含了一个 variable object [VO]
。 variable object [VO]
这个对象包含了传入函数的参数、函数中的变量声明和函数声明。
每一个 execution context
的 Scope Chain
简单来说就是当前 context
的 [VO]
和 parent’s lexical [VO]
s 的集合
Scope = VO + All Parent VOs
Eg: scopeChain = [ [VO] + [VO1] + [VO2] + [VO n+1] ]
在 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
,于是输出。
JavaScript 本质上是基于原型的,除了 null
和 undefined
之外,都是 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
属性,于是去它的原型链中寻找,找到就输出了。