for(const num of list) 为什么可以运行?

JS 在 ES6 加入了 const 关键字,除了加入了其它语言一样的常量特性,不仅没有 var 的变量提升,还有块级作用域特性。

常量是不可变的,在 ES6 中指的是常量存储地址的不变性,而不是常量的不可变。所以在 const arr = [];arr.push(1) 是可以运行的。

了解这个之后,我们再看看 for 循环

1
2
3
for (const i = 0; i < 5; i++) {
console.log(i)
}

那么这个就是错的,如果我们用 C++ 改写,并且输出的是循环量的地址的话,那么就可以看出,for 循环每次循环都是相同的地址。

1
2
3
4
5
6
7
int main() {
for (int i = 0; i < 3; i++) {
cout << &i << endl;
}
return 0;
}
// 都是输出一样的

根据 const 是不可修改原地址值得特性,所以说 const 用在 for 循环中是错误的。不过下面使用 for...of 就是对的。

1
2
3
4
5
const list = [1, 2, 3, 4, 5]

for (const num of list) {
console.log(num)
}

根据上述说明,可以推断出 for...of 每次循环都是产生一个新的地址,并把新的值指向这个地址。那么 for...of 使用 const 有什么作用?

1
2
3
4
5
6
7
8
// 例子来源于 MDN
let iterable = [10, 20, 30];

for (let value of iterable) {
// 修改了值
value += 1;
console.log(value);
}

也就是说如果使用了 const 在此块级作用域下是不可以修改的。

不过在 Go 中是复用相同地址的变量,不能设置 const 的常量的形式,所以就是相同的地址了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
)

func main() {
s := []int{1, 2, 3, 4}

for _, v := range s {
fmt.Println(&v)
}
// 0xc4200160a0
// 0xc4200160a0
// 0xc4200160a0
// 0xc4200160a0
}

Markdown 实现代码 diff 效果

如下图所示,github 提供了代码提交记录的 diff 效果,其实在 markdown里面也可以轻松实现。

image

1
2
-if(v > 100)
+if(v > 120)

代码块的语法类型为patch,在删除行加上 -,在新增行加上 +

更新:

看到 web3.js 上用的语法类型是 diff ,和上面的 patch 差不多。

1
2
3
-var web3 = require('web3');
+var Web3 = require('web3');
+var web3 = new Web3();

[Linux]在终端下输入密码时显示星号提示符

在使用 sudo 命令的时候会要求我们输入密码,但是这个密码并没有任何反馈,比如下面这个例子。

~$ sudo apt update
[sudo] password for root:

下面简单修改 sudo 的配置文件就可以类似在浏览器表单中输入密码一样显示 **** 的字样。

1
2
3
4
5
6
7
8
# backup 
sudo cp /etc/sudoers /etc/sudoers.bak
# edit
sudo visudo
# find ` Default env_reset`
Default env_reset,pwfeedback
# save
:wq

大功告成!

Chrome 已经原生支持截图功能,还可以给节点截图

昨天Chrome62稳定版释出,除了常规修复各种安全问题外,还增加很多功能上的支持,比如说今天要介绍的强大的截图功能。

直接截图

打开开发者工具页面,选择左上角的元素选择按钮(Inspect)

image

Windows 下按住 Ctrl,Mac 就按住 Command,然后在页面拖动选择区域即可。

image

Chrome会自动使用下载方式进行存储,如下效果图,感觉还不错。

image

给节点截图

比如说我们刚才手动截取的区域其实是一个Node节点,如果想完整截取这一部分,我们就需要使用节点截图功能。

image

首先在开发者工具里面选择节点,这个如上图所示直接点选HTML即可。

然后按下快捷键 Ctrl + Shift + p 打开命令工具,Mac下就是 Cmd + Shift + p,输入 node 选择 Capture node screenshot 即可。图片会自动下载。

image

那么我们真的不需要网页截图插件了,如果想截图整个网页,我们直接在根节点选取就可以了。

是不是很方便?

原文首发在我的 GitHub 博客

express 路由模块化

使用 express.Router 类来创建可安装的模块化路由处理程序。Router 实例是完整的中间件和路由系统;因此,常常将其称为“微型应用程序”。

以下示例将路由器创建为模块,在其中装入中间件,定义一些路由,然后安装在主应用程序的路径中。

在应用程序目录中创建名为 birds.js 的路由器文件,其中包含以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var express = require('express');
var router = express.Router();

// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// define the home page route
router.get('/', function(req, res) {
res.send('Birds home page');
});
// define the about route
router.get('/about', function(req, res) {
res.send('About birds');
});

module.exports = router;

接着,在应用程序中装入路由器模块:

var birds = require('./birds');
...
app.use('/birds', birds);

此应用程序现在可处理针对 /birds 和 /birds/about 的请求,调用特定于此路由的 timeLog 中间件函数。

前端表单响应回车键的注意点

1
2
3
4
5
6
7
<form @submit.prevent>
<div>
<label for="nickname">昵称</label>
<input type="text" id="nickname" placeholder="输入账户昵称" v-model="nickname" required>
</div>
<button type="submit" :disabled="regLoading" @click="submit">{{regText}}</button>
</form>
  1. submit 事件的发生对象是form元素,而不是button元素,submit 事件会触发 submit button 的单击事件,而 submit button 单击事件会触发 form action,单击 submit 按钮也会直接触发 submit 事件。
  2. 当输入回车的时候,表单响应的可能是第一个 button 元素,即使这个按钮没有设置 submit 类型,如果表单有多个按钮,一定要注意,不是提交按钮应当设置 <button type="button">notSubmit</button>,而在提交按钮上设置 <button type="submit">Submit</button>
  3. 另外在实践中,需要响应回车键的同时,还需要组织浏览器的默认跳转(回车会自动跳转到 form action 属性的网址,没有就会自动刷新),我们只需要在 form submit 事件使用 preventDefault(),在 vue 中直接可以使用 <form @submit.prevent></form> 即可。

一些Mac的使用技巧

常用的复制文本是Command+C,相应的粘贴剪贴都是用的Command结合xv

开发工具

Mac默认自带许多Runtime,比如说Ruby和Python,不过Git貌似就没有自带,这时候我们只要在Terminal里面运行xcode-select --install,这个命令就会下载很多必要的开发组件,比如说git。**当升级系统之后,最好重新运行以下这个重装。**因为可能升级系统了,这些包可能会丢失。

Homebrew

安装

打开命令行运行

1
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

国内镜像

1
2
3
cd /usr/local/Homebrew
git remote set-url origin https://coding.net/homebrew/homebrew
brew update

OK!

Terminal设置代理

命令行环境不支持软件定义的代理,我们可以自己设置代理。一千我用windows的时候在git bash中也是可以这样设置的。

1
2
3
4
5
6
export http_proxy=""
export https_proxy=""

# delete proxy
unset http_proxy
unset https_proxy

Terminal退出关闭窗口

在Terminal中默认是输入exit是退出但不关闭窗口,我们可以在菜单中选择偏好设置–shell进行设置。
image

浏览器手势

  1. 双指左右滑 => 前进后退
  2. 缩小 / 减小文字大小 => 两指捏合

Delete快捷键

mbp只有退格键没有删除键,不过只要按fn+backspace就可以了。

拖拽或文本选择

网上说以前可以使用三指拖动实现这种功能,不过需要在设置–辅助功能–鼠标与触摸板–触摸板选项–启用拖移,这里我设置了我在Windows的习惯,双击然后拖动。不用三指拖动也是为了和一些三指触控板手势区分开。

image

触控板手势

用习惯之后,确实Mac要比Windows强大的多,因为屏幕小,所以都可以把应用全屏,然后用手势切换全屏应用。

image

Node.js Console 颜色清单

在 Node.js 中打印颜色很简单,就是加上一段字符串描述就可以了,例如:

1
2
console.log('\x1b[36m%s\x1b[0m', info);  //cyan
console.log('\x1b[33m%s\x1b[0m: ', path); //yellow

更多:

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
# 控制符
Reset = "\x1b[0m"
Bright = "\x1b[1m"
Dim = "\x1b[2m"
Underscore = "\x1b[4m"
Blink = "\x1b[5m"
Reverse = "\x1b[7m"
Hidden = "\x1b[8m"

# 前景色
FgBlack = "\x1b[30m"
FgRed = "\x1b[31m"
FgGreen = "\x1b[32m"
FgYellow = "\x1b[33m"
FgBlue = "\x1b[34m"
FgMagenta = "\x1b[35m"
FgCyan = "\x1b[36m"
FgWhite = "\x1b[37m"

# 背景色
BgBlack = "\x1b[40m"
BgRed = "\x1b[41m"
BgGreen = "\x1b[42m"
BgYellow = "\x1b[43m"
BgBlue = "\x1b[44m"
BgMagenta = "\x1b[45m"
BgCyan = "\x1b[46m"
BgWhite = "\x1b[47m"

需要注意的是,一但加上某一个颜色,之后所有的字符都会受到影响。

所以需要加上 reset 来清除颜色。

更强大的库可以使用 https://github.com/chalk/chalk

URL传递中文编码的解决方案

网页URL的合法字符分成两类。

URL元字符:分号(;),逗号(’,’),斜杠(/),问号(?),冒号(:),at(@),&,等号(=),加号(+),美元符号($),井号(#)

语义字符:a-z,A-Z,0-9,连词号(-),下划线(_),点(.),感叹号(!),波浪线(~),星号(*),单引号(\),圆括号(()`)

除了以上字符,其他字符出现在URL之中都必须转义,规则是根据操作系统的默认编码,将每个字节转为百分号(%)加上两个大写的十六进制字母。比如,UTF-8的操作系统上,http://www.example.com/q=春节这个URL之中,汉字“春节”不是URL的合法字符,所以被浏览器自动转成http://www.example.com/q=%E6%98%A5%E8%8A%82

其中,“春”转成了%E6%98%A5,“节”转成了%E8%8A%82。这是因为“春”和”节“的UTF-8编码分别是E6 98 A5E8 8A 82,将每个字节前面加上百分号,就构成了URL编码。

encodeURI 方法的参数是一个字符串,代表整个URL。它会将元字符和语义字符之外的字符,都进行转义。encodeURIComponent只转除了语义字符之外的字符,元字符也会被转义。因此,它的参数通常是URL的路径或参数值,而不是整个URL

decodeURI用于还原转义后的URL。它是encodeURI方法的逆运算。decodeURIComponent用于还原转义后的URL片段。它是encodeURIComponent方法的逆运算。

在浏览器地址栏里,浏览器认为%是个转义字符,浏览器会把%与%之间的编码,两位两位取出后进行解码,然后再传递给后端,然后由后端进行再次解码。

image

如果使用encodeURI()进行了一遍编码,传过去后,发现解码出现问题,需要使用两次encodeURI方法,例如encodeURI(encodeURI("中文"));第一次是把中文编码成%xy的格式,第二次是对%xy中的%进行编码%编码成%25

字符 说明 转化
+ URL 中+号表示空格 %2B
空格 URL中的空格可以用+号或者编码 %20
/ 分隔目录和子目录 %2F
? 分隔实际的URL和参数 %3F
% 指定特殊字符 %25
v# 表示书签 %23
& URL 中指定的参数间的分隔符 %26
= URL 中指定参数的值 %3D

JS事件中target与currentTarget区别

(1)currentTarget

currentTarget属性返回事件当前所在的节点,即正在执行的监听函数所绑定的那个节点。作为比较,target属性返回事件发生的节点。如果监听函数在捕获阶段和冒泡阶段触发,那么这两个属性返回的值是不一样的。

1
2
3
4
5
6
function hide(e){
console.log(this === e.currentTarget); // true
e.currentTarget.style.visibility = "hidden";
}

para.addEventListener('click', hide, false);

上面代码中,点击para节点,该节点会不可见。另外,在监听函数中,currentTarget属性实际上等同于this对象。

(2)target

target属性返回触发事件的那个节点,即事件最初发生的节点。如果监听函数不在该节点触发,那么它与currentTarget属性返回的值是不一样的。

1
2
3
4
5
6
7
8
function hide(e){
console.log(this === e.target); // 有可能不是true
e.target.style.visibility = "hidden";
}

// HTML代码为
// <p id="para">Hello <em>World</em></p>
para.addEventListener('click', hide, false);

上面代码中,如果在para节点的em子节点上面点击,则e.target指向em子节点,导致em子节点(即World部分)会不可见,且输出false。

在IE6—IE8之中,该属性的名字不是target,而是srcElement,因此经常可以看到下面这样的代码。

1
2
3
4
function hide(e) {
var target = e.target || e.srcElement;
target.style.visibility = 'hidden';
}

ref: http://javascript.ruanyifeng.com/dom/event.html#toc15