Songqian Li's Blog

去历史上留点故事

Promise

Promise 最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Promise((resolve, reject) => {
console.log("初始化");

resolve();
})
.then(() => {
throw new Error("有哪里不对了");
console.log("执行「这个」”");
})
.catch(() => {
console.log("执行「那个」");
})
.then(() => {
log("执行「这个」,无论前面发生了什么");
});

闭包

函数可以访问由函数内部定义的变量。

JS 有多态吗

多态:同一个操作作用于不同的对象上,可以产生不同的解释和不同的执行结果。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var makeSound = function (animal) {
animal.sound();
};

var Duck = function () {};
Duck.prototype.sound = function () {
console.log("嘎嘎嘎");
};
var Chiken = function () {};
Chiken.prototype.sound = function () {
console.log("咯咯咯");
};

makeSound(new Chicken());
makeSound(new Duck());

一个进程能创建多少个线程

Linux 下通过ulimit -u可以查看系统中的进程上限,通过ulimit -s查看线程的调用栈大小。假设一个进程的虚拟内存是 4G,在 Linux32 位平台下,内核分走了 1G,留给用户用的只有 3G,于是我们可以想到,创建一个线程占有了 10M 内存,总共有 3G 内存可以使用。于是可想而知,最多可以创建差不多 300 个左右的线程。因此,进程最多可以创建的线程数是根据分配给调用栈的大小,以及操作系统(32 位和 64 位不同)共同决定的。
Windows 下默认情况下,一个线程的栈要预留 1M 的内存空间 ,而一个进程中可用的内存空间只有 2G,所以理论上一个进程中最多可以开2048 个线程

三栏布局的实现方式,尽可能多写

三列布局又分为两种,两列定宽一列自适应,以及两侧定宽中间自适应
两列定宽一列自适应:
1、使用 float+margin:
给 div 设置 float:left,left 的 div 添加属性 margin-right:left 和 center 的间隔 px,right 的 div 添加属性 margin-left:left 和 center 的宽度之和加上间隔
2、使用 float+overflow:
给 div 设置 float:left,再给 right 的 div 设置 overflow:hidden。这样子两个盒子浮动,另一个盒子触发 bfc 达到自适应
3、使用 position:
父级 div 设置 position:relative,三个子级 div 设置 position:absolute,这个要计算好盒子的宽度和间隔去设置位置,兼容性比较好,
4、使用 table 实现:
父级 div 设置 display:table,设置 border-spacing:10px//设置间距,取值随意,子级 div 设置 display:table-cell,这种方法兼容性好,适用于高度宽度未知的情况,但是 margin 失效,设计间隔比较麻烦,
5、flex 实现:
parent 的 div 设置 display:flex;left 和 center 的 div 设置 margin-right;然后 right 的 div 设置 flex:1;这样子 right 自适应,但是 flex 的兼容性不好
6、grid 实现:
parent 的 div 设置 display:grid,设置 grid-template-columns 属性,固定第一列第二列宽度,第三列 auto,
对于两侧定宽中间自适应的布局,对于这种布局需要把 center 放在前面,可以采用双飞翼布局:圣杯布局,来实现,也可以使用上述方法中的 grid,table,flex,position 实现

类的继承

  1. 原型链继承
1
2
3
4
5
6
7
8
9
10
function Cat() {}
Cat.prototype = new Animal();
Cat.prototype.name = "cat";
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat("fish"));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

介绍:在这里我们可以看到 new 了一个空对象,这个空对象指向 Animal 并且 Cat.prototype 指向了这个空对象,这种就是基于原型链的继承。
特点:基于原型链,既是父类的实例,也是子类的实例
缺点:无法实现多继承

  1. 构造继承:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
1
2
3
4
5
6
7
8
9
10
function Cat(name) {
Animal.call(this);
this.name = name || "Tom";
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:可以实现多继承
缺点:只能继承父类实例的属性和方法,不能继承原型上的属性和方法。

  1. 实例继承和拷贝继承

实例继承:为父类实例添加新特性,作为子类实例返回
拷贝继承:拷贝父类元素上的属性和方法
上述两个实用性不强,不一一举例。

  1. 组合继承:相当于构造继承和原型链继承的组合体。通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
1
2
3
4
5
6
7
8
9
10
11
12
function Cat(name) {
Animal.call(this);
this.name = name || "Tom";
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

特点:可以继承实例属性/方法,也可以继承原型属性/方法
缺点:调用了两次父类构造函数,生成了两份实例

  1. 寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Cat(name) {
Animal.call(this);
this.name = name || "Tom";
}
(function () {
// 创建一个没有实例方法的类
var Super = function () {};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true

较为推荐

输出程序结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
async1();
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");

执行过程:

  1. 输出”script start”(毋庸置疑)
  2. setTimeout加入宏任务队列
  3. 执行async1()函数:输出”async1 start”,运行到await时,async2()立即执行,输出”async2”,然后将当前任务阻塞并加入到微任务队列中,继续执行其他代码;
  4. 执行Promise,输出”promise1”,并将.then()方法加入到微任务队列
  5. 输出”script end”,当前微任务结束,依次执行当前宏任务中包含的微任务;
  6. 输出”async1 end”
  7. 输出”promise2”,当前宏任务的所有微任务执行结束,执行下一个宏任务;
  8. 输出”setTimeout”,宏任务队列为空,执行结束。

输出结果:

1
2
3
4
5
6
7
8
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

setTimeout就是作为宏任务来存在的,而Promise.then则是具有代表性的微任务

相关文章
评论
分享
  • 《操作系统真象还原》:第十章 输入输出系统

    上一章中我们遇到的字符混乱和 GP 异常问题,根本原因是由于临界区代码的资源竞争,这需要一些互斥的方法来保证操作的原子性。 10.1 同步机制——锁 10.1.1 排查 GP 异常,理解原子操作 多线程执行刷屏时光标值越界导致...

    《操作系统真象还原》:第十章 输入输出系统
  • 《操作系统真象还原》:第九章 线程

    线程和进程将分两部分实现,本章先讲解线程。 9.1 实现内核线程 9.1.1 执行流 在处理器数量不变的情况下,多任务操作系统采用多道程序设计的方式,使处理器在所有任务之间来回切换,这称为“伪并行”,由操作系统中的任务调度器决定当...

    《操作系统真象还原》:第九章 线程
  • GPU虚拟化

    用户层虚拟化 本地 API 拦截和 API formwarding 在用户态实现一个函数库,假设叫 libwrapper, 它要实现底层库的所有 API; 让 APP 调用这个 libwrapper。如何做? libwrap...

    GPU虚拟化
  • 硬件虚拟化

    硬件虚拟化介绍 硬件虚拟化要做的事情 体系结构支持 体系结构 实现功能 作用 模式切换 Host CPU <-> Guest CPU 切换 CPU 资源隔离 二阶段地址转换 GVA-> GPA...

    硬件虚拟化
  • 《操作系统真象还原》:第八章 内存管理系统

    8.1 makefile 简介 这部分可参考阮一峰的讲解:https://www.ruanyifeng.com/blog/2015/02/make.html 8.1.1 makefile 是什么 makefile 是 Linu...

    《操作系统真象还原》:第八章 内存管理系统
  • 《操作系统真象还原》:第七章 中断

    7.1 中断是什么,为什么要有中断 运用中断能够显著提升并发,从而大幅提升效率。 7.2 操作系统是中断驱动的 略 7.3 中断分类 把中断按事件来源分类,来自 CPU 外部的中断就称为外部中断,来自 CPU 内部的中断称为内部...

    《操作系统真象还原》:第七章 中断
  • 《操作系统真象还原》:第六章 完善内核

    6.1 函数调用约定简介 咱们实验使用cdecl。这里提一下stdcall,cdecl与stdcall的区别在于由谁来回收栈空间。 stdcall是被调用者清理参数所占的栈空间。 举例来说: 12int subtract(int ...

    《操作系统真象还原》:第六章 完善内核
  • 《操作系统真象还原》:第五章 保护模式进阶——加载内核

    5.3 加载内核 5.3.1 用 C 语言写内核 第一个 C 语言代码: 1234int main(void) { while(1); return 0;} 这个内核文件什么都没做,通过while(1)这个死循...

    《操作系统真象还原》:第五章 保护模式进阶——加载内核
  • 《操作系统真象还原》:第五章 保护模式进阶——内存分页机制

    从这一刻起,我们才算开始了真正的操作系统学习之旅 5.1 获取物理内存容量 5.1.1 Linux 获取内存的方法 在 Linux 2.6 内核总是用detect_memory函数来获取内存容量的。其函数本质上是通过调用 BI...

    《操作系统真象还原》:第五章 保护模式进阶——内存分页机制
  • 《操作系统真象还原》:第四章 保护模式入门

    4.1 保护模式概述 在本章大家会见到全局描述符表、中断描述符表、各种门结构,这是 CPU 提供给应用的,咱们用好就行。 保护模式强调的是“保护”,它是在 Intel 80286 CPU 中首次出现,这是继 8086 之后,Inte...

    《操作系统真象还原》:第四章 保护模式入门