git入门

至少你得先安装git bash 然后才能启动

右键git bash here 依次输入下面的命令 不要管为啥 用多了自然而然就知道了

配置

1
2
3
4
5
git config --global user.name xxx #方便产品经理找(怼)你
git config --global user.email yyy #方便产品经理找(怼)你
git config --global push.default matching # 原因参考: https://stackoverflow.com/a/21865319/1262580
git config --global core.quotepath false #防止文件名变成数字
git config --global core.editor "vim" # 使用vim编辑提交信息

常用的只有几个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
git status -sb 查看文件信息
git add . 添加当前目录里所有文件到暂存区
git commit -m '提交信息' 提交到本地仓库
git pull 建议先执行 同步远程仓库的代码
git push 提交到远程仓库
git log
mkdir a 创建目录a
mkdir -p 'a/b/c'目录路径
rm -r 递归删除目录 但是每次都会问你确认删除吗?
rm -rf 强制删除目录 不停的问你确认删除吗?
echo "hello">newFile.txt 创建文件
rm newFile.txt 删除文件
mv 1.txt 2.txt 重命名==》 把1.txt移动到2.txt
pwd 显示当前目录
ls 查看路径 所有以.开头的都不显示
ls -a 路径 显示所有文件及目录
ls -l 路径 显示更多信息 读写权限
ls -al 路径 显示所有文件及目录 还有读写权限
curl -L http://www.baidu.com > baidu.html 下载文件

如果你不知道一个命令啥意思 又不想去查 可以安装插件tldr to long don’t read

1
2
cnpm  install -g tldr
tldr cat 会告诉 cat命令的用法

配置远程仓库

1.进入 https://github.com/settings/keys
2.如果页面里已经有一些 key,就点「delete」按钮把这些 key 全删掉。如果没有,就往下看
3.点击 New SSH key,你需要输入 Title 和 Key,但是你现在没有 key,往下看
4.打开 Git Bash
5.复制并运行 rm -rf ~/.ssh/* 把现有的 ssh key 都删掉,这句命令行如果你多打一个空格,可能就要重装系统了,建议复制运行。
6.运行 ssh-keygen -t rsa -b 4096 -C “你的邮箱”,注意填写你的邮箱!
7.按回车三次
8.运行 cat ~/.ssh/id_rsa.pub,得到一串东西,完整的复制这串东西
9.回到上面第 3 步的页面,在 Title 输入「我的第一个 key」
10.在 Key 里粘贴刚刚你你复制的那串东西
11.点击 Add SSH key
12.回到 Git Bash
13.运行 ssh -T git@github.com,你可能会看到这样的提示:

Are you sure you want to continue connecting (yes/no)?

输入 yes 回车……问你话你就答,别傻在那
14.然后如果你看到 Permission denied (publickey). 就说明你失败了,请回到第 1 步重来,是的,回到第 1 步重来;如果你看到 Hi FrankFang! You’ve successfully authenticated, but GitHub does not provide shell access. 就说明你成功了!
好了,终于 TMD 添加了一个无聊的 SSH key,不要问我这个有什么用,因为一会儿你就会用到它,你想了解原理就看这篇 文章

如果要讲清楚,太浪费时间了,我们只是想用用 GitHub 而已。

一台电脑只需要一个 SSH key 一个 SSH key 可以访问你的所有仓库,即使你有 1000000 个仓库,都没问题
如果你新买了电脑,就在新电脑上重新生成一个 SSH key,把这个 key 也上传到 GitHub,它可以和之前的 key 共存在 GitHub 上 如果你把 key 从电脑上删除了,重新生成一个 key 即可,替换之前的 key

解释每个命令推荐你去这个

https://explainshell.com/explain?cmd=ls+-lrt

如何上传更新

你在本地目录有任何改变就按下面的命令

1
2
3
4
git add 文件路径
git commit -m '提交信息'
git pull 相信我 你一定会忘记这个
git push

如果你已经commit了 但是发现这次改的需要跟上次commit合并

1
2
3
4
# 修复之前的commit,和本次修改一并提交
git commit --amend -v .
# 上传到远程仓库
git push

其他 中级命令

1
2
3
4
5
6
7
8
9
10
# 给仓库 git@github.com:xxxxxxx.git 起名叫做 origin
git remote add origin git@github.com:xxxxxxx.git 将本地仓库与远程仓库关联
git remote set-url origin git@github.com:xxxxx.git 上一步手抖了,可以用这个命令来挽回
git branch 新建分支
git merge 合并分支
git stash 通灵术
git stash pop 反转通灵术
git revert 后悔了
git reset 另一种后悔了
git diff 查看详细变化

回到之前版本 git reset

  • 慎用,使用前一定要把当前所有代码 commit了,它会使没 commit的内容消失

假设你总共提交了三次代码

1
2
3
第一次 commit版本号 ba0
第二次 commit版本号 eba
第三次 commit版本号 c11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 此时你在第三次提交这个版本
# 查看当前commit内的所有版本号
git log
这样会看到 倒序的提交信息
第三次 commit版本号 c11
第二次 commit版本号 eba
第一次 commit版本号 ba0

# 你想回到第一次提交的代码版本
git reset --hard ba0

# 此时代码回到了版本1
# 再次 git log,你发现只能看到 第一次提交的所有版本信息
第一次 commit版本号 ba0

# 我想回到版本3咋办?
git reflog
这样就列出了 所有提交版本号,和 reset等操作

git loggit reflog区别

  • git log 会上传到远程仓库
  • git reflog 是你私有的日记

高级操作简化git 命令:alias

添加如下内容到 ~/.bashrc

1
2
3
alias ga="git add"
alias gc="git commit -m"
alias gl="git pull"
  • source ~/.bashrc

通灵术 git stash

假设你有实验性代码 aa.js 不能提交,但你又不想删除

1
2
3
4
5
6
7
8
9
# 第一步 添加到暂存区
git add aa.js
# 第二步 这样 aa.js 就被放入 结界中了
git stash
# 此时你就看不到 aa.js了
# 第三步 通灵术召唤 aa.js
git stash pop

git stash 就是用来临时把文件藏起来

git分支操作

git分支操作

如果你想更深入,可以看 廖学峰 的git教学

  1. 查看分支
1
git branch
  1. 新建分支
1
2
3
git branch 「分支名称」
#形如
git branch a001
  1. 切换分支
1
2
3
git checkout 「分支名称」
#形如
git checkout a001
  1. 创建+切换分支
1
git checkout -b 「分支名称」
  1. 合并某分支到当前分支
1
2
3
git merge 「分支名称」
#形如
git merge a001
  1. 删除分支
1
2
3
git branch -d 「分支名称」
#形如
git branch -d a001
  1. 添加本地分支到远程分支
1
git push origin 「分支名称」
  1. 查看远程分支
1
git branch -r
  1. 删除远程分支
1
2
3
4
5
git branch -r -d origin/「分支名称」  
git push origin :「分支名称」
#形如
git branch -r -d origin/a001
git push origin :a001

例子

场景如下

你在github上新建了一个仓库 如 bbb

在你本地创建 bbb目录 并切换到 bbb目录

执行如下命令,你会初始化一个 README.md文件

1
2
3
4
5
6
echo "# bbb" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:slTrust/bbb.git
git push -u origin master
  1. 当前分支为master 创建 1.txt 输入 2222 并且提交到远程分支
1
2
3
4
echo '2222'>1.txt
git add 1.txt
git commit -m 'add 1.txt'
git push
  1. 新建分支并切换到新建的分支
1
2
git branch a001
git checkout a001

运行 git branch 你会发现 「a001」高亮了

  1. 新分支下「a001」新建 2.txt 并提交到本地仓库
1
2
3
echo '3333'>2.txt
git add 2.txt
git commit -m 'add 2.txt'
  1. 如何把新分支提交到远程仓库并建立远程分支
1
git push origin a001

刷新浏览器你的github上就多了一个 a001 的分支

  1. 删除远程分支
1
2
3
4
5
6
7
8
#你可以查看远程分支有哪些 然后再进行删除
git branch -r
# 你会看到远程有哪些分支

#删除远程分支
git branch -r -d origin/a001
git push origin :a001
# 刷新浏览器你会发现你的 a001 分支已经不在了

注意事项

  • 新建分支的时候 git branch a001
  • 会复制你当前分支(比如我的是master) 的所有内容复制到你的新分支里(a001分支)

合并冲突了咋办

前置条件,两个分支

  • master
  • a01

index.html 都修改了第三行

你现在在 master分支准备合并

1
2
3
4
5
6
7
8
9
10
11
git merge a01
# 提示你冲突了

如果你用的是 vscode
打开这个 index.html

你会发现 冲突代码被高亮了

head是当前分支的改动
======= 分割线
其他分支的变动

上传代码到远程仓库

  • 本地配置ssh
  • 远程建立仓库如 demo-1
  • git remote add origin git@demo-1
    • 在本地添加远程仓库地址
    • origin是远程仓库的默认名字
  • git push -u origin master

上传其他分支

1
2
3
4
5
6
# 方法一
git push origin x:x

# 方法二
git checkout x
git push -u origin x

一个本地仓库上传多个远程仓库

1
2
git remote add repo2 git@xxx
git push -u repo2 master

hexo换主题

主题合集

https://github.com/hexojs/hexo/wiki/Themes

随便找一个主题,进入主题的 GitHub 首页,比如我找的是 Jacman

复制它的 SSH 地址或 HTTPS 地址,假设地址为 https://github.com/wuchong/jacman.git

比如我现在在自己的hexo blog根目录 myBlog

一 进入themes目录

1
$ cd themes

二 克隆该风格到你本地的主题

1
$ git clone https://github.com/wuchong/jacman.git

三 切换回项目目录

1
$ cd ..

四 将 _config.yml 的第 75 行改为 theme: jacman,保存

五 重新初始化你的博客

1
$ hexo generate

六 部署

1
$ hexo deploy

七 等一分钟,然后刷新你的博客页面

jacman这个主题的Blog

上传源代码

注意「你的用户名.github.io」上保存的知识博客,并没有保存 hexo 目录,你需要再创建一个名为 generate_hexoBlog 的空仓库,用来保存 myBlog 里面的代码。

1.在 GitHub 创建 generate_hexoBlog 空仓库

顺序执行第一个段落里的命令 …or create a new repository on the command line

1
2
3
4
5
6
echo "# ddd" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:slTrust/generate_hexoBlog.git
git push -u origin master

2.命令执行后,记住,千万别用 HTTPS 地址。

这样备份之后,你就不会因为误删 myBlog 目录而痛哭了。以后每次 hexo deploy 完之后,都要 push 一下代码,以备份。

这个 generate_hexoBlog 就是用来生成博客的程序,而「你的用户名.github.io」仓库就是你的博客页面。

3.注意如果你 hexo generate 会在public里生成文章页面 具体的就是index.html 如果内容为空

那是因为你设置的主题依赖没安装

1
2
3
4
5
切换到 themes目录
git clone 你想要的风格仓库地址
cd ..
hexo generate
hexo deploy

hexo环境搭建

Welcome to My First Hexo Blog!

hexo环境搭建

前置条件

  1. 请先确保你有node 一路next 不要手残把默认选中的add to path取消
  2. 请先确保你有 git bash 否则没有往下看的必要了

在 GitHub 上新建一个空仓库 repo

仓库名称是你github账户名 比如我的是slTrust 那么你新建的仓库名就会是这个格式 slTrust.github.io

安装 Hexo 命令行工具

1
$ npm install -g hexo-cli

初始化你的博客比如 myBlog

1
$ hexo init myBlog

进入你的博客目录

1
$ cd  myBlog

安装依赖

1
$ npm i

新建第一个博客 firstBlog

这样会出现一个 .md文件 在 source/_posts目录下

1
$ hexo new  firstBlog

编辑这个文件,注意是markdown语法

1
$ start source/_posts/firstBlog.md

start _config.yml,编辑网站配置

  • 把第 6 行的 title 改成你想要的名字
  • 把第 9 行的 author 就是作者
  • 把最后一行的 type 改成 git
  • 在最后一行,与 type 平齐,加上一行 repo: 仓库地址 如 git@github.com:slTrust/slTrust.github.io.git

    注意这个配置的 repo参数 仓库名是 你新建的空仓库 一定一定一定是你账号名的格式 slTrust.github.io
    这样你的 repo 参数就可以设置为这个 git@github.com:slTrust/slTrust.github.io.git

step

step

安装 git 部署插件

1
$ npm install hexo-deployer-git --save

发布

1
$  hexo deploy

进入你的github仓库

注意注意 你刚刚新建的仓库名 一定是这个格式 你的github用户名.github.io 如 我的是 slTrust.github.io
预览地址!

如何创建博客

新建博客

1
$  hexo new 'test002'

编辑你的博客 复制显示的路径,使用 start 路径 来编辑它

1
$  start 'source/_posts/test002.md'

重新初始化你的博客

1
$ hexo generate

部署

1
$ hexo deploy

命令行基础

### 为什么学命令行

  1. 这关系到一个程序员的初始
  2. 现代的前端工具都没有提供图形界面、只有命令行界面、从使用角度、前端必须学会命令行
  • Gulp/Webpack/SASS/Node.js/Vue/React/Angular
  • 这么多工具你不学咋混啊

基本概述(注意请使用gitbash的命令窗口)

  1. 文件与目录(文件夹)
  2. ~ / . .. $的意思

~ 代表用户目录(linux/unix是没有盘符的概念的)

windows前提安装了gitbash

1
2
cd ~   //切换到用户目录
pwd //输出当前路径 ==> /c/Users/hjx

/ 代表硬盘(没有c d e f盘)

. 代表当前目录

.. 代表上一层目录

$ 代表没有实际意义只是提示你可以输入命令了

命令行难吗?

实际上命令行简单,你觉得难是因为你在命令行上待的时间还不足24小时,而你在window上待的时间超过了10年,简单你学了那么多年?

如何学习

首先背单词:

  • 英文 翻译
  • directory 目录、文件夹
  • file 文件
  • make 新建
  • remove 删除
  • move 移动
  • copy 复制
  • list 罗列
  • link 链接
  • find 查找
  • echo 发出回音、重复
  • touch 触摸
  • change 改变

背下来了吗?每个单词都很短,应该不难背。好的,你基本已经学会命令行了。接下来我们学习这些单词的缩写

缩写

命令 全写 缩写
创建目录 make directory mkdir
删除 remove rm
移动 / 重命名 move mv
复制 copy cp
罗列 list ls
改变目录 change directory cd

缩写规则就是:删掉元音字幕(A E I O U),保留前 2 到 3 个辅音字母

好了,你已经学会 50% 了,接下来我们来试试。

实践

  1. cd ~/Desktop 进入桌面
  2. mkdir demo-1 创建目录,这时你可以切到桌面,看到 demo-1 目录
  3. rm -rf demo-1 删除目录
  4. touch 1.txt 创建文件,如果你发现文件后缀不见了,请让该死的 Windows 显示文件后缀
  5. mv 1.txt 2.txt 这样我们就把 1.txt 移到 2.txt 了,也就是重命名

常见的自带命令

  • 操作 命令
  • 进入目录 cd
  • 显示当前目录 pwd
  • 创建目录 mkdir 目录名
  • 创建目录 mkdir -p 目录路径
  • 我是谁 whoami

  • 查看路径 ls 路径
  • 查看路径 ls -a 路径 包含隐藏的一些文件
  • 查看路径 ls -l 路径
  • 查看路径 ls -al 路径

  • 创建文件 echo ‘1’ > 文件路径
  • 强制创建文件 echo ‘1’ >! 文件路径
  • 追加文件内容 echo ‘1’ >> 文件路径
  • 创建文件 touch 文件名
  • 改变文件更新时间 touch 文件名

  • 复制文件 cp 源路径 目标路径
  • 复制目录 cp -r 源路径 目标路径 递归的复制一个目录和其内容

  • 移动节点 mv 源路径 目标路径

  • 删除文件 rm 文件路径
  • 强制删除文件 rm -f 文件路径
  • 删除目录 rm -r 目录路径 递归的删除目录每次删都问你一次
  • 强制删除目录 rm -rf 目录路径 递归的删除目录而且不提示

  • 查看目录结构 tree
  • 建立软链接 ln -s 真实文件 链接

如何学习我目前还没有掌握的命令?

Google: Linux 查看文件内容

快捷键

  • ↑↓ 上一命令 / 下一命令
  • !! 上一命令占位符
  • Tab 自动补全路径
  • Alt+. 上一命令的最后一个参数
  • && 前面的执行成功了,再执行后面的
  • || 前面的执行失败了,就执行后面的
  • ; 前面执行完了,不管成功失败,就执行后面的
  • “>” 重定向
  • | 管道

    命令行技巧

    ~/.bashrc 文件的功能很强大。

自动运行

  1. 首先 touch ~/.bashrc 创建一下这个文件
  2. start ~/.bashrc 选用编辑器编辑这个文件,内容为 echo ‘Hi’
  3. 你也可以用命令行编辑文件 echo “echo ‘hi’” >> ~/.bashrc
  4. 关闭退出 Git Bash,然后打开 Git Bash,是不是看到了 Hi,这说明每次进入 Git Bash,就会优先运行 ~/.bashrc 里面的命令
  5. 重新编辑 ~/.bashrc,内容改为 cd ~/Desktop,重启 Git Bash,有没有发现默认就进入桌面目录了?
    你可以用 ~/.bashrc 在进入 Git Bash 前执行任何命令,十分方便。

alias

1.在 ~/.bashrc 里新增一行 alias f=”echo ‘frank is awesome’”,等于号两边不能有空格,你最好一个字都不要错。
2.运行 source ~/.bashrc,作用是执行 ~/.bashrc
3.运行 f,就会看到 frank is awesome
4.也就是说,现在 f 就是 echo ‘frank is awesome’ 的缩写了,利用这个技巧,我们可以把很多常见的命令缩写一下,比如

1
2
3
4
5
6
7
alias la='ls -a'
alias ll='ls -l'
alias gst='git status -sb'
alias ga='git add'
alias ga.='git add .'
alias gc='git commit'
alias gc.='git commit .'

保存退出,然后运行 source ~/.bashrc
这样一来,你的 Git 操作就会简单很多:

1
2
3
4
5
ga 1.txt
ga .
gc 1.txt
gc.
gst

接下来说两个目前用不到的技巧。

环境变量

还可以在 ~/.bashrc 里面设置一些环境变量,比如你可以在 ~/.bashrc 里面添加一行

1
export SASS_BINARY_SITE="https://npm.taobao.org/mirrors/node-sass"

那么以后你安装 node-sass 的时候就不会因为被墙而报错了。以后会用到的,现在先说一下。

设置 PATH

export PATH=”目录的绝对路径:$PATH”
可以在 PATH 里添加一个目录,不要运行,等用到的时候再来查。

计算机是如何存数据的

请看最后的结论

第一步(内存)如何存0和1

实际就是晶体管里的电信号

  • 1就充电
  • 0就不充电

读取

  • 电量大于50%就是1
  • 电量小于50%就是0

第二步 如何存数字

十进制转二进制 (十进制)37 ==》(二进制)100101

计算机只存0和1

第三步 存字符

ASCⅡ码

  • 如果存 a 那么a对应的asc码就是 97
  • 而97对应的二进制就是0110 0001

第四步 存中文

GB2312 国标码 共收录6763个常用字

后来为了存储生僻字、繁体字、日语、韩语等微软推出了GBK

第五步 如何存储所有字符

Unicode 将全球所有字符编号

2016年6月总共存有12万个字符

第六步 如何将Unicode存到计算机里

变长存储有可能一个字节有可能三个字节

低性价比

a > 00000000 00000000 00000000 01100001 = 0061
你 > 00000000 00000000 01001111 01100000 = 4F60

高性价比 UTF-8

a > 01100001
你 > 11100100 10111101 10100000

utf-8的原理

  1. if 你表示的数字小于七位

    1
    0XXX XXXX
  1. else if 你的数字大于七位加4位 也就是11位,前面110代表占用两字节的空间

    1
    110XXXXX 10XXXXXX
  2. else if 表示两个字节,前面1110代表占用三字节的空间

    1
    1110XXXX 10XXXXXX 10XXXXXX  所有X位加一起16位 两个字节就可以表示所有的汉字
  3. else 11110代表占用四个字节的空间

    1
    11110XXX 10XXXXXX 10XXXXXX 10XXXXXX

规律就是你由几个字节组成 最左面的冗余信息就是用来表示你占用几个字节

你实际占用两个字节就是11,三个字节就是111 四个字节就是1111

UTF-8是一种编码方式,不是字符集

现实问题(这些字符集的发明时间)

  • 1981年 GB2312
  • 1993年 GB13000 unicode 1.1
  • 1993 -1999 GBK
  • 1995年 js诞生的时候 unicode3还没有出来
  • 1999年 Unicode3.0
  • 2000年 GB18030 兼容GBK并扩展

现在开发网站一定要使用UTF-8

现实问题2

JS使用了Unicode字符集,但是没有使用UTF-8

JS用的UCS-2编码(已经没人使用的编码)最多表示2个字节的汉字
因为1995年UTF-16还没有发明,JS也不想使用UTF-32

1
'\u4f60'  //代表 '你' 这是unicode

后果

ES5无法表示\uFFFF之后的字符,也就是大于2个字节的(如\u1D306)某些情况会出bug

1
2
3
var str = '\u1D306'  
str.length ? //2
//js会把 1D30看成一个字符 6是另一个字符

但是HTML支持utf-8

1
2
3
4
<h1 id="xxx">&#x1d306;</h1> 

xxx.innerText.length = ? //2
// 显示的是一个字符,但是JS取出来的长度却是 2 这就是 JS在ES5之前编码上的bug

什么是前端

前端行业介绍

简单来说就是你看到的无处不在的网页
如果一个公司没有一个资深的前端坐镇,他都不能自称为”互联网公司”
互联网+ 的时代 微博、团购、P2P金融、微商、在线培训都是借助前端在传播
还有一个原因 就是从乔布斯开始一个词被提到了风口浪尖就是 “用户体验”

对用户体验的提升

  • 以前你打开网页可能提示你浏览器等级太低建议安装更高版本
  • 比如中国银行只能用ie打开 而招商银行能用chrome打开 这样我就首选用招商银行
  • 你用过好的体验再去体验差的你自己就接受不了

如何自学前端

现在要学的

自学前端难度如何?

由于知识点过多你会出现不知道学习什么,哪里要学哪里不学的困境
知乎上的论调就是 给你一排的链接 并说你照着学就能会啦!(神经病和天才才能学会)

在学习一个技术时是要至少抱一次大腿的

说自学成功的肯定是隐藏了他抱大腿的行为
找能帮你自学的人

培训最快

节省时间精力:甄别知识,学80%公司里需要的
提高成功几率:学习圈子
避免误入歧途:错误知识、不良习惯

如何成为很厉害的程序员

那些NB的程序员一开始就很厉害,所以他以后也很厉害

记忆曲线

现在要学的

如何巩固

冷笑话

以前有一个概率学家,他为人特别谨慎,然后做什么事就特别小心安全,有一次他要参加演讲去坐飞机,他特别怕发生劫机事件(因为刚经历了911)于是他自己带了个炸药上飞机!

他为什么自己带炸药呢?因为他调查了飞机的事故记录,规律就是从来没有一架飞机上出现两拨劫匪!我自己带不就好了吗

  • 很少有人能学习一次知识就记住它
  • 为什么不直接进入第二次呢?
  • 我先预习一下
  • 别人指导的时候就是第二次
  • 成功率会提高

学习顺序

更好的学

推荐

名人案例

阮一峰:经济学博士有很多文章,涉及领域广泛,英文特别好

我很崇拜他,因为他敢于挑战自己从一个经济学的老师变成一个前端这已经很颠覆了。

他为什么当前端:他整理了10年来的博客,发现自己毫无建树,所以他想变一变,然后阿里的前端老大给他P7(50-80年薪)其实这对他没有吸引力,而且从老师到前端社会地位也降低了。来之前他去了沙哈拉沙漠,因为他想去一下从来没有去过的地方,因为他现在要去一个从来没去过的领域,他希望在沙漠里遇到一些没有看见过的东西,但是他去了之后发现很失望的。为什么?因为他以为去了沙漠以后就会特别渴,生活条件特别差,但是并不是这样的,他发现沙漠已经被商业化了,有水有旅馆有人给你拍照,然后发现“这根本不是我想要去的地方,而是我一直在的地方–现代社会”所以他就有一种更强烈的愿望—去我没有去过的地方!

如果你在一个地方待很久,然后你不是特别满意,那你最好换一个地方

学习建议

  • 搜google 不要搜百度(有广告)
  • 搜MDN 不要搜w3school中文网

推荐

如果是 HTML

你就搜MDN

如果是 CSS

你就 CSS Tricks

如果是 JS

你就 MDN 或者 阮一峰的js教程 如果买书只推荐高程,如果你这本都看不完,买其他的也没用。

推荐

MDN全称是Mozilla Developer Network

知识分类

  1. 工具使用知识:把你能看到的按钮全点一次
  2. 概念知识:一些高级特性都建立在概念之上如闭包,回调函数(诸如物理公式,化学方程式)
  3. 工程实践知识:代码写得越多越好(img src属性写错成scr卡了一天/浏览器缓存没有清除卡了一天)
  4. 经验:(有一个工厂机器出了毛病,新来的工人都搞不定,然后就找老师傅来,老师傅来了看了一眼运作效果就说把某一个地方螺丝拧一下就好了,他为社么知道呢?他自己都不知道为什么知道,因为平时被坑多了,以至于看到有问题就知道为什么错了。人机合一,我们是人码合一)

我们要做什么平时要总结,这就是经验。

滚雪球原理

fake it until you make it

如果你想变成一个很厉害的人就一直假装你是一个很厉害的人

比如你看学霸平时都是在看书就连听歌都是为了学英语,那就以他的行为要求自己。

学习的过程看一遍是不够的,做一遍也是不够的

你至少看两遍

你说看一遍高程就全会了的可能性?

ES6_新增容器(十一)

容器

Set

数组去重复

1
2
3
4
5
6
7
8
//ES6之前
var obj = {};
[1,1,2,2].forEach(item=>obj[item]=1);
var arr2 = Object.keys(obj)

// ES6之后
var set = new Set([1,1,2,2])
var arr2 = [...set]

WeakSet

Map

  • set(key, value): 向 Map 中加入或更新键值对
  • get(key): 读取 key 对用的值,如果没有,返回 undefined
  • has(key): 某个键是否在 Map 对象中,在返回 true 否则返回 false
  • delete(key): 删除某个键,返回 true, 如果删除失败返回 false
  • clear(): 删除所有元素
1
2
3
let map = new Map()
map.set('a', '1');
console.log(map.get('a'))

WeakMap

  • 它的key必须是对象,value可以是任意的
  • 因为key是弱引用的,所以不可迭代

Map和Weakmap区别

  • Map 的键可以是任意类型,WeakMap 的键只能是对象类型
  • WeakMap 没有遍历方法

ES6_Proxy(十)

Proxy

  • Proxy从英文可以知道是“代理”的意思
1
const proxy = new Proxy(target, handler)
  • target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理
  • handler 一个通常以函数作为属性的对象,用来定制拦截行为
1
2
3
4
5
const origin = {}
const obj = new Proxy(origin, {});
obj.a = 1;
console.log(obj.a) // 1
console.log(origin.a) // 1

举个例子,二手房购买

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const 房东 = {}
const 代理中介 = new Proxy(房东, {
get: function (target, propKey, receiver) {
return '没问题'
}
});

代理中介.看房 // 没问题
代理中介.心理底价 // 没问题
代理中介.房屋安全隐患 // 没问题

origin.看房 // undefined 你说啥?
origin.心理底价 // undefined 你说啥?
origin.房屋安全隐患 // undefined 你说啥?
  • 这就好比给房东.get设置了一层代理,所有get操作都会返回中介定制的内部套路“没问题”
  • 代理只会对 proxy 有效
  • 如 目标对象 房东 就没有任何效果

handler 的一些方法

你可以对get操作做判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const origin = {
a: 1
}
const handler = {};
const obj = new Proxy(origin, {
get: function (target, key) {
if (key in target) {
return target[key]
} else {
return undefined
}
}
});
console.log(obj.a) // 1
console.log(origin.a) // 1
console.log(origin.b) // undefined

可撤销的 proxy

1
2
3
4
5
const target = { name: 'vuejs'}
const {proxy, revoke} = Proxy.revocable(target, {})
proxy.name // 正常取值输出 vuejs
revoke() // 取值完成对proxy进行封闭,撤消代理
proxy.name // TypeError: Revoked

Vue3用 Proxy重写

为什么?用 Proxy

Proxy之前, JS中提供Object.defineProperty 允许对对象的 getter/setter 进行拦截

区别

  • Object.defineProperty是在 对象的“属性”上做处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// ...
if (Dep.target) {
// 收集依赖
dep.depend()
}
return value
},
set: function reactiveSetter (newVal) {
// ...
// 通知视图更新
dep.notify()
}
})

对象新增属性为什么不更新

1
2
3
4
5
6
7
8
9
10
11
12
13
// vue2里
data () {
return {
obj: {
a: 1
}
}
}
methods: {
update () {
this.obj.b = 2
}
}

因为 data初始化是在 created 之前,并对 data 绑定一个 Observer,之后 data 的更新都会通知依赖收集器 Dep 触发UI更新

  • 而当你在 data 初始化完成后,在添加属性,不会帮你绑定 观察者
  • 这也是为啥 Vue 提供了 $set ,本质就是让你手动添加 observer

数组用索引不会触发更新问题

  • 这个和data 对象后续新增属性一样
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
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]

methodsToPatch.forEach(function (method) {
// 缓存原生数组
const original = arrayProto[method]
// def使用Object.defineProperty重新定义属性
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args) // 调用原生数组的方法

const ob = this.__ob__ // ob就是observe实例observe才能响应式
let inserted
switch (method) {
// push和unshift方法会增加数组的索引,但是新增的索引位需要手动observe的
case 'push':
case 'unshift':
inserted = args
break
// 同理,splice的第三个参数,为新增的值,也需要手动observe
case 'splice':
inserted = args.slice(2)
break
}
// 其余的方法都是在原有的索引上更新,初始化的时候已经observe过了
if (inserted) ob.observeArray(inserted)
// dep通知所有的订阅者触发回调
ob.dep.notify()
return result
})
})

Proxy 的优势

  • ES6升级,提供了语言层级的支持
  • Object.defineProperty 监听的是对象身上的 “属性”
    • 当你新增属性你要手动绑定 observe
  • Proxy 劫持的是整个对象,能深度监听

ES6_迭代器和生成器(九)

迭代器

看例子~~

1
2
3
4
5
6
7
8
9
10
11
let version = 0
function 发布(){
version++;
return version;
}

发布() // 1
发布() // 2
发布() // 3
发布() // 4
发布() // 5
  • 第一次升级
1
2
3
4
5
6
7
8
function 发布器(){
return {
next:undefined
}
}

let a = 发布器();
a.next // undefined
  • 第二次升级,补充next
1
2
3
4
5
6
7
8
function 发布器(){
return {
next:function(){}
}
}

let a = 发布器();
a.next // 是个函数
  • 第三次升级: next的函数返回一个对象
1
2
3
4
5
6
7
8
9
10
11
12
13
function 发布器(){
return {
next:function(){
return {
value:1
}
}
}
}

let a = 发布器();
a.next // 是个函数
a.next() // 返回对象 {value:1}
  • 第四次升级: 发布器内部有一个初始变量,在 next调用的时候 自增
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function 发布器(){
let _value = 0;
return {
next:function(){
_value++;
return {
value:_value
}
}
}
}

let a = 发布器();
a.next // 是个函数

a.next() // 返回对象 {value:1}
a.next() // 返回对象 {value:2}
a.next() // 返回对象 {value:3}
  • 第五次升级: 发布器内部有done 标志,和边界条件max
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
function 发布器(){
let _value = 0;
let max = 5;
return {
next:function(){
_value++;
if(_value > max){
throw new Error('你突破了天际,已经撞墙了!')
}
if(_value === max){
return {
value:_value,
done:true
}
}
return {
value:_value,
done:false
}
}
}
}

let a = 发布器();
a.next // 是个函数

a.next() // 返回对象 {value:1}
a.next() // 返回对象 {value:2}
a.next() // 返回对象 {value:3}
a.next() // 返回对象 {value:4}
a.next() // 返回对象 {value:5}
a.next() // 报错 Uncaught Error: 你突破了天际,已经撞墙了!

这就是迭代器

迭代器

  • 它有一个next方法,并返回一个 对象形如 {value:你的value,done:false}

生成器:迭代器的语法糖

写法

1
2
3
function 后加一个 "*"

function内部定义 yield
  • 在看我们的代码
1
2
3
4
5
6
7
8
9
10
11
function * 发布器(){
var version = 0;
while(true){
version++;
yield version;
}
}
var a = 发布器();
a.next() // {value: 1, done: false}
a.next() // {value: 2, done: false}
// ... 由于是 while(true), 你可以一致 a.next() 到世界尽头

解析代码

1
2
var a = 发布器(); // 代码运行到  version++ , yield之前
a.next() // 第一次next,把结果吼出来 {value: 1, done: false} ,同时再次进入while 运行到 version++;再次停到 yield之前

for …of

  • 针对数组
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

// 这个是迭代
for(let item of [3,2,1]){
console.log(item)
}
// 打印 3,2,1


// 这个是遍历
for(let item in [3,2,1]){
console.log(item)
}
// 打印 0 ,1,2

// 数组添加额外属性!!!

var c = [3,2,1]
c.x = 'y'
// 这个是遍历
for(let item in c){console.log(item)}
// 打印 0,1,2,x

// 这个是迭代
for(let item of c){console.log(item)}
// 打印 3 ,2,1

再看object

1
2
3
4
5
6
var d = {a:1,b:2,c:3}
// 这个是遍历
for(let i in d){console.log(i)} // 打印的是key: a,b,c

// 这个是迭代
for(let i of d){console.log(i)} // 报错 d is not iterable

区分 for in 和 for of

  • for in 是旧语法
    • 无论数组还是对象都可以 “遍历”
  • for of 是新语法

    • 数组可以迭代
    • 对象是不可迭代的
  • 数组和对象都能 “遍历” for in

  • 数组能 “迭代” 对象 不能 “迭代”

什么样的东西能迭代呢?

  • 数组
  • 符合某些特征的东西 含有 obj[Symbol.iterator] 它就是一个可迭代的
1
2
3
4
5
let arr = []
arr[Symbol.iterator] // 返回一个函数

let obj = {}
obj[Symbol.iterator] // undefined

让对象可迭代

1
2
3
4
5
6
7
8
9
10
11
let obj = {a:'aa',b:'bb',c:'cc'}
obj[Symbol.iterator] // undefined

// 让对象支持迭代
obj[Symbol.iterator] = function*(){
let keys = Object.keys(obj)
for(let i=0;i<keys.length;i++){
yield obj[keys[i]]
}
}
for(let i of obj){console.log(i)} // aa bb cc

为啥数组可以迭代,对象不行?

因为数组知道如何按顺序访问,但是对象不知道取key的顺序是不确定的

Symbol.iterator 是啥?

  • 它是一个 Symbol
  • 它是一个独一无二的值
  • 它就是为了实现迭代器,实现可迭代对象

假如没有 Symbol

1
2
3
4
5
// 难道我们要这样?
object['__iterator__'] // 这样好吗? ,会不会冲突,会不会和现存的内容覆盖?
// 如果用Symbol呢?
这样跟谁都没有关系
object[Symbol.iterator]