跳到主要内容

· 阅读需 1 分钟
DK

删除分支后本地查看远程分支仍显示已删除的分支

  1. 使用 git branch -r 命令查看远程分支,发现远程分支已经删除,但本地还显示
  2. 使用 git remote show origin 查看 remote 地址,远程分支,及远程分支与本地分支相对应关系等信息
  3. 使用 git remote prune origin 同步本地分支
  4. 使用 git branch -r 命令再次查看远程分支就与 gitlab 一致了

新建分支后本地查看远程分支不显示已建的分支

  1. 使用 git remote update origin --prune 命令
  2. 使用 git branch -r 就可以查看到新建的分支

· 阅读需 3 分钟
DK

命令式和声明式

从范式上来看,视图层框架通常分为命令式和声明式,它们各有优缺点。作为框架设计者,应该对两种范式都有足够的认知,这样才能做出正确的选择,甚至想办法汲取两者的优点并将其捏合。

命令式和声明式各有优缺点,在框架设计方面,则体现在性能与可维护性之间的权衡。这里我们先抛出一个结论:声明式代码的性能不优于命令式代码的性能。 命令式。

早年间流行的 jQuery 就是典型的命令式框架。命令式框架的一大特点就是关注过程。例如,我们把下面这段话翻译成对应的代码:

· 阅读需 2 分钟
DK

模拟实现

Array.prototype.selfReduce = function (callback, initValue) {
// 获取源数组
const originArray = this;

// 判断源数组是否为空,如果为空,抛出异常
if (!originArray.length) {
throw new Error('selfReduce of empty array with no initial value');
}

// 声明累计器
let accumulator

// 是否有初始值情况
// 设置累计器初始值(如果有初始值,第一次调用`callback`时,`callback`的第一个参数的值为初始值,否则为源数组的第一项)
if (initValue === undefined) {
accumulator = originArray[0];
} else {
accumulator = initValue;
}

// 遍历数组,执行`callback`函数
for (let i = 0; i < originArray.length; i++) {
// 如果没有初始值且是最后一次循环,不再执行callback
if (initValue === undefined && (i + 1) === originArray.length) break;

// 循环执行 `callback`
// 这里判断一下`currentValue`
// 因为有初始值时,`currentValue`是`originArray[i]`
// 没有初始值时`currentValue`是`originArray[i + 1]`
accumulator = callback(accumulator, initValue === undefined ? originArray[i + 1] : originArray[i], i, originArray);
}

// 把累计器返回出去
return accumulator
}

测试

// 找出最大值
const r = [2, 4, 8, 1].selfReduce((a, b) => Math.max(a, b))
console.log(r) // 输出: 8

// 数组元素求和
const arr = [1, 2, 3, 4];
const initVal = 0;
const total = arr.reduce((acc, cur, index, array) => {
console.log(acc, cur);
// 0 1
// 1 2
// 3 3
// 6 4
return acc + cur;
}, initVal);
console.log(total); // 输出:10

· 阅读需 2 分钟
DK

一般情况下,我们或许会使用instanceof,是没有问题的。

async function back(){
return 1;
}
const res = back();
console.log(res instanceof Promise); //true

但是问题来了,Promise是一个规范而不只是一个类。

遵循Promise规范的库包含了ES6默认Promise、bluebird Promise、Q Promise等,那么我们使用bluebird Promise生成的Promise去instanceofES6的默认Promise会不会有问题呢? 显然,要出错。

所以这引出了React官方使用的方式是通过判断条件:

typeof destroy.then === 'function'

来判断一个对象是否是异步返回对象。

这样子的好处是,对于所有实现了Promise规范的异步库,这样的判断方式都是有效的。虽然这有产生误报的风险,但这是所有Promise库都必须遵循的规范。

同样的Promise判断方式并不只是React在使用,可以试试在F12运行这行代码,这将不会有任何输出。

await {then:()=>1};

原因无他,await的语法糖里判断Promise对象也是通过promise.then==='function',这源于Promise A+最基本的定义:

  • "promise"是具有then方法的对象或函数

· 阅读需 2 分钟
DK

两种方式选其一

在当前工程中配置

.prettierrc 文件中

// 根目录 .prettierrc 文件
{
"semi": false,
"singleQuote": true,
"printWidth": 80,
"useTabs": false,
"bracketSpacing": true,
"trailingComma": "none",
"arrowParens": "always",
"endOfLine": "auto"
}

在VScode中配置

{
// 编辑器设置 - 默认采用prettier做格式化
"editor.defaultFormatter": "esbenp.prettier-vscode",
// prettier 设置
"prettier.printWidth": 80, // 换行的行长
"prettier.jsxBracketSameLine": true, // react的jsx让>与结束标签同行
"prettier.jsxSingleQuote": true, // jsx tsx采用单引号
"prettier.semi": false, // 不要给语句加;
"prettier.singleQuote": true, // 采用单引号
"prettier.bracketSpacing": true, // 去掉数组内部前后的空格
"prettier.arrowParens": "avoid", // 箭头函数括号 avoid 尽可能省略括号
"prettier.trailingComma": "none", // 不要尾随逗号
"prettier.endOfLine": "auto" // 行结束配置 保持现有的行尾(一个文件中的混合值通过查看第一行之后使用的内容进行标准化)
}

.vscode/settings.json 文件中

// 根目录 .vscode/settings.json 文件

{
"editor.defaultFormatter": "esbenp.prettier-vscode", // 设置默认格式化工具为prettier
"editor.formatOnSave": true, // 保存时自动格式化
// ... 也可以把上边在编辑器中配置prettier 放在这里单独为这个项目使用
}

需要用到的包

prettier:可以不依赖编辑器使用,当然也需要一些配置

package.json中配置

{
"scripts": {
// 执行命令按照prettier去格式化文件
"format:prettier": "prettier **/*.{js,jsx,ts,json5,json} --write",
// 配合lint-staged使用
"lint-staged": "lint-staged"
},
"lint-staged": {
"*.{js,ts,json5}": [
// 配合lint-staged使用
"prettier --write",
]
},
}

eslint-config-prettiereslint-plugin-prettier:配合eslint使用,让prettier的错误交由eslint(错误、警告)

在.eslintrc中配置

{
"extends": ["plugin:prettier/recommended"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
}
}

· 阅读需 2 分钟
DK

首先需要明白,盒子模型主要定义四个区域:内容(content)、边框与内容的区域,称为内边距(padding)、边框(border)、边框与外部的区域,称为外边距(margin)。

区别

  1. margin:表示边框以外的区域
  2. padding:表示边框与内容之间的区域

使用场景

· 阅读需 3 分钟
DK

为什么要有这些模式,目的:职责划分、分层(将Model层、View层进行分类)借鉴后端思想,对于前端而已,就是如何将数据同步到页面上

  • 传统的 MVC 指的是,用户操作会请求服务端路由,路由会调用对应的控制器来处理,控制器会获取数据。将结果返回给前端,页面重新渲染
  • MVVM:传统的前端会将数据手动渲染到页面上, MVVM 模式不需要用户收到操作 dom 元素,将数据绑定到 viewModel 层上,会自动将数据渲染到页面中,视图变化会通知 viewModel层 更新数据。ViewModel 就是我们 MVVM 模式中的桥梁

MVVM模式 映射关系的简化,隐藏了controller

MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。

  • Model: 代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。我们可以把Model称为数据层,因为它仅仅关注数据本身,不关心任何行为
  • View: 用户操作界面。当ViewModel对Model进行更新的时候,会通过数据绑定更新到View
  • ViewModel: 业务逻辑层,View需要什么数据,ViewModel要提供这个数据;View有某些操作,ViewModel就要响应这些操作,所以可以说它是Model for View.

总结: MVVM模式简化了界面与业务的依赖,解决了数据频繁更新。MVVM 在使用当中,利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。

· 阅读需 2 分钟
DK
  1. 确保文件名修改正确:首先,确保你正确地修改了文件名的大小写。在某些操作系统上,文件名的大小写可能会被忽略,所以请确保你在修改文件名时使用了正确的大小写。

  2. 清除 Git 缓存:如果你修改了文件名的大小写,并且 Git 仍然将其视为相同的文件,可能是因为 Git 缓存了旧文件名的信息。你可以使用以下命令清除 Git 缓存:

git rm --cached <filename>
// 请将 <filename> 替换为被修改文件的名称。这将从 Git 缓存中移除旧文件名的信息。
// 如: git rm --cached myfile.txt

// 清除 Git 缓存
git rm -r --cached .
  1. 提交修改:完成文件名的修改和 Git 缓存的清除后,重新提交你的修改:
git add .
git commit -m "Rename file with correct case"

· 阅读需 2 分钟
DK

1.Beacon API用于将少量数据通过 post 请求发送到服务器。

2.Beacon是非阻塞请求,不需要响应

完美的解决性能缺陷问题:

1.浏览器将Beacon请求排队让它再空闲的时候执行并立即返回

2.它再unload状态下也可以异步发送,不阻塞页面刷新/跳转等操作 使用 navigator.sendBeacon()

const blob = new Blob(['room_id=123'], {
type: 'application/x-www-form-urlencoded',
})

const result = navigator.sendBeacon('http://127.0.0.1:8686', blob)

result是一个布尔值,代表这次发送请求的结果 浏览器接受并把请求排队了 返回true 如果过程中出现了问题 返回false

navigator.sendBeacon接受两个参数: url :请求的url ,请求是post请求 data:要发送的数据

Beacon其他相关 Beacon更多的情况是用于做前端埋点,监控用户活动 客户端优化:可以将 Beacon 请求合并到其他请求上,一同处理, 尤其在移动环境下。

· 阅读需 7 分钟
DK

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性就是计算机科学文献中称的 “闭包”。这个术语非常古老,是指函数变量可以被隐藏于作用链之内,因此看起来是和函数将变量“包裹”起来。

在 JavaScript 中,只有函数内部的局部变量不能直接访问到,而函数中的子函数能读取局部变量,因此可以把闭包简单理解成 "定义在一个函数内部的函数"

《JS 高级程序设计》里面的定义是:一个可以访问另一个函数里局部变量的函数既闭包。

阮一峰的网络日志:在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

闭包是一种形式,它是以函数的方式表现出来。

用处:

  • 能读取其他函数内部变量的函数,并且是 「间接」访问,不能「直接」访问。
  • 让这些变量的值始终保持在内存中。

特点:

  • 可以记录诞生他的环境(全局环境是必有的),JS 也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个 作用域是在函数定义时决定的,而不是调用时决定的。
  • 闭包是把需要的变量保存到内置属性[[Scopeds]]上,这是一个数组,默认必有一个全局环境变量。其实就是帮我们把变量重新声明在当前的作用域中。(网上看的一些知识,自己也不太懂,逼格太高)

形成闭包的三个条件:

  1. 函数嵌套(有一个函数 A , 在函数 A 内部返回一个函数 B
  2. 访问所在的作用域的父级作用域 (在函数 B 中访问函数 A 的私有作用域变量
  3. 在所在的作用域外被调用 (在函数 A 外部,有变量引用函数 B
function A() {
var num = 100;
return function B() {
// 返回值函数B
var num1 = num; // 在函数B中访问函数A的局部变量
console.log(num1);
return num1;
};
}
var fn1 = A(); // 此处调用函数A,执行的结果就是函数B
var fn2 = fn1(); // 此处调用函数fn1(),执行结果就是引用函数B

使用闭包的注意点:

闭包会使得函数中的变量都被保存在内存中每个父函数调用完,就会形成新的闭包。父函数中的变量始终会在内存中,相当于缓存内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,尤其是IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。

但是我认为: 使用了闭包那里面的变量明明就是我们需要的变量。不能说是内存泄漏,内存泄露是指: 你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。

IE 内存泄漏的解决方法:在退出函数之前,将不使用的局部变量全部删除。大部分浏览器,通过赋值为 null,释放内存。在 IE 中 在 return 返回值下面 delete 变量。

闭包可以在父函数外部,改变父函数内部变量的值。 所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,因为都在内存中,所以不要随便改变父函数内部变量的值,否则一处变其它地方也会随之变化

个人理解:

其实我觉得,根本不需要知道闭包这个概念,一样可以使用闭包!

因为函数也是一个 var,只是它比较特殊,函数里面的 var 不能被外接直接访问,然后在函数里面定义了一个函数来访问。其实跟调用一个对象里面定义的函数然后访问对象里面 key,区别就是对象里面的一个 key 可以被外面显示调用(造成不能隐蔽的问题),闭包解决这个问题

闭包是 JS 函数作用域的副产品。

由于 JS 的函数内部可以使用函数外部的变量,正好符合了闭包的定义,而不是 JS 故意要使用闭包。

总结

闭包让程序更加安全,封装数据、暂存变量。不让外部直接访问到我们的数据。