0%

github port 22 not working

今天在使用github的时候,发现git push的时候报错:

1
2
3
4
5
ssh: connect to host github.com port 22: Connection timed out
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

解决办法,使用443端口,首先测试是否可用。

1
ssh -T -p 443 git@ssh.github.com

如果可以正常连接,那么就可以修改~/.ssh/config文件,添加如下内容:

1
2
3
Host github.com
Hostname ssh.github.com
Port 443

然后再次测试:

1
ssh -T git@github.com

输出如下信息:

1
Hi Philip! You've successfully authenticated, but GitHub does not provide shell access.

接下来就可以愉快的使用git了。

Reference

Groovy JSON operation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import groovy.json.JsonOutput
import groovy.json.JsonSlurper

// Convert a groovy map to json object
Map user = [name: 'zdd', age: 18, info: [address: 'beijing', phone: '123456789']];
println JsonOutput.toJson(user)

// Convert a json object to groovy map
def jsonString = '{"name": "John", "age": 30, "city": "New York"}'
def jsonSlurper = new JsonSlurper()
def jsonObject = jsonSlurper.parseText(JsonOutput.toJson(user))
println jsonObject

// Given a string "feat(configuration): id-xxxx add configuration for user page", please extract the jira id: id-xxx
// can you do it with regex? the target string start with ": " and end with " ", return null if not found
def str = "feat(configuration): id-xxxx add configuration for user page"
def jiraId2 = str =~ /: (.*?) /
println jiraId2[0][1]

async & defer, what’s the differences?

Html引入外部脚本

Html中引入外部脚本的方式有很多种,最常见的就是使用<script>标签,我们可以在<head>或者<body>中引入外部脚本。

1
2
3
4
5
<head>
<script src="./my_script.js"></script>
<script async src="./my_script1.js"></script>
<script defer src="./my_script2.js"></script>
</head>
1
2
3
4
5
<body>
<script src="./my_script.js"></script>
<script async src="./my_script1.js"></script>
<script defer src="./my_script2.js"></script>
</body>

那么从<head><body>中引入外部脚本有什么区别呢?

  • <head>中引入外部脚本,会阻塞Dom的解析,直到脚本下载完毕并执行完毕。
  • <body>末尾中引入外部脚本,不会阻塞Dom的解析,脚本下载和Dom解析并行进行。

你一定听说过上面的说法,但是上面的说法有个前提,那就是同步引入脚本,也就是不加async或者defer.

如果使用async或者defer关键字,那么在<head>中引入外部脚本和在<body>末尾引入外部没有太大区别。(只是<head>中的脚本会先于<body>中的脚本下载)

async & defer

在Html引入外部脚本时,可以使用async或者defer,那么两者有和区别呢?我们通过一个表格来分析一下:

关键字 下载时机 执行时机 是否阻断Dom解析 多个脚本的执行顺序
遇到对应的<script>标签,立即下载 下载后立即执行 是,下载阶段和执行阶段都阻断DOM解析。 按顺序执行
async 遇到对应的<script>标签,立即下载 下载后立即执行,只能保证在window.load事件之前执行,但是可能在window.DomContentLoade之前或之后。 没有固定顺序,取决于哪个脚本先下载完成。
defer 遇到对应的<script>标签,立即下载 在页面解析完之后,且在DomContentLoaded事件触发之前执行。 <script>标签出现的顺序执行

注意事项:

  1. async脚本的执行无固定顺序,谁先下载完,谁先执行。
  2. defer脚本按出现的先后顺序执行。

以下代码中,我们使用defer加载两个脚本,其中short.js非常小,很快就下载完了,而long.js非常大,下载时间很长。因为defer脚本是按照书写顺序进行执行的,所以即使short.js先下载完了,也要等到long.js下载完毕才能开始执行。

1
2
<script defer src='https://xxx/yyy/long.js'>
<script defer src='https://xxx/yyy/short.js'>

用一张图来总结一下,图里中颜色含义如下:
绿色线条 - 表示dom解析
蓝色线条 - 表示脚本的下载
红色线条 - 表示脚本的执行。
script分为普通脚本和模块化脚本。

https://html.spec.whatwg.org/images/asyncdefer.svg

解释一下上面这张图:

  1. 第一行,普通脚本,没有指定async或者defer,下载和执行阶段会阻断dom解析。
  2. 第二行,普通脚本,有defer关键字,下载和执行阶段都不阻断dom解析。(下载和dom解析并行进行,执行在dom解析完成后开始)
  3. 第三行,普通脚本,有async关键字,下载阶段不阻断dom解析,但执行阶段有可能阻断dom解析(如果脚本已经下载完毕,但是dom解析尚未完成的情况下)
  4. 第四行,模块化脚本,默认包含defer属性,该脚本及其依赖的其他脚本的下载与dom解析平行进行,待dom解析完毕开始执行脚本。
  5. 第五行,模块化脚本,有async关键字,该脚本及其依赖的其他脚本的下载与dom解析平行进行,下载完毕后立即执行脚本。

async & defer

  • 标记为async或defer的script,下载阶段都不会阻断Dom的解析,但是async是下载后立即执行,而defer是下载后且等待dom解析完毕才执行,所以两者唯一的区别就是:async脚本执行阶段可能会阻断dom解析(前提是脚本已经下载完毕,但dom解析尚未完成)。
  • module script默认包含defer属性
  • 多个标记为async的脚本,无法保证执行顺序。
  • 多个defer脚本按照script标签出现的顺序执行。
  • 没有标记async或defer的脚本会阻断Dom的解析。

window.load和window.DomContentLoaded

这是两个重要的事件,与async及defer的执行时机息息相关。

  • window.load - 标志整个页面全部加载完成,包括images,styles和JavaScript等所有外部资源。
  • window.DomContentLoaded - Html文件解析和加载完成(parsed and loaded),且所有标记为defer的js脚本全部下载并执行完成后触发,注意,该事件不会等待其他资源,比如images,subframes,或者标记为async的script下载完成。另外,该事件不会等待stylesheet完成,但是:因为defer脚本会等待stylesheet加载完才执行,而该事件又在defer脚本执行完才触发,所以如果有defer脚本存在的话,那么该事件一定会等待stylesheet加载完才触发。

问题来了

看起来async和defer没有太大的区别,那么两者分别在什么场合使用呢?

  • async一般用在与当前页面无关联的外部脚本,比如Google统计,计数脚本等。
  • defer一般用于需要操作当前页面的脚本,所以它需要等Dom解析完之后才执行。

一道小题

下面代码的输出结果是什么?

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<div id="container"></div>
<script>
window.addEventListener("DOMContentLoaded", () => {
console.log("DOMContentLoaded event triggered");
});
window.addEventListener("load", () => {
console.log("load event triggered");
});
</script>
<script async src="./async_script.js"></script>
<script defer src="./defer_script.js"></script>
</body>

async_script.js

1
console.log('async script loaded');

defer_script.js

1
console.log('defer script loaded');

答案:

1
2
3
4
async script loaded
defer script loaded
DOMContentLoaded event triggered
load event triggered

多执行几次,你会发现,有时候输出还会是下面这样的。这充分印证了上面的结论,defer script一定在DOMContentLoaded之前执行,但是async script可能在DOMContentLoaded之前,也可能在其之后。

1
2
3
4
defer script loaded
DOMContentLoaded event triggered
async script loaded
load event triggered

References:

https://javascript.info/script-async-defer

https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer V8引擎

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event

https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event

https://javascript.info/onload-ondomcontentloaded

Famous NPM Packages

cross-env

cross-env is a cross platform solution to setting and using environment variables. It’s available as a command line utility as well as a Node.js module.

cross-env 是一个用于处理跨平台环境变量的 Node.js 包。在 Node.js 应用程序中,设置环境变量通常使用 process.env。然而,不同的操作系统(如 Windows、macOS 和 Linux)有不同的方式来设置和获取环境变量。cross-env 的目标是提供一种简单的方法来在跨平台上设置和获取环境变量。

How Jest Works(Jest是如何工作的)

1. 什么是Jest

Jest是由Facebook(Meta)开发的一个Javascript测试框架。支持多种前端框架,比如Babel, TypeScript, Node, React, Angular, Vue等等。它的特点是快速,简单,可扩展。

那么多文件都是干啥的?

如果你配置过Jest的测试环境,你会发现Jest用到很多文件,比如jest.config.js, tsconfig.json, setupTest.js, 等等。它们的作用是什么呢?

简单来说,当你在命令行执行npm run test时,node会调用Jest,Jest会读取jest.config.js文件,解析配置,然后执行测试。而jest.config.js文件中会指定一些其他的配置文件,setupTest.js等等。

2. 基于Nx框架的Jest配置

我们目前的项目基于Nx框架,其实Nx本身对于Jest已经支持的很好了,几乎不用做太多的配置,但是本文仍以Angular为例,看看如何配置Jest。

当我们使用Nx创建一个Angular项目时,会自动创建一个jest.config.ts文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* eslint-disable */
export default {
displayName: 'todos',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
},
coverageDirectory: '../../coverage/apps/todos',
transform: {
'^.+\\.(ts|mjs|js|html)$': 'jest-preset-angular',
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};

transform一节,我们看到了jest-preset-angular,这个是Angular官方提供的一个Jest预设,它会自动帮我们处理Angular的一些特殊文件,比如.ts, .html, .svg等等。如果我们不使用Angular,而是使用React,那么我们需要使用jest-preset-react,如果是Vue,那么我们需要使用vue-jest。如果我们使用的是Nx,那么我们可以使用@nrwl/jest,它会自动帮我们处理Nx的一些特殊文件,比如.ts, .html, .svg等等。

为什么需要transform呢?因为Jest只能处理JavaScript,对于TypeScript/JSX等高级的文件格式,我们必须先将其转换为JavaScript,然后再交给Jest处理。

transform块的含义是,对于所有以.ts, .mjs, .js, .html结尾的文件,都使用jest-preset-angular来处理。

如果transform配置不正确,就会出现如下错误:

1
2
3
4
5
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.

How jest parse config and execute tests?

Jest是如何解析配置文件,并执行测试的呢?我们可以通过--debug参数来查看Jest的执行过程。

To be continue…

References

如何写榜书

榜书需用熟纸,最好是铜版纸,十分光滑。墨需要用浓墨,最好买超浓墨汁,也可以自制宿墨(将墨水准备好之后,放置一晚),最见精神,如果写完纸张发皱,可用熨斗熨一下。笔可用兼毫,写大字时一定要将笔毫铺开,方能如意!不可只用笔尖,那是小字的笔法。
初练榜书时,可用报纸,将一张报纸从中间裁开,每张写一字,

如何让墨汁好写

可以在墨汁中添加洗洁精或者液态胶水,今天晚上我就加了一点液态胶水,效果很好,墨汁不会太稀,也不会太浓,很好写。非常丝滑,胶水不用太好的,就文具店那种白色液体胶水即可。

宿墨

宿墨可以自行制作,也可以买现成的宿墨,今日见抖音作者用宣和宿墨写豆腐宣,很是得手。

笔法

写字需笔笔交待清楚!要把动作做到位,不可草率。

毛笔

  1. 山马就是马尾巴,这个称呼来自岛国,这事今天在抖音武京生老师直播间学到的,特此记录。

How many ways to convert a value to string in JavaScript

1. value.toString()

1
2
const num = 123;
const str = num.toString();

2. String(value)

1
2
3
const num = 123;
const str = String(num);
console.log(str);

3. value + ‘’

1
2
const num = 123;
const str = num + '';

4. `${value}`

const num = 123;
const str = `${num}`;

5. JSON.stringify(value)

1
2
const num = 123;
const str = JSON.stringify(num);

What’s the difference between them?

undefined in javascript

undefined是可以说是javascript中最特殊的一个类型,许多其他语言中都没有这个类型。它表示一个变量已经声明,但还没有被赋值。

1
let a; // a的值是undefined

如果没有掌握好undefined,则可能会写出一些非常业余的代码,我们来看一个例子,这是在做code review时遇到的真实例子,你能看出下面的代码有什么问题吗?

1
2
3
4
5
6
7
function getUserName(user) {
if (user) {
return user.name;
} else {
return undefined;
}
}

在揭晓答案之前,我们先系统学习一下什么情况下JavaScript会产生undefined类型。

1. 显式undefined类型

1
2
const a = undefined;
console.log(a); // undefined.

2. 未初始化的变量

1
2
let a;
console.log(a); // undefined.

3. 访问对象中不存在的属性

1
2
3
4
5
6
7
8
9
10
const person = {
name: 'zdd',
age: 41,
};

console.log(person.gender); // undefined.


const a = [1, 2, 3];
console.log(a[3]); // 数组只有三个元素,而a[3]表示第四个元素,所以它们的值是undefined

4. 函数没有返回值

函数没有返回值的时候,实际上返回的就是undefined

1
2
3
4
5
function test() {
console.log('hello, world!');
}

console.log(test()); // undefined.

5. 调用函数没有传递对应的参数

下面的代码中,函数add没有传入任何参数,所以ab的值都是undefined

1
2
3
4
5
6
7
function add(a, b) {
console.log(a); // 输出undefined.
console.log(b); // 输出undefined.
return a + b;
}

add(); // 没有传递参数

undefined != not defined

需要注意的是,undefinednot defined是两个不同的概念。undefined表示一个变量已经声明但还没有被赋值,而not defined表示一个变量没有被声明。

1
2
3
let a;
console.log(a); // undefined.
console.log(b); // error, b is not defined

undefined vs void 0

既然已经有了undefined,为什么有很多JavaScript库中还使用void 0呢? 原因就是undefined是一个值,而不是关键字,能被用户串改,看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
const undefined = 1; // undefined被用户篡改!

const add = (a, b) => {
// 这里判断参数是否传入,结果失效了,因为undefined值在前面被改成了1
if (a === undefined || b === undefined) {
console.error('请输入两个数');
} else {
return a + b;
}
};

add(1, 2); // 这里会执行add函数中的if分支,是不是很崩溃?

使用void 0就不会有这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
const undefined = 1;

const add = (a, b) => {
// 写成void 0就没有问题了,无论undefined被改成什么,都不影响。
if (a === void 0 || b === void 0) {
console.error('请输入两个数');
} else {
return a + b;
}
};

console.log(add(1, 2));

那么为什么void 0返回undefined呢?这是因为void是一个操作符,它的作用是对其后面的表达式求值,然后返回undefined。在JavaScript中,void 0等价于undefined,其实你也可以写void 1, void 'hello'等,结果都是undefined

void expression的求值规则 - 先对expression求值,然后返回undefined

正则表达式中的undefined

在正则表达式中,可以使用test来测试某个字符串是否满足特定的规则。

1
console.log(/^hello/.test('hello, world!')); //true

如果你没有传递参数给test,那么它会尝试匹配字符串undefined.

1
console.log(/undefined/.test()); // true

This is equivalent to the following code, since undefined convert to string is 'undefined', so the result is true.

1
console.log(/undefined/.test(undefined));

详情请看这里

undefined vs null

undefinednull经常被放到一起比较,那么他们之间有什么区别呢?

  • undefined表示一个变量已经声明但还没有被赋值,
  • null表示一个变量已经被赋值为一个空值。
  • null是JS中的关键字,但是undefined是一个全局属性。

undefined与其他类型之间的转换

这里面比较特殊的是和数字类型之间的转换,undefined转换为数字类型时会返回NaN,而null转换为数字时会返回0

1
2
3
console.log(String(undefined)); // "undefined"
console.log(Number(undefined)); // NaN
console.log(Boolean(undefined)); // false

注意null转换为其他类型时与undefined的区别

1
2
3
console.log(String(null)); // "null"
console.log(Number(null)); // 0
console.log(Boolean(null)); // false

回到文章开始的问题,根据上面第四点,函数没有返回值时,返回的就是undefined,所以上面的代码可以简化为如下形式,else分支完全没有必要。

1
2
3
4
5
function getUserName(user) {
if (user) {
return user.name;
}
}

References:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#undefined_type
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

Optional chaining

Why optional chaining

Have you write the following code before?

1
2
3
if (user && user.address && user.address.street) {
console.log(user.address.street);
}

With optional chaining operator, you don’t need so many && to get a deeply nested property.

1
console.log(user?.address?.street);

How to use optional chaining

The optional chaining (?.) operator accesses an object’s property or calls a function. If the object accessed or function called using this operator is undefined or null, the expression short circuits and evaluates to undefined instead of throwing an error.

Access object’s property

1
2
3
4
5
6
const person = {
name: 'Philip',
};

console.log(person.info.address); // TypeError: Cannot read property 'address' of undefined.(Because, person does not have property 'info')
console.log(person.info?.address); // undefined

Call a function

1
2
3
4
5
6
7
8
9
10
function doSomething(onContent, onError) {
try {
// Do something with the data
} catch (err) {
// Testing if onError really exists
if (onError) {
onError(err.message);
}
}
}

With optional chaining, you don’t need too check weather onError is defined or not.

1
2
3
4
5
6
7
function doSomething(onContent, onError) {
try {
// Do something with the data
} catch (err) {
onError?.(err.message); // No exception if onError is undefined
}
}

Short circuit

When using optional chaining, if the left operand is null or undefined, the expression will not be evaluated, for instance:

1
2
3
4
5
let i = 0;
const nums = null;
const firstNumber = nums?.[i++];
console.log(x); // 0, since num?. trigger short circuit, [i++] is not evaluated.

?. not work for non-declared root object

Optional chaining can not be used with a non-declared root object, but can be used with a root object that is null or undefined.

1
2
3
console.log(a?.b); // Uncaught ReferenceError: a is not defined
console.log(null?.b); // undefined
console.log(undefined?.b); // undefined

In javascript, not defined and undefined are two different concepts. See undefined vs not defined

Unit test coverage for optional chaining

Reference

undefined vs not defined

In JavaScript, undefined and not defined are two different concepts.

  • undefined: a variable has been declared but has not yet been assigned a value.
  • not defined: a variable has not been declared(not exists).

undefined

1
2
let a;
console.log(a); // undefined

not defined

1
console.log(b); // Uncaught ReferenceError: b is not defined

Whenever you try to access a variable that is not declared, JavaScript throws an error: Uncaught ReferenceError: xxx is not defined. This is because variable b is not declared anywhere in the code. but you can still use typeof to check if a variable is defined or not.

typeof

1
2
3
let a;
console.log(typeof a); // undefined
console.log(typeof b); // undefined, even b is not defined