pnpm 有一个名为 resolution-mode 的配置项(在 .npmrc 文件中配置),但无论是 resolution-mode 这个名字还是配置项的值的名字,都相当的不直观,更不方便记忆。
背景:resolution-mode 影响的是 pnpm 的版本解析。适当的配置可以减少子依赖所带来的供应链攻击,并且在缓存的作用下可以加快安装速度。但请不要看到「安全」「快速」就盲目配置这个选项——阅读完下面的内容,并明确自己的意图再去动这个配置。
在解释之前,先做一些约定:
1. 不管在不同模式下,版本解析会有怎样的不同,它们都遵循语义化版本。这是大前提。
2. 下文的「子依赖」表示的是依赖的依赖,也可以被称为「间接依赖」。
目前 resolution-mode 允许设为下面三个值之一:
- highest(当前的默认值)这个模式下所有依赖(包括项目的直接依赖以及子依赖)都会被解析到最新版本;
- time-based 这个模式下项目的直接依赖会被解析到最老版本,而子依赖则会被解析到该直接依赖发布时间之前的最新版本;
- lowest-direct 这个模式下仅仅是项目的直接依赖会被解析到最老版本,子依赖会被解析到最新版本。
下面以这样的一个项目为例子:(测试时所使用的 pnpm 版本为 8.11.0)
ora 库有多个依赖,但我们在这里只需要关注 ora 本身以及它的其中一个依赖 chalk 的版本,并通过 pnpm 生成的 lock file 来得知它们被解析到哪个版本。
- 当 resolution-mode 设为 highest 时:ora 的版本被解析为 6.3.1,chalk 的版本被解析为 5.3.0。两个库都被解析到最新版本。
- 当 resolution-mode 设为 time-based 时:ora 的版本被解析为 6.2.0,chalk 的版本被解析为 5.2.0。ora 能满足语义化版本的最老版本是 6.2.0,而 ora 6.2.0 的发布时间为 2023/3/19 18:13:22,在此时间之前 chalk 最新版本是 5.2.0。(尽管 ora 的 package.json 文件里声明的是
- 当 resolution-mode 设为 lowest-direct 时:ora 的版本被解析为 6.2.0,chalk 的版本被解析为 5.3.0。可以看到即使 ora 作为项目的直接依赖被解析为 6.2.0,但它的依赖 chalk 被解析到最新版本即 5.3.0。
参考:
1. pnpm 关于 resolution-mode 的文档
2. ora 的所有版本
3. chalk 的所有版本
背景:resolution-mode 影响的是 pnpm 的版本解析。适当的配置可以减少子依赖所带来的供应链攻击,并且在缓存的作用下可以加快安装速度。但请不要看到「安全」「快速」就盲目配置这个选项——阅读完下面的内容,并明确自己的意图再去动这个配置。
在解释之前,先做一些约定:
1. 不管在不同模式下,版本解析会有怎样的不同,它们都遵循语义化版本。这是大前提。
2. 下文的「子依赖」表示的是依赖的依赖,也可以被称为「间接依赖」。
目前 resolution-mode 允许设为下面三个值之一:
- highest(当前的默认值)这个模式下所有依赖(包括项目的直接依赖以及子依赖)都会被解析到最新版本;
- time-based 这个模式下项目的直接依赖会被解析到最老版本,而子依赖则会被解析到该直接依赖发布时间之前的最新版本;
- lowest-direct 这个模式下仅仅是项目的直接依赖会被解析到最老版本,子依赖会被解析到最新版本。
下面以这样的一个项目为例子:(测试时所使用的 pnpm 版本为 8.11.0)
{
"dependencies": {
"ora": "^6.2.0"
}
}
ora 库有多个依赖,但我们在这里只需要关注 ora 本身以及它的其中一个依赖 chalk 的版本,并通过 pnpm 生成的 lock file 来得知它们被解析到哪个版本。
- 当 resolution-mode 设为 highest 时:ora 的版本被解析为 6.3.1,chalk 的版本被解析为 5.3.0。两个库都被解析到最新版本。
- 当 resolution-mode 设为 time-based 时:ora 的版本被解析为 6.2.0,chalk 的版本被解析为 5.2.0。ora 能满足语义化版本的最老版本是 6.2.0,而 ora 6.2.0 的发布时间为 2023/3/19 18:13:22,在此时间之前 chalk 最新版本是 5.2.0。(尽管 ora 的 package.json 文件里声明的是
"chalk": "^5.0.0")- 当 resolution-mode 设为 lowest-direct 时:ora 的版本被解析为 6.2.0,chalk 的版本被解析为 5.3.0。可以看到即使 ora 作为项目的直接依赖被解析为 6.2.0,但它的依赖 chalk 被解析到最新版本即 5.3.0。
参考:
1. pnpm 关于 resolution-mode 的文档
2. ora 的所有版本
3. chalk 的所有版本
TypeScript 新特性抢先看:新的内置类型
正如其名,
playground
在这个例子中,参数 a 的类型将被自动推断,此时
如果上面没有使用
另外,
playground
类型
在参与类型计算时,
这里
值得注意的是,在使用
playground
它跟上面的
PR: https://github.com/microsoft/TypeScript/pull/56794
NoInfer<T> 。它与 Uppercase 等类型都属于 marker type(即 lib.d.ts 里的实现只有一个 intrinsic 关键字),编译器对于这类 marker type 都会特殊对待。正如其名,
NoInfer<T> 类型用于防止类型被自动推断。也就是说,在某些地方(通常是函数泛型等),如果不希望该处的类型被自动推断,就可以使用。例如:(下面的例子由官方示例简化而来)declare function test<T extends string>(a: T, b: NoInfer<T>): void
test('a', 'b')
playground
在这个例子中,参数 a 的类型将被自动推断,此时
T 为 'a' ;而对于参数 b,因为我们使用 NoInfer 类型,那么它也就不会参与自动推断,由于前面 T 被推断为 'a' ,而此处的实际参数却是 'b' ,因此类型不兼容进而报错。如果上面没有使用
NoInfer , T 将会被推断为 'a' | 'b' ,读者可以打开 playground 链接自行修改测试。另外,
NoInfer<T> 不会对 T 产生影响,例如:type Result = NoInfer<'s'>
playground
类型
Result 仍为 's' 。在参与类型计算时,
NoInfer<T> 其实只是被替换为 unknown 。以上面的 test 函数为例,它相当于:declare function test<T extends string>(a: T, b: unknown): void
这里
T 仍为 'a' ,只不过因为使用了 unknown 而无法对参数 b 进行约束。值得注意的是,在使用
NoInfer<T> 时,一定要留意 T 在可被推断的地方被推断为了什么类型。例如:declare function test2<T>(a: T, b: NoInfer<T>): void
test2('a', 'b')
playground
它跟上面的
test 函数相比, T 没有了 extends string 的泛型约束。这时,参数 a 将被推断为 string 而不是 'a' ,也就是 T 推断为 string 。但因为 'b' 满足 string 的要求,因此这段代码不会报错。PR: https://github.com/microsoft/TypeScript/pull/56794
👍3
位于全局的
-
-
请尽可能使用
参考:
1. MDN 上的示例代码
2. ECMAScript 标准中关于 isNaN 的说明
3. ECMAScript 标准中关于 Number.isNaN 的说明
isNaN 与 Number.isNaN 虽然函数名字一样,但具体行为是有差别的:-
isNaN 会对传入的参数执行类型转换,转换为数字类型后再去判断;-
Number.isNaN 则不会执行类型转换,对于数字类型以外的值均返回 false 。请尽可能使用
Number.isNaN 以避免因类型转换而导致意外发生。参考:
1. MDN 上的示例代码
2. ECMAScript 标准中关于 isNaN 的说明
3. ECMAScript 标准中关于 Number.isNaN 的说明
👏7👎1
ECMAScript 有三种严格(即不会执行类型转换)相等判断的语义,分别是:
- SameValue
- SameValueZero
- IsStrictEqual
对于这三种语义,它们都会对类型不同的值返回 false;对于非数字类型的值,都会执行 SameValueNonNumber 语义;而对于数字类型,它们的差别在于比较 NaN、+0 与 -0 有不同:
- SameValue 会视 NaN 与 NaN 为相等,+0 与 -0 不等;
- SameValueZero 会视 NaN 与 NaN 为相等,+0 与 -0 相等;
- IsStrictEqual 会视 NaN 与 NaN 为不等,+0 与 -0 相等。
下面是三种语义所被使用到的常见地方:
SameValue:
- Object.is
- WeakMap 的 key、WeakSet
SameValueZero:
- Array.prototype.includes
- %TypedArray%.prototype.includes
- Map 的 key、Set
IsStrictEqual:
- === 比较运算符
- Array.prototype.indexOf
- Array.prototype.lastIndexOf
- %TypedArray%.prototype.indexOf
- %TypedArray%.prototype.lastIndexOf
感谢 @JackWorks
----------------
笔者根据规律想到一个辅助记忆办法:
1. === 与 indexOf/lastIndexOf 都是 ECMAScript 中很早就存在的,对于这类「古老」的都使用 IsStrictEqual 语义;
2. includes 与 Map/Set 是 ES2015 及更晚才出现的,对于这类「新兴」的使用 SameValueZero 语义;
3. 虽然
4. WeakMap 的 key 与 WeakSet 不允许是 primitive type,因此可以忽略。
- SameValue
- SameValueZero
- IsStrictEqual
对于这三种语义,它们都会对类型不同的值返回 false;对于非数字类型的值,都会执行 SameValueNonNumber 语义;而对于数字类型,它们的差别在于比较 NaN、+0 与 -0 有不同:
- SameValue 会视 NaN 与 NaN 为相等,+0 与 -0 不等;
- SameValueZero 会视 NaN 与 NaN 为相等,+0 与 -0 相等;
- IsStrictEqual 会视 NaN 与 NaN 为不等,+0 与 -0 相等。
下面是三种语义所被使用到的常见地方:
SameValue:
- Object.is
- WeakMap 的 key、WeakSet
SameValueZero:
- Array.prototype.includes
- %TypedArray%.prototype.includes
- Map 的 key、Set
IsStrictEqual:
- === 比较运算符
- Array.prototype.indexOf
- Array.prototype.lastIndexOf
- %TypedArray%.prototype.indexOf
- %TypedArray%.prototype.lastIndexOf
感谢 @JackWorks
----------------
笔者根据规律想到一个辅助记忆办法:
1. === 与 indexOf/lastIndexOf 都是 ECMAScript 中很早就存在的,对于这类「古老」的都使用 IsStrictEqual 语义;
2. includes 与 Map/Set 是 ES2015 及更晚才出现的,对于这类「新兴」的使用 SameValueZero 语义;
3. 虽然
Object.is 也是新的,但它可以被认为有别于 === 的存在,所以特殊处理(Jest 等测试框架对于 toBe 断言就是使用 Object.is 去判断);4. WeakMap 的 key 与 WeakSet 不允许是 primitive type,因此可以忽略。
👍2🥰1
通常的来说,我们对 V8 的认知便是性能优化极好的 JavaScript 运行引擎,但是我们可以来看一下这俩段代码。
-
-
从 JavaScript 的语义上来说,这俩段代码并没有什么不同,但是当我们将他们运行在 Chrome 或者 Node.js 中,我们便能得到俩个截然不同的错误。
那这和 V8 性能优化极好又有什么关系呢?我们可以在他们的源码中看到如下内容:
https://github.com/v8/v8/blob/04f51bc70a38fbea743588e41290bea40830a486/src/objects/objects.cc#L3004-L3007
简单理解一下,就是在这里直接使用了引用与系统内的常量字符串
那么为什么 '
-
这也是为什么后者没办法作为一个常量字符串和全局只读常量字符串
----
主题之外,其实我们刚刚的代码上面看到一段东西:
https://github.com/v8/v8/blob/04f51bc70a38fbea743588e41290bea40830a486/src/objects/objects.cc#L3003
#v8 #defineProperty #JavaScript
-
Object.defineProperty([], 'length', {value: -1, configurable: true})-
Object.defineProperty([], 'len' + 'gth', {value: -1, configurable: true})从 JavaScript 的语义上来说,这俩段代码并没有什么不同,但是当我们将他们运行在 Chrome 或者 Node.js 中,我们便能得到俩个截然不同的错误。
Object.defineProperty([], 'length', {value: -1, configurable: true})
VM8179:1 Uncaught RangeError: Invalid array length
at Function.defineProperty (<anonymous>)
at <anonymous>:1:8
(anonymous) @ VM8179:1
Object.defineProperty([], 'len' + 'gth', {value: -1, configurable: true})
VM8183:2 Uncaught TypeError: Cannot redefine property: length
at Function.defineProperty (<anonymous>)
at <anonymous>:2:8那这和 V8 性能优化极好又有什么关系呢?我们可以在他们的源码中看到如下内容:
https://github.com/v8/v8/blob/04f51bc70a38fbea743588e41290bea40830a486/src/objects/objects.cc#L3004-L3007
if (*name == ReadOnlyRoots(isolate).length_string()) {
// 2a. Return ArraySetLength(A, Desc).
return ArraySetLength(isolate, o, desc, should_throw);
}
简单理解一下,就是在这里直接使用了引用与系统内的常量字符串
"length" 引用进行了一个 O(1) 的快速匹配,从而优化了性能,但是也导致了我们上面的错误。那么为什么 '
length' 和 'len' + 'gth' 有区别呢?这个没有优化吗?我们可以在这《Exploring V8's strings: implementation and optimizations》看到实际上在 V8 中存在着不同的字符串,当我们使用 %DebugPrint 对俩段内容进行输出的时候,我们可以发现:-
%DebugPrint('leng'+'th'):ONE_BYTE_STRING_TYPE
- %DebugPrint('length'):ONE_BYTE_INTERNALIZED_STRING_TYPE
这也是为什么后者没办法作为一个常量字符串和全局只读常量字符串
"length" 进行比较的原因。----
主题之外,其实我们刚刚的代码上面看到一段东西:
https://github.com/v8/v8/blob/04f51bc70a38fbea743588e41290bea40830a486/src/objects/objects.cc#L3003
// TODO(jkummerow): Check if we need slow string comparison.
#v8 #defineProperty #JavaScript
GitHub
v8/src/objects/objects.cc at 04f51bc70a38fbea743588e41290bea40830a486 · v8/v8
The official mirror of the V8 Git repository. Contribute to v8/v8 development by creating an account on GitHub.
👍3
Flow 在 v0.209.0 版本(发布于 2023 年 6 月)新增一个语法支持:component syntax,这个语法专门针对编写 JSX 组件而设计。但这个语法曾在后续几个版本被临时关闭(能识别、解析语法,但会报错提示不支持),直到 v0.214.0 版本(发布于 2023 年 8 月)才重新开启。不过笔者粗略地浏览了这几个版本的更新日志,都没有看到有关 component syntax 的说明。
component syntax 大致像函数声明:通过 component 关键字(注意这只是 Flow 自己的「软关键字」或「上下文关键字」),随后列出参数作为组件的 props:
另外,从 v0.218.0 版本(发布于 2023 年 10 月)起可以像指定函数返回类型那样,指定当前组件会渲染出什么组件:
也是从 v0.218.0 版本起,可以指定 props 传入什么组件:
它还有更多的特性,例如可选参数、类型限定等,详细可阅读 Flow 文档:
1. https://flow.org/en/docs/react/component-syntax/
2. https://flow.org/en/docs/react/render-types/
component syntax 大致像函数声明:通过 component 关键字(注意这只是 Flow 自己的「软关键字」或「上下文关键字」),随后列出参数作为组件的 props:
component LargeHeader(color: string) {
return <Header size="large" color={color} />
}
另外,从 v0.218.0 版本(发布于 2023 年 10 月)起可以像指定函数返回类型那样,指定当前组件会渲染出什么组件:
component LargeHeader(color: string) renders Header {
return <Header size="large" color={color} />
}
也是从 v0.218.0 版本起,可以指定 props 传入什么组件:
component Layout(header: renders Header) {
return <div>{header}</div>;
}
它还有更多的特性,例如可选参数、类型限定等,详细可阅读 Flow 文档:
1. https://flow.org/en/docs/react/component-syntax/
2. https://flow.org/en/docs/react/render-types/
前端工具鏈沒有說的秘密
Flow 在 v0.209.0 版本(发布于 2023 年 6 月)新增一个语法支持:component syntax,这个语法专门针对编写 JSX 组件而设计。但这个语法曾在后续几个版本被临时关闭(能识别、解析语法,但会报错提示不支持),直到 v0.214.0 版本(发布于 2023 年 8 月)才重新开启。不过笔者粗略地浏览了这几个版本的更新日志,都没有看到有关 component syntax 的说明。 component syntax 大致像函数声明:通过 component 关键字(注意这只是…
Medium
New Flow Language Features for React
Write Safer and more Succinct React with Flow
未闭合的属性选择器,比如
是可以的。但这种写法只针对 DOM API,在 CSS 代码里则是不合语法的。
然而,querySelector 认为
a[href ,在 querySelector 中是合法的。即:document.querySelector('a[href')是可以的。但这种写法只针对 DOM API,在 CSS 代码里则是不合语法的。
然而,querySelector 认为
a[href= 是无效的选择器。
前端工具鏈沒有說的秘密
ECMAScript 有三种严格(即不会执行类型转换)相等判断的语义,分别是: - SameValue - SameValueZero - IsStrictEqual 对于这三种语义,它们都会对类型不同的值返回 false;对于非数字类型的值,都会执行 SameValueNonNumber 语义;而对于数字类型,它们的差别在于比较 NaN、+0 与 -0 有不同: - SameValue 会视 NaN 与 NaN 为相等,+0 与 -0 不等; - SameValueZero 会视 NaN 与 NaN 为相等,+0…
Tuple & Record 提案被废弃,取而代之的是 Composite 提案:
https://github.com/tc39/proposal-composites/
我们在此先不讨论 Composite 好用与否,但我们注意到该提案对 Array.prototype.indexOf 与 Array.prototype.lastIndexOf 的一个改动:
即,如果参数是 Composite,将采用 SameValueZero 语义而非 IsStrictEqual。(其它类型的值仍采用 IsStrictEqual 语义)这无疑会使得 indexOf 与 lastIndexOf 变复杂,同时导致一致性变差。
https://github.com/tc39/proposal-composites/
我们在此先不讨论 Composite 好用与否,但我们注意到该提案对 Array.prototype.indexOf 与 Array.prototype.lastIndexOf 的一个改动:
即,如果参数是 Composite,将采用 SameValueZero 语义而非 IsStrictEqual。(其它类型的值仍采用 IsStrictEqual 语义)这无疑会使得 indexOf 与 lastIndexOf 变复杂,同时导致一致性变差。
GitHub
GitHub - tc39/proposal-composites
Contribute to tc39/proposal-composites development by creating an account on GitHub.
😱3❤2💩1
分久必合,合久必分。当年 JSCS 并入 ESLint,使得 ESLint 能格式化代码;而如今 ESLint 处理代码风格相关的规则被拆出,由 ESLint Stylistic 接手维护。
https://eslint.org/blog/2016/04/welcoming-jscs-to-eslint/
https://eslint.org/blog/2016/04/welcoming-jscs-to-eslint/
eslint.org
Welcoming JSCS To ESLint - ESLint - Pluggable JavaScript Linter
A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code quality with ease.