0%

本文主要记录 require 升级为 import 遇到的问题

__dirname is not defined in ES module scope

1
2
3
4
5
6
7
8
9
10
11
12
import path from 'path';
import {fileURLToPath} from 'url';

const __filename = fileURLToPath(import.meta.url);

// 👇️ "/home/john/Desktop/javascript"
const __dirname = path.dirname(__filename);
console.log('directory-name 👉️', __dirname);

// 👇️ "/home/borislav/Desktop/javascript/dist/index.html"
console.log(path.join(__dirname, '/dist', 'index.html'));

import json file

1
2
import json from "./foo.json" assert { type: "json" };
import("foo.json", { assert: { type: "json" } });

import 条件导入

1
2
3
4
if (condition) { // 报错
import moduleA from './moduleA';
}

import 函数

1
2
3
4
5
import('./moduleA .js')
.then(moduleA => {
console.log(moduleA);
});

1
2
3
4
if(condition){
import('moduleA').then(...);
}

代码如何执行

使用高级语言编写的代码需要先编译为机器码(二进制代码)再执行

执行方式有如下两种

编译执行

代码 =》 解析器 =》 中间代码 =》 编译器 =》 机器码 =》 执行结果

把代码一次性编译为机器码,然后执行

解释执行

代码 =》 解析器 =》 中间代码 =》 解释器 =》 执行结果

编译一行代码,执行一行代码,一边编译一边执行

为何需要虚拟机

java 和 javascript 等语言需要虚拟机,是为了跨平台,抹平平台差异。

为何需要中间代码

中间代码一般为字节码

因为把源码编译为机器码占用太多空间,热代码需要缓存下来,避免重复编译。而使用字节码可以节省空间。

当然字节码还能提升代码启动速度、降低代码负责度

V8是如何执行一段JavaScript代码的?

字节码(一):V8为什么又重新引入字节码?

重写 fetch 和 XHR

原理

重写 fetch 和 XHR,返回自定义数据。
把重写 fetch 和 XHR 的代码通过 <script></script> 标签 插入到 html 里

问题

当你插入 <script></script> 标签的时候,请求可能已经发出了,所以此种方法不能保障拦截所有请求

tweak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
var startTime = new Date().getTime();
var count = 0;
setInterval(function(){
var i = 0;
while(i++ < 100000000);
}, 0);
function fixed() {
count++;
var offset = new Date().getTime() - (startTime + count * 1000);
var nextTime = 1000 - offset;
if (nextTime < 0) nextTime = 0;
setTimeout(fixed, nextTime);

console.log(new Date().getTime() - (startTime + count * 1000));
}
setTimeout(fixed, 1000);

参考

不会被 iOS 停掉的网页定时器

postMessage 可用于解决以下方面的问题:

  1. 页面和其打开的新窗口的数据传递
  2. 页面与嵌套的 iframe 消息传递
  3. 多窗口之间消息传递

发送

1
otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow

其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames。

message

要发送的数据。它将会被结构化克隆算法序列化,所以无需自己序列化(部分低版本浏览器只支持字符串,所以发送的数据最好用JSON.stringify() 序列化)。

targetOrigin

通过 targetOrigin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串“*”(表示无限制)或者一个 URI(如果要指定和当前窗口同源的话可设置为”/“)。在发送消息的时候,如果目标窗口的协议、主机地址或端口号这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会发送。

接收

1
2
3
4
5
6
7
window.addEventListener("message", (event)=>{
var origin = event.origin;
// 通常,onmessage()事件处理程序应当首先检测其中的origin属性,忽略来自未知源的消息
if (origin !== "http://example.org:8080")
return;
// ...
}, false);

event 的属性有:

  • data: 从其他 window 传递过来的数据副本。
  • origin: 调用 postMessage 时,消息发送窗口的 origin。例如:“http://example.com:8080”。
  • source: 对发送消息的窗口对象的引用。可以使用此来在具有不同 origin 的两个窗口之间建立双向数据通信。

————————————————
版权声明:本文为CSDN博主「huangpb0624」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/huangpb123/article/details/83692019

最近看光哥的文章,说 Express 是洋葱模型。我记得 Koa 是洋葱模型,而且这是其与 Express 不同的地方之一。
于是搜索引擎搜索了一通,只有说 Koa 是洋葱模型的,没有说 Express 是洋葱模型的。
而且一般都会提供下面的代码证明 Koa 是洋葱模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// koa-app.js

const Koa = require('koa2');
const app = new Koa();

app.use(async (ctx, next) => {
console.log('第一层洋葱 - 开始')
await next();
console.log('第一层洋葱 - 结束')
});

app.use(async (ctx, next) => {
console.log('第二层洋葱 - 开始')
await next();
console.log('第二层洋葱 - 结束')
});

app.use(async ctx => {
console.log('第三层洋葱 - 开始')
ctx.body = 'Hello World';
console.log('第三层洋葱 - 结束')
});

app.listen(3000)

控制台打印如下

1
2
3
4
5
6
第一层洋葱 - 开始
第二层洋葱 - 开始
第三层洋葱 - 开始
第三层洋葱 - 结束
第二层洋葱 - 结束
第一层洋葱 - 结束

看样子真像那么回事

于是我用 Express 试了一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var express = require("express");
var app = express();

app.use((req, res, next) => {
console.log('第一层洋葱 - 开始');
next();
console.log('第一层洋葱 - 结束');
});
app.use((req, res, next) => {
console.log(' 第二层洋葱 - 开始');
next();
console.log(' 第二层洋葱 - 结束');
});
app.use((req, res, next) => {
console.log(' 第三层洋葱 - 开始');
next();
console.log(' 第三层洋葱 - 结束');
});

app.listen(3000);

控制台打印和 koa 的例子是一样的
什么,难道 Express 也是洋葱模型

其实,以上例子并不能说明 Koa 是洋葱模型,洋葱模型的关键是,在请求返回之前,你的请求是不是经过中间件两次,而且按照先进后出的顺序。

Koa 模型
进洋葱 -> 出洋葱 -> 返回请求
Express 模型
进洋葱 -> 返回请求 -> 出洋葱

以下代码才真正能体现 Koa 的洋葱模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const Koa = require("koa2");
const app = new Koa();
const indent = (n) => new Array(n).join("&nbsp;");
app.use(async (ctx, next) => {
ctx.body = "<h3>第一层洋葱 - 开始</h3>";
await next();
ctx.body += "<h3>第一层洋葱 - 结束</h3>";
});

app.use(async (ctx, next) => {
ctx.body += `<h3>${indent(4)}第二层洋葱 - 开始</h3>`;
await next();
ctx.body += `<h3>${indent(4)}第二层洋葱 - 结束</h3>`;
});

app.use(async (ctx, next) => {
ctx.body += `<h3>${indent(8)}第三层洋葱 - 开始</h3>`;
next();
ctx.body += `<h3>${indent(8)}第三层洋葱 - 结束</h3>`;
});
app.use((ctx) => {
ctx.body += `<h3>${indent(12)}koa 核心业务</h3>`;
});

app.listen(3000);

浏览器显示结果

1
2
3
4
5
6
7
第一层洋葱 - 开始
第二层洋葱 - 开始
第三层洋葱 - 开始
koa 核心业务
第三层洋葱 - 结束
第二层洋葱 - 结束
第一层洋葱 - 结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var express = require("express");
var app = express();

const indent = (n) => new Array(n).join("&nbsp;");

app.use((req, res, next) => {
res.body = "<h3>第一层洋葱 - 开始</h3>";
next();
res.body += "<h3>第一层洋葱 - 结束</h3>";
});
app.use((req, res, next) => {
res.body += `<h3>${indent(4)}第二层洋葱 - 开始</h3>`;
next();
res.body += `<h3>${indent(4)}第二层洋葱 - 结束</h3>`;
});
app.use((req, res, next) => {
res.body += `<h3>${indent(8)}第三层洋葱 - 开始</h3>`;
next();
res.body += `<h3>${indent(8)}第三层洋葱 - 结束</h3>`;
});
app.use((req, res) => {
res.body += `<h3>${indent(12)}我的核心业务</h3>`;
res.send(res.body);
});
app.listen(3000);

浏览器显示

1
2
3
4
第一层洋葱 - 开始
第二层洋葱 - 开始
第三层洋葱 - 开始
我的核心业务

小数的表示分为「定点法」和「浮点法」

定点法

其实,整数和小数的主要区别,我们可以不规范地理解为是否存在小数点,对吧?(其实整数也是有小数点的,我们这里暂且这么理解)那么要表示小数,只需要在整数表示的基础上,加一个小数点,不就可以了吗?没错,这就是定点数的思路。

​定点数使用二进制,小数点的位置是事先约定好的,在使用的过程中不能改变,这也是定点数的名称的由来。有的同学可能会问了,那么,小数如何从十进制换成定点数呢?不着急,且看下面的例子:

其实很简单,和整数部分的完全相反。连续将小数部分的值乘以2,对于每一次得到的数,若整数为0,则顺序记下一个0,若整数突破了1,则记下1,然后整数部分重置为0,小数部分继续重复上述操作,直到小数部分为0为止。就像下面这样:

十进制的0.125

0.125*2=0.25 (记下0)

0.25*2=0.5 (记下0,则目前为00)

0.5*2=1.0 (整数部分为1,记下1,则目前为001)

小数部分为0,转换结束(这里是刚好结束,若整数位进一后小数部分还有剩余,则把整数部分变成0再继续重复上面的算法)

则十进制0.125的二进制形式为0.001

二进制转十进制

二进制 101.011 转化为十进制



一般在现在计算机的应用中,定点数一般只有两种情况,第一种是小数点在符号位之后,也就是表示纯小数,第二种是在最后,也就是表示整数。这种表示方法的范围并不大,如果需要表示类似于这样的数值的话,那么用定点数的话就会占很大的空间。(毕竟你要很多很多个0才行,对吧)于是乎,我们的先人又开始秀智商了。

浮点法

表示一组数,0.123,1.23,12.3,123

如果此时你用定点数来表示的话,你会发现,这四组数字的表示方法完全不同。但是,我们的数学经验却又告诉我们,这几个数字其实是可以用一种通式来表示的,你应该已经想到了,就是科学计数法。如果使用科学计数法,那么这几组数字的通式可以表示为。貌似比什么定点数方便多了。

但是,我们知道,计算机是用来处理二进制的,那么,我们在二进制里面,可不可以打造一套类似于科学计数法的思路来表示小数呢——这就是浮点数的基本思路。浮点数的表示方法有点类似科学计数法,但是又比科学计数法要复杂。

参考:
https://blog.csdn.net/jarvan5/article/details/115012434
https://www.jianshu.com/p/104f53c663c9

题记

  1. 1 px 问题是如何产生的?
  2. 为什么会有 二倍图,三倍图?
  3. 苹果公司所说的 Retina 屏是啥?
  4. 屏幕清晰度由哪个参数决定,分辨率越高就一定越清晰吗?
  5. 屏幕尺寸相同可以通过分辨率判断屏幕清晰度,屏幕尺寸不同,如何判断哪块屏幕更清晰?

pt

物理单位
1 pt = 1/72 inch
1 inch = 72 pt
72 pt = 96 px
绝对长度单位,多用于打印

inch

1 inch = 2.54 cm

px

1px 问题
获取设备像素比
js
window.devicePixelRatio
css
-webkit-min-device-pixel-ratio

dpr (devicePixelRatio)

设备像素比
dpi 和 dpr
window.devicePixelRatio是设备物理像素和设备独立像素(device-independent pixels,dips)之间的比率。是一个约数?
window.devicePixelRatio = 物理像素 / 设备独立像素

DIP (Device Independent Pixel)

设备独立像素(Device Independent Pixel):与设备无关的逻辑像素
css 像素是逻辑像素,并不是真实像素
Windows / Mac 电脑都可以设置设备独立像素

dpi (dots per inch)

和 ppi 基本相同

ppi (pixels per inch)

ppi 计算公式

ppi在120-160之间的手机被归为低密度手机,160-240被归为中密度,240-320被归为高密度,320以上被归为超高密度(例如苹果公司的Retina显示屏)

总结

CSS 只与设备独立像素有关系,尺寸相同,设备独立像素相同的屏幕,网页上的同一张图片显示的尺寸是相同的,只不过清晰度会有所不同。所以会有二倍图,三倍图。
宽 100px 的图片在 1:1 的屏幕上css设置为 100px,在 2:1 的屏幕上可以使用200px 的图片,css 同样设置为 100px 。
设备独立像素 (DIP) 是与设备无关的,可以调整的逻辑像素。

参考:

  1. 1 pt 的图形大小与其在屏幕上显示出的大小之间有什么关系?

  2. pt

  3. 设备像素比(devicePixelRatio)

  4. CSS像素、设备独立像素、设备像素之间关系