git 加速

在本地拥有代理的情况下,配置命令行 git 加速

一、配置 .ssh/config 文件

配置 http 代理监听 127.0.0.1:1087

1
vim ~/.ssh/config

添加文件内容如下:

1
2
3
Host github.com
User git
ProxyCommand nc -x localhost:1086 %h %p

即可走 http 代理,效果如下

二、配置 git 走 socks5

配置 socks5 代理监听 127.0.0.1:1086

在命令行中执行以下命令

1
2
git config --global http.proxy "socks5h://localhost:1086"
git config --global https.proxy "socks5h://localhost:1086"

即可。

前端面试题整理

call、apply、bind

手动实现 call

1
2
3
4
5
6
7
8
9
10
11
12
13
Function.prototype.myCall = function (context, ...args) {
context = context || window
let symbol = Symbol()
context[symbol] = this
context[symbol](...args)
delete context[symbol]
}

function sayHi () {
console.log(`Hi, ${this.name}`)
}
var a = {name: 'Patrick'}
sayHi.myCall(a)
  • Symbol() 处可以使用一个空对象代替,因为对象的引用地址一定是唯一的。

手动实现 apply

同 call 的实现,只有接收参数的地方不同。

1
2
3
Function.prototype.myApply = function (context, args) {
......
}

手动实现 bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var myBind = function (context, ...presetArgs) {
return (...args) => {
this.call(context, ...presetArgs, ...args)
}
}

Function.prototype.myBind = myBind

function foo(arg1, arg2) {
console.log(this, arg1, arg2)
}

// case 1
foo('hello', 88)

// case 2
var a = {}
foo.myBind(a, 'world')(99)

使用 babel 转义为 es5 代码

  • 注意第二行返回的是一个箭头函数,这边的 this 才是 foo,否则的话 this 会指向 window。
  • bind 可以传预设的参数列表,可以用作函数 curly 化。
  • 连续的 bind 只有第一个 bind 会生效,解释如下:
    • es6 的代码中,使用的是箭头函数,this 指向在调用第一个 bind 的时候就确定了。
    • es5 的代码中,函数调用的时候,后面的 bind 返回的函数反而会先执行,最后实际起作用的是第一个 bind 绑定的对象。

TODO

[-] 如何实现连续 bind,this 指向最后一个绑定的对象?

=> 箭头函数的实现

继承相关

  1. 有几种继承方式

Class

Class 转成 es5 之后的代码

Reflect

判断数据的类型

可以通过 typeof 或者 instanceof 来判断数据的类型

typeof 只能判断基础数据类型,instanceof 可以用来判断的对象的类型

A instanceof B 判断 B 是否存在于 A 的原型链上

如何通过 instanceof 来判断一个基础数据类型?

答:创建一个类 ,改写这个 function 的 [Symbol.hasInstance] 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class PrimitiveString {
static [Symbol.hasInstance] = (x) => {
return typeof x === 'string'
}
}

// 用 es5 实现
function PrimitiveString() {}
Object.defineProperty(PrimitiveString, Symbol.hasInstance, {
value: function (x) {
return typeof x === 'string';
}
});

// 错误的写法,错误的原因:通过 Assignment 的方式去改写 [Symbol.xxx] 属性不会生效。
function PrimitiveString() {}
PrimitiveString[Symbol.hasInstance] = function (x) {
return typeof x === 'string';
}

清除无效的分支

清除本地中无效的远程追踪分支

通过下列的命令可以删除 本地中无效的远程追踪分支

即:远端已经删除过了,本地还存在 remotes/origin/fix/foo,则可通过这个命令清理

1
git remote prune origin

该命令不会删除本地的分支

清除本地的分支

通过以下命令我们可以找到所有带有 fix 或者 feat 的分支

1
git branch | grep -E 'fix|feat'

如果我们想删除这些分支,只需执行以下命令

1
git branch | grep -E 'fix|feat' | xargs git branch -D

如果你只想保留本地中某些分支,我们可以通过 grep -v 反选的功能来查找

1
git branch | grep -vE 'develop|module|release'

同理,我们再添加删除分支的管道 xargs git branch -D

1
git branch | grep -vE 'develop|module|release' | xargs git branch -D

执行完以上命令,我们本地就得到了只包含有 ‘develop|module|release’ 的分支了

千万注意,如果要执行删除分支的命令,一定要确保不要到删除自己正在开发的分支!

hexo 中插入数学公式

前期准备

使用 hexo-renderer-pandoc 渲染的前提条件是,当前的操作系统安装过 pandoc

安装教程:https://github.com/jgm/pandoc/blob/master/INSTALL.md#linux

以 Linux 举例,从 release 下载最新的包并安装

1
2
wget https://github.com/jgm/pandoc/releases/download/2.9.2.1/pandoc-2.9.2.1-1-amd64.deb
sudo dpkg -i pandoc-2.9.2.1-1-amd64.deb

配置

  1. 替换 hexo 的渲染引擎,在 hexo 的项目根目录执行以下命令

    1
    2
    npm uninstall hexo-renderer-marked --save
    npm install hexo-renderer-pandoc --save
  2. 在主题文件夹的目录下,编辑 _config 文件,打开 MathJax 的开关

    1
    2
    3
    4
    5
    # MathJax Support
    mathjax:
    enable: true
    per_page: true
    cdn: //cdn.bootcss.com/mathjax/2.7.1/latest.js?config=TeX-AMS-MML_HTMLorMML
  3. 在写博客的时候,在文章的 Front-matter 处声明开启 MathJax

    1
    2
    3
    4
    title: 切披萨的方案数题解
    date: 2020-05-14
    tag: [leetcode, dp]
    mathjax: true
  4. 配置完成后执行重新生成,之前写好的公式就能正常显示了

    1
    2
    hexo clean
    hexo generate

相关工具

typora

Markdown 编辑器,公式会被实时转义。效果如下图

切披萨方案数题解

原题

Leetcode 切披萨方案数

查看原题

给你一个 rows x cols 大小的矩形披萨和一个整数 k ,矩形包含两种字符: ‘A’ (表示苹果)和 ‘.’ (表示空白格子)。你需要切披萨 k-1 次,得到 k 块披萨并送给别人。

切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。

请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果。

示例 1:

1
2
3
输入:pizza = ["A..","AAA","..."], k = 3
输出:3
解释:上图展示了三种切披萨的方案。注意每一块披萨都至少包含一个苹果。

示例 2:

1
2
输入:pizza = ["A..","AA.","..."], k = 3
输出:1

示例 3:

1
2
输入:pizza = ["A..","A..","..."], k = 1
输出:1

提示:

1
2
3
4
5
1 <= rows, cols <= 50
rows == pizza.length
cols == pizza[i].length
1 <= k <= 10
pizza 只包含字符 'A' 和 '.' 。

分析

前置说明

为了描述方便,我们用 披萨(i, j) 表示左上角坐标为 (i, j) 右下角坐标为(rows -1 ,cols-1) 的披萨

使用 dp 变量表示状态

dp[i][j][k] 表示 披萨(i, j) 切 k 次 分给 k+1个人 一共有多少种情况

拿例一中的图举例:

dp[2][2][0] == 0 表示下图的红框中的披萨切0次,分给1个人,共有 0 种情况。

dp[1][1][1] == 1 表示下图红框中的披萨切1次,分给 2个人,共有1情况

题目中所求的即为 dp[0][0][k-1] 的值,他代表的意思为,从 披萨(0, 0)中切 k-1 次分给 k 个人的情况数

状态转移方程

dp 算法的关键是,如何将一个问题转化为子问题进行求解,即写出状态转移方程。

公式

$$dp[i][j][k] = \sum_{r=i+1}^{rows-1}(hasApple(i,j,r,j) ? dp[r][j][k-1] : 0) + \sum_{r=j+1}^{cols -1}(hasApple(i,j,i,r) ? dp[i][r][k-1] : 0)$$

其中:

$$hasApple(i,j,i,j) = remains[i]][j] - reamains[i][j] > 0$$

解释

dp[i][j][k]

对于任意的 披萨 (i, j),我们遍历他们所有的切割方法(包括横切和纵切),记每种切割完后剩余的披萨为 (i', j'),则剩下的披萨的切割情况数可以用dp[i'][j'][k-1]来表示,我们将所有的这些满足条件的子情况数加起来,就得到了dp[i][j][k]

hasApple

代表切下来的部分是否含有苹果。

remains[i][j] 数组表示披萨 (i, j) 包含的苹果总数

记切除后的披萨为 披萨(i', j')

则切下来的部分的披萨包含苹果数就可以用 remains[i][j] - remains[i'][j'] 来表示了

如何求 remains 数组

我们观察上图,remains[x][y] 的值就等于 他本身 + 绿色的框 + 蓝色的框 - 红色的框(重复叠加了)

即 $remains[x][y] = current + remains[x+1][y] + remains[x][y+1] - remains[x+1][y+1] $

通过上述dp转移方程,我们算出披萨中的每个点的remains值存在数组中备用。

代码

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var ways = function (pizza, k) {
let row = pizza.length;
let column = pizza[0].length;
let remains = new Array(row).fill(null).map((each) => []);
let mod = 1e9 + 7;

for (let i = row - 1; i >= 0; i--) {
for (let j = column - 1; j >= 0; j--) {
let current = pizza[i][j] === 'A' ? 1 : 0;
if (i === row - 1) {
remains[i][j] = (remains[i][j + 1] || 0) + current;
continue;
}
if (j === column - 1) {
remains[i][j] = (remains[i + 1][j] || 0) + current;
continue;
}

remains[i][j] =
remains[i][j + 1] + remains[i + 1][j] + current - remains[i + 1][j + 1];
}
}

let dp = [];
for (let i = 0; i < row; i++) {
dp[i] = new Array();
for (let j = 0; j < column; j++) {
dp[i][j] = new Array(k).fill(0);
}
}

for (let i = row - 1; i >= 0; i--) {
for (let j = column - 1; j >= 0; j--) {
if (remains[i][j] > 0) dp[i][j][0] = 1;

for (let cut = 1; cut < k; cut++) {
// 横着切
for (let r = i + 1; r < row; r++) {
if (remains[i][j] - remains[r][j] > 0) {
dp[i][j][cut] = (dp[r][j][cut - 1] + dp[i][j][cut]) % mod;
}
}
// 竖着切
for (let r = j + 1; r < column; r++) {
if (remains[i][j] - remains[i][r] > 0) {
dp[i][j][cut] = (dp[i][r][cut - 1] + dp[i][j][cut]) % mod;
}
}
}
}
}
return dp[0][0][k - 1];
};

使用 Let’s Encrypt 注册 ssl 证书

本文使用环境

ubuntu 16.04

Let’s Encrypt 简介

为了在您的网站上启用 HTTPS,您需要从证书颁发机构(CA)获取证书(一种文件)。Let’s Encrypt 是一个证书颁发机构(CA)。要从 Let’s Encrypt 获取您网站域名的证书,您必须证明您对域名的实际控制权。您可以在您的 Web 主机上运行使用 ACME 协议的软件来获取 Let’s Encrypt 证书。– 摘自 letsencrypt.org

Cerbot 简介

Certbot is a free, open source software tool for automatically using Let’s Encrypt certificates on manually-administrated websites to enable HTTPS.

Certbot is made by the Electronic Frontier Foundation (EFF), a 501(c)3 nonprofit based in San Francisco, CA, that defends digital privacy, free speech, and innovation. – 摘自 cerbot 官网 certbot.eff.org

Certbot 是Let’s Encrypt官方推荐的获取证书的客户端,可以帮我们获取免费的Let’s Encrypt 证书。

安装 cerbot

1
2
3
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-apache

申请证书 & 配置

1
certbot certonly --webroot -d pandaomeng.com -d www.pandaomeng.com

证书生成完毕后,我们可以在 /etc/letsencrypt/live/ 目录下看到对应域名的文件夹,里面存放了指向证书的一些快捷方式。

这时候我们的第一生成证书已经完成了,接下来就是配置我们的web服务器,启用HTTPS。

配置 web 服务器,启用 nginx

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
27
server {
listen 80;
listen [::]:80;

rewrite ^(.*)$ https://$host$1 permanent;

server_name pandaomeng.com www.pandaomeng.com;
}

server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/pandaomeng.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pandaomeng.com/privkey.pem;
root /var/www/html;

index index.html index.htm index.nginx-debian.html;

server_name pandaomeng.com www.pandaomeng.com;

if ($host != 'www.pandaomeng.com' ) {
rewrite ^/(.*)$ https://www.pandaomeng.com/$1 permanent;
}

location / {
try_files $uri $uri/ =404;
}
}

自动更新 SSL 证书

Let’s Encrypt 证书 90天过期,我们使用 linux 系统自带的 cron 来配置定时任务,在证书即将到期的时候运行命令自动更新证书

执行

1
crontab -e

添加以下内容

1
* */12 * * * certbot renew --quiet --renew-hook "/etc/init.d/nginx reload"

它表示,每 12 小时,系统会执行一次 certbot renew 的命令,执行成功之后重启 nginx

至此,没费 SSL 证书配置完成。

其他

如果使用七牛的图床,可以使用七牛云的免费证书

npm 相关命令记录

查看全局安装过的包

1
npm list -g --depth 0

参数解释

命令 解释
npm list 显示安装过的包
-g 指全局安装过的包
–depth 0 限制输出模块层级

从coding转移git仓库到github

步骤

  1. 获取源git仓库的地址

    如: git@git.coding.net:pandameng/xxx.git

  2. 在github上创建一个空的仓库

    如:git@github.com:pandaomeng/xxx.git

  3. 随意找个目录,执行以下命令

    1
    2
    3
    git clone --bare git@git.coding.net:pandameng/xxx.git # 源仓库地址
    cd xxx.git/
    git push --mirror git@github.com:pandaomeng/xxx.git # 目标地址

es6 Iterator 和 Generator

Iterator 基本概念

  1. 遍历器(Iterator)就是一种统一的接口机制,任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
  2. Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
  3. 遍历器本质上是一个指针对象
  4. 每一次调用next()方法,都会返回一个具有value和done属性的对象

基本概念

  1. generator 函数和 普通函数写法不同, 在 function 关键字和 函数名之间有个 * 号
  2. generator 函数的执行结果会返回一个遍历器对象,只有调用遍历器对象的 next() 方法
  3. yield 只能用在 generator 函数里使用。

JS 的原型

对象原型

  1. 对象的原型可以通过 Object.getPrototypeOf(obj) 或者 obj.__proto__(弃用)得到

  2. 区别对象的原型 obj.__proto__ 和构造函数的 prototype 属性

    Object.getPrototypeOf(new Foobar()) 与 Foobar.prototype 指向同一个对象

  3. 对象原型 即 对象的原型 即 对象的原型对象,因为原型也是一个对象

  4. 区别 doSomething() 和 new doSomething()

  5. 原型对象:构造函数的prototype属性所指向的对象

  6. Object.create() 从指定的原型对象创建一个新的对象

    1
    var car = Object.create(Car.prototype)
  7. constructor 是构造函数自带的属性,可以通过 constructor.name 获取构造器名字

  8. js 通过原型链向上查找属性

  9. 原型的 constructor 属性指向构造函数,构造函数又通过 prototype 属性指回原型

new 操作符

  1. 用法:

    new constructor[([arguments])]

  2. new 原理

    • 创建一个空的对象(即 {} )
    • 将对象的 __proto__ 指向构造函数的prototype,即继承
    • 带参数调用构造函数的constructor(即构造函数本身),但是this的值指向刚创建的那个对象
    • 如果constructor有返回,则 new 的结果就用他的返回值,如果没有就使用this
0%