Renkosky's Home

莲子的小窝


  • 首页

  • 归档

  • 标签

ESTree

发表于 2023-08-06
字数统计 974 字 | 阅读时长 3 分钟

JavaScript AST (Abstract Syntax Tree) 是将 JavaScript 代码转换为抽象语法树的一种表示形式。它可以用来分析、转换和验证 JavaScript 代码。

EStree 是一种用于描述 JavaScript 代码抽象语法树的规范。它定义了 AST 节点类型和属性,以及节点间的关系。EStree 规范是由 ECMAScript 标准委员会制定的,旨在成为 JavaScript AST 的标准表示形式。

EStree 规范定义了许多节点类型,包括表达式、语句、声明等。每个节点类型都有其独特的属性和方法,用于描述 JavaScript 代码的结构和逻辑。通过 EStree 规范,我们可以构建出 JavaScript 代码的完整抽象语法树,从而对代码进行分析、转换和验证。

EStree 规范已经在许多 JavaScript 工具中得到了广泛应用,如 ESLint、Babel 等。理解 EStree 规范对于深入理解 JavaScript AST 和相关工具的实现非常重要。

ESTree 中的不同节点

所有节点类型都实现以下接口:

1
2
3
4
5
interface Node {
type: string;
range?: [number, number];
loc?: SourceLocation;
}

该type字段是表示AST变体类型的字符串。该loc字段表示节点的源位置信息。如果解析器没有生成有关节点源位置的信息,则该字段为null;否则它是一个对象,包括一个起始位置(被解析的源区域的第一个字符的位置)和一个结束位置.

阅读全文 »

Typescript的逆变协变于不变

发表于 2022-10-02
字数统计 572 字 | 阅读时长 2 分钟

TypeScript 给 JavaScript 添加了一套静态类型系统,是为了保证类型安全的,也就是保证变量只能赋同类型的值,对象只能访问它有的属性、方法。

比如 number 类型的值不能赋值给 boolean 类型的变量,Date 类型的对象就不能调用 exec 方法。

这是类型检查做的事情,遇到类型安全问题会在编译时报错。但是这种类型安全的限制也不能太死板,有的时候需要一些变通,比如子类型是可以赋值给父类型的变量的,可以完全当成父类型来使用,也就是“型变”(类型改变)。这种“型变”分为两种,一种是子类型可以赋值给父类型,叫做协变,一种是父类型可以赋值给子类型,叫做逆变。

协变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Person {
name: string;
age: number;
}

interface Chen {
name: string;
age: number;
hobbies: string[];
}
let person: Person;
let chen: Chen = { name: "czt", age: 26, hobbies: ["coding", "game"] };

person = chen;

↑ chen是person的子类型所以可以赋值给person

这种子类型可以赋值给父类型的情况就叫做协变。

阅读全文 »

React中的bailout

发表于 2021-04-18
字数统计 308 字 | 阅读时长 1 分钟

定义

react 中的 bailout 指的是当满足一定条件是,react判断组件更新前后没有变化,则使用上次生成的fiber树来作为本次更新的 fiber。命中 bailout 逻辑后将不会出发 render

Fiber

在 React15 及以前,Reconciler 采用递归的方式创建虚拟 DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多时间,造成卡顿。

为了解决这个问题,React16 将递归的无法中断的更新重构为异步的可中断更新,由于曾经用于递归的虚拟 DOM 数据结构已经无法满足需要。于是,全新的 Fiber 架构应运而生。

bailout 条件

同时满足以下 4 点后,会进入 bailout 逻辑

1. oldProps !== newProps

props 中的每一个属性都没有变化,引用是全新的。

2. context 的 value 没有变化

3. workInProgress.type === current.type

更新前后 fiber.type 是否变化,比如 div 是否变为 p

4. !includesSomeLane(renderLanes, updateLanes)

当前 fiber 上是否存在更新,如果存在那么更新的优先级是否和本次整棵 fiber 树调度的优先级一致,如果一致则进入 render 逻辑。

demo 链接

Map/set WeakMap/WeakSet hash

发表于 2021-04-01
字数统计 1.5k 字 | 阅读时长 5 分钟

javascript Map/set WeakMap/WeakSet hash

什么是 hash

hash(散列,hash)指的是把任意长度的一段数据通过 Hash 算法映射到固定长度的一段域上。常见的 hash 算法有 md5,sha1 等。

好的 hash 算法的特点有:

  1. 不可以反推原数据
  2. 输入微小的改动都会导致输出的 hash 值发生巨大变化
  3. 哈希算法的执行效率要高效,长的文本也能快速地计算出哈希值
  4. hash 算法的冲突概率要小

Map

基于 hash 的一种数据结构,以键值对存储数据。

创建

1
2
3
4
5
6
7
8
9
10
11
12
// 使用嵌套数组初始化映射 const m1 = new Map([
["key1", "val1"],
["key2", "val2"],
["key3", "val3"]
]);
alert(m1.size); // 3
// 使用自定义迭代器初始化映射 const m2 = new Map({
[Symbol.iterator]: function*() {
yield ["key1", "val1"];
yield ["key2", "val2"];
yield ["key3", "val3"];
} });

特性

  1. Map 的 key 可以是任意类型的值。
  2. Map 中的元素会按照插入的顺序排序。
  3. Map 是可迭代对象,因此可以对其使用for of 和 forEach

与 object 的不同

  1. object key 只能是 number,string 或 symbol
  2. 元素不按照插入顺序,非负整数会最先被列出,排序是从小到大的数字顺序。然后所有字符串,负整数,浮点数会被列出,顺序是根据插入的顺序。最后才会列出 Symbol,Symbol 也是根据插入的顺序进行排序的。
  3. Map 上修改 key 对应 value 时不会覆盖原型,object 会。
  4. object 上的 key 是不可迭代的,只能使用 for in 或者 Object.keys 访问

可枚举可迭代指得是什么

可迭代对象指的是实现了 @@iterator 方法,符合了可迭代协议。一些对象例如 Array,Map 就是如此,被称为内置可迭代对象。object 则没有实现@@iterator 因此不可迭代

实现@@iterator 方法意味着这个对象或者它的原型上一定会有一个 key 等于@@iterator 的属性,这个属性可以用Symbol.iterator访问得到

1
2
3
4
5
new Map()[Symbol.iterator]
// ƒ entries() { [native code] }
//map的@@iterator 方法叫entries
new Map().entries()
// MapIterator {}
阅读全文 »

为什么react hook不能再循环或嵌套函数中调用

发表于 2019-07-02
字数统计 116 字 | 阅读时长 1 分钟

在函数组件中,每次 state 的更新都会重新赋值 state 以及设置对应的 setState 方法,因此保持二者的一致性就十分重要,React 需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用 Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。

typescript学习笔记

发表于 2019-03-02
字数统计 1k 字 | 阅读时长 4 分钟

什么是 Typescript

typescript 是一个 javascript 的超集。在传统的 javascript 中,整个语言都是若类型的,而 typescript 提供了一种强类型的规范,并且实现了接口,模块化,等功能。语法上 es6 和它比较接近,如果你之前比较熟悉 es6 的话可以轻松上手。

基本类型

在 javascript 中类型一共有,number,string,boolean,null,undefined,object。在 typescript 中又新增了一些类型

阅读全文 »

git内部原理

发表于 2019-02-02
字数统计 986 字 | 阅读时长 3 分钟

Git 是什么

从根本上来讲 Git 是一套内容寻址 (content-addressable) 文件系统,在此之上提供了一个 VCS 用户界面。

git 目录

当你在项目文件夹下执行 git init后,Git 会创建一个 .git 目录,几乎所有 Git 存储和操作的内容都位于该目录下

1
2
3
4
5
6
7
8
9
10
目录结构
HEAD
branches/
config
description
hooks/
index
info/
objects/
refs/

新版本的 Git 不再使用 branches 目录,description 文件仅供 GitWeb 程序使用,所以不用关心这些内容。config 文件包含了项目特有的配置选项,info 目录保存了一份不希望在 .gitignore 文件中管理的忽略模式 (ignored patterns) 的全局可执行文件。hooks 客户端或服务端钩子脚本。

最关键的是以下四个目录:

objects 目录存储所有数据内容,refs 目录存储指向数据 (分支) 的提交对象的指针,HEAD 文件指向当前分支,index 文件保存了暂存区域信息。马上你将详细了解 Git 是如何操纵这些内容的

git 中的对象

Git 中有四种对象类型,分别为 blob、tree、commit 和 tag 对象类型,每个对象都有一个 SHA-1 值(指针)指向自身实例,SHA-1 是在创建对象时生成,通过这个 SHA-1 来访问对象实例,对象内容存储在.git/objects 目录。

类型 说明
blob 数据对象 可存储较大内容的文本,可看作是操作系统中文件的内容
tree 树对象 可存储多个 tree 和 blob 对象,可看作是操作系统中的目录
commit 提交对象 可存储一个 tree 对象和其它信息(提交人、提交日期、父 commit 对象和提交说明等)
tag 标签对象 tag 对象存储一个 SHA-1(SHA-1 可以指向任意对象,不仅仅是 commit 对象)和 tag 创建人、创建时间、tag 说明等信息,创建 annotated(附注标签)类型标签就是在创建一个 tag 对象

git add 和 git commit 命令时 Git 进行的工作包括保存修改了的文件的 blob,更新索引,创建 tree 对象,最后创建 commit 对象,这些 commit 对象指向了顶层 tree 对象以及先前的 commit 对象。这三类 Git 对象 ── blob,tree 以及 commit ── 都各自以文件的方式保存在 .git/objects 目录下。

Git References

你可以执行像 git log 1a410e 这样的命令来查看完整的历史,但必须要记住最后一次提交的 sha1 值,这样才能在提交历史中找到这些对象。你需要一个文件来用一个简单的名字来,

在 Git 中,“引用”(references 或者 refs,译者注)可以记录这些 SHA-1 值。你可以在 .git/refs 目录下面找到这些包含 SHA-1 值的文件。这样你就可以用这些指针而不是原来的 SHA-1 值去检索了。

branch

branch 是一类引用。HEAD 除了直接指向 commit,也可以通过指向某个 branch 来间接指向 commit。当 HEAD 指向一个 branch 时,commit 发生时,HEAD 会带着它所指向的 branch 一起移动。

push

实质上,push 做的事是:把当前 branch 的位置(即它指向哪个 commit)上传到远端仓库,并把它的路径上的 commits 一并上传。
push 的时候,如果当前分支是一个本地创建的分支,需要指定远程仓库名和分支名,用 git push origin branch_name 的格式.

rebase

rebase 命令与 merge 相似都可以用来合并代码。区别就在于 rebase 可以提供更线性的提交历史。
rebase 的意思是,给你的 commit 序列重新设置基础点(也就是父 commit)。也就是说在合并代码时,远端会设立新的起点,把本地的提交历史复制到起始点之后,这样两个分支的提交历史就不再分叉了

基于JWT的权限认证

发表于 2018-12-31
字数统计 16 字 | 阅读时长 1 分钟

token 认证的相关概念

auth2.0

tokenauth

token 认证过程

node/koa

柯里化

发表于 2018-12-22
字数统计 535 字 | 阅读时长 2 分钟

什么是柯里化

柯里化可以说是一种函数式编程的过程,其主要目的就是把多个参数的函数转换成一个只接受一个单一参数的函数。被柯里化的函数会返回一个新的函数并接受新的一个参数
在所有的参数用完之后,它才会去执行得到最后的结果

实现柯里化

一个常见又简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
function add(a, b, c) {
return a + b + c
}
//curryadd
function curryadd(a) {
return function(b) {
return function(c) {
return a + b + c
}
}
}
curryadd(1)(2)(3) //6

通过闭包的形式把新的函数 return 出来,这种方式一看就懂,但是如果一开始不知道参数数量就没法这么写了,所以我们可以改写成这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function curry(func) {
var args = [].slice.call(arguments, 1)
//新建_func函数作为返回值
var _func = function() {
//参数长度为0,执行func函数,完成该函数的功能
if (arguments.length === 0) {
return func.apply(this, args)
} else {
//否则,存储参数到闭包中,返回本函数
[].push.apply(args, arguments)
return _func
}
}
return _func
}

解释一下这个函数,argments 中保存着函数的参数,.length 方法可以获取到参数个数,当还有剩余参数的时候,就把参数合并,返回新函数,如果参数填入完整就调用。

一个更加简洁的版本

1
2
3
4
5
6
const curry = fn => {
if (fn.length <= 1) return fn
const generator = args =>
args.length === fn.length ? fn(...args) : arg => generator([...args, arg])
return generator([], fn.length)
}

作用

这是一个计算商品价格的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function discount(price, discount) {
return price * discount
}

const price = discount(500, 0.1) // $50
// $500 - $50 = $450
const price = discount(1500, 0.1) // $150
// $1,500 - $150 = $1,350
const price = discount(2000, 0.1) // $200
// $2,000 - $200 = $1,800
const price = discount(50, 0.1) // $5
// $50 - $5 = $45
const price = discount(5000, 0.1) // $500
// $5,000 - $500 = $4,500
const price = discount(300, 0.1) // $30
// $300 - $30 = $270

其实折扣力度都一样,变化的只是原价格,我们利用 curry 改写一下

1
2
3
4
5
6
fucntion price(discount){
return function(price){
return discount*price
}
}
const tenPercentDiscount = discount(0.1);

这下我们只用输入价格就 ok 了

Canvas学习笔记(1)

发表于 2018-11-25
字数统计 549 字 | 阅读时长 2 分钟

什么是 Canvas

Canvas 是 html5 中新增的标签,用来在网页中创建一块画布。其本身只是一块区域,绘制需要用到 js 脚本

创建 Canvas

1
<canvas id="myCanvas" width="200" height="200"></canvas>

<canvas> 元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。
<canvas> 标签要有一个 id 用于在脚本中获取画布,同时注意 <canvas>必须是闭合的

##绘制 canvas

1
var ctx = document.getElementById('myCanvas').getContext('2d')

getContext(“2d”) 对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。

可以用一段简单的代码检查 canvas 的支持性

1
2
3
4
5
if (canvas.getContext) {
var ctx = canvas.getContext('2d')
} else {
//替换内容
}

绘制矩形

canvas 中一共有三种方法绘制矩形

1
2
3
ctx.fillRect(x, y, width, height) //绘制一个实心矩形
ctx.strokeRect(x, y, width, height) //绘制一个空心矩形
ctx.clearRect(x, y, width, height) //清除指定矩形区域,让清除部分完全透明

绘制路径

canvas只提供了矩形的api,因此绘制其他形状时需要通过创建路径来实现

绘制路径分为以下步骤

  1. 创建路径起始点
  2. 画出路径
  3. 关闭路径
  4. 使用描边或填充渲染图形
1
2
3
4
5
6
beginPath() //创建路径
moveTo(x,y) // 移动笔触初始画笔位置
lineTo(x,y) // 绘制一条从当前指定位置x到y的直线
stroke() //通过线条绘制轮廓
fill() //填充路径的内容区域生成实心图形
closePath() //闭合路径

绘制一个三角形

1
2
3
4
5
6
7
var tri1Ctx = document.getElementById('tri2').getContext('2d')
tri1Ctx.beginPath();
tri1Ctx.moveTo(25,65);
tri1Ctx.lineTo(105,5);
tri1Ctx.lineTo(105,125);
tri1Ctx.closePath()
tri1Ctx.stroke();

圆形/圆弧

1
arc(x, y, radius, startAngle, endAngle, anticlockwise)

画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。

1
arcTo(x1, y1, x2, y2, radius)

根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。

1
2
3
circleCtx.beginPath()
circleCtx.arc(200,100,50,0,2*Math.PI)
circleCtx.fill()
123
Renko

Renko

30 日志
26 标签
© 2023 Renko
由 Hexo 强力驱动
主题 - NexT.Pisces
本站总访问量 次    本站访客数人次