Wiki

Table of Contents

WIKI

设计模式准则

[2020-06-24 Wed 10:57]

我以前给一些公司讲一些设计模式的培训课,我一再提到,那23个经典的设计模式和 OO 半毛钱关系没有,只不过人家用 OO 来实现罢了。

设计模式就三个准则:

  • 中意于组合而不是继承,
  • 依赖于接口而不是实现,
  • 高内聚,低耦合。

你看,这完全就是 Unix 的设计准则。

文件的描述符和重定向

[2020-05-12 Tue 18:36]

文件描述符 是和文件的输入、输出相关联的非负整数,Linux 内核(kernel)利用文件描述符来访问文件。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

系统预留文件描述符:

  • 0 - stdin 标准输入;
  • 1 - stdout 标准输出;
  • 2 - stderr 标准错误。

实例:

 1: # 输出重定向
 2: # 1. 截取模式保存到文件 - 写入到文件之前,文件内容首先会被清空
 3: echo "this is a text line one" > test.txt
 4: # 2. 追加模式保存到文件 - 写入到文件之后,会追加到文件结尾
 5: echo "this is a text line one" >> test.txt
 6: 
 7: # 标准错误输出的重定向方法
 8: cat linuxde.net                 # → cat: linuxde.net: No such file or directory
 9: # 1. 没有任何错误提示,正常运行
10: cat linuxde.net 2> out.txt
11: # 2. 错误信息被保存到了 out.txt 文件中
12: cat linuxde.net &> out.txt
13: # 3. 将错误输出丢弃到 /dev/null 中,特殊的设备文件 - 黑洞
14: cat linuxde.net 2> /dev/null
15: 
16: # 输入重定向
17: echo < test.txt

数据归档和解压缩

[2020-05-12 Tue 18:35]

首先要弄清两个概念:打包和压缩。 打包 是指将一大堆文件或目录变成一个总的文件; 压缩 则是将一个大的文件通过一些压缩算法变成一个小文件。

1. tar 命令

利用tar命令,可以把一大堆的文件和目录全部打包成一个文件,这对于备份文件或将几个文件组合成为一个文件以便于网络传输是非常有用的。

# 语法
tar (选项) (参数)

实例:

 1: # 打包、压缩
 2: tar -cvf log.tar log2012.log      # 仅打包,不压缩!
 3: tar -zcvf log.tar.gz log2012.log  # 打包后,以 gzip 压缩
 4: tar -jcvf log.tar.bz2 log2012.log # 打包后,以 bzip2 压缩
 5: 
 6: # 查询
 7: tar -tvf log.tar                # 直接查询
 8: tar -ztvf log.tar.gz            # 查询以 gzip 压缩的文件
 9: tar -jtvf log.tar.bz2           # 查询以 bzip2 压缩的文件
10: 
11: # 解压缩
12: tar -zxvf log.tar.gz            # 以 gzip 解压缩
13: tar -jxvf log.tar.bz2           # 以 bzip2 解压缩
14: 
15: tar -zxvf log.tar.gz -C log     # 以 gzip 解压缩在目录 log

其中:

选项 说明
-v 显示操作过程
-f <FILE> --file=<FIEL> 指定备份文件
-c --create 建立新的备份文件
-t --list 列出备份文件的内容
-x --extract --get 从备份文件中还原文件
-z --gzip --ungzip 通过gzip指令处理备份文件
-j 支持bzip2解压文件
-C <DIR> 在特定目录解压缩

小结:

压 缩:tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称
查 询:tar -jtv -f filename.tar.bz2
解压缩:tar -jxv -f filename.tar.bz2 -C 欲解压缩的目录

关于 CSS 中设置 height 为 100% 不起作用

[2020-04-25 Sat 15:57]

W3C 的规范,百分比的高度在设定时需要根据这个元素的父元素的高度。

Web 浏览器有计算有效宽度时会考虑浏览器窗口的打开宽度,缼省为页面整个横向宽度。

事实上,浏览器根本就不计算内容的高度,除非内容超出了视窗范围(导致滚动条出现),缺省为 height: auto 。或者你给整个页面设置一个绝对高度,否则浏览器就会简单的让内容往下堆砌,页面的高度根本就无需考虑。

!!!个人实践,在给 html、body 设置 background 相关属性的时候情况会很奇特,给其内的元素设置背景的时候就不存在这些奇葩问题。

所以,当我们想要设置竖直高度的百分比,需要对 html、body 进行一些初始化设置,如:

 1: html {
 2:     /* 设置根元素高度 */
 3:     height: 100%;
 4: }
 5: 
 6: body {
 7:     /* 设置 body 高度 */
 8:     height: 100%;
 9: }
10: 
11: /* body 内元素 .container */
12: .container {
13:     height: 60%;
14:     background: #f66;
15: }

其实试一下,你就会发现,如果直接对 body 设置 background 的背景图片或是背景色,都会占满整个容口。

JavaScript 中的 bind、call、apply 方法1

[2020-04-20 Mon 14:25]

如果你在浏览器的控制台执行 console.dir(Function) ,就会发现 apply、bind、call 方法都是 Function 的原型方法,也就是说,JavaScript 中的每一个 Function 对外都包含上述三种方法。

call、apply、bind 方法都用来重定义 this 这个对象的。来看一个简单的例子:

 1: var name = 'Amy', age = 16;
 2: var obj = {
 3:     name: 'Lucy',
 4:     objAge: this.age,
 5:     myFun: function() {
 6:         console.log(this.name + '年龄' + this.age);
 7:     }
 8: }
 9: var db = {
10:     name: 'Jack',
11:     age: 26
12: }
13: 
14: obj.myFun.call(db);             // → Jack年龄26
15: obj.myFun.apply(db);            // → Jack年龄26
16: obj.myFun.bind(db)();           // → Jack年龄26

注意: bind 方法返回的是一个函数,必须调用才会被执行。

call、bind、apply 这三个函数的第一个参数都是 this 的指向对象,区别在于第二个传参数:

  • call 的参数是直接放进去,用逗号分隔;
  • apply 的所有参数都必须放在一个数组里传进去;
  • bind 除了返回函数以外,参数和 call 一样。

JavaScript 对 url 的编码和解码

[2020-04-18 Sat 18:26]

有时候,你会发现一些 url 链接是编码过的,如这样: http%3A%2F%2Fw3cschool.cn%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab

JavaScript 中使用 encodeURIComponent() 方法可以对 URI 进行编码;使用 decodeURIComponent() 方法可以对 URI 进行解码。

W3C 提供了简单的实现,如下:

1: var uri="http://w3cschool.cn/my test.php?name=ståle&car=saab";
2: var uri_encode=encodeURIComponent(uri);
3: document.write(uri_encode);
4: document.write("<br>");
5: document.write(decodeURIComponent(uri_encode));

↓↓↓

http%3A%2F%2Fw3cschool.cc%2Fmy%20test.php%3Fname%3Dst%C3%A5le%26car%3Dsaab
http://w3cschools.com/my test.asp?name=ståle&car=saab

JavaScript indexOf

[2020-03-19 Thu 09:18]

indexOf() 方法可返回指定的字符串值在字符串中 首次 出现的位置:

  • 如果没有找到匹配的字符串则返回 -1 ;
  • indexOf() 方法区分大小写。
1: let str = 'Hello world, welcome to the universe.';
2: let n = str.indexOf('welcome');  // → 13
3: let m = str.indexOf('e', 5);     // → 14
4: let v = str.indexOf('none');     // → -1

具体语法如下:

string.indexOf(searchvalue, start)
参数 描述
searchvalue 必需,规定需检索的字符串值
start 可选的整数参数,规定在字符串中开始检索的位置。它的合法值是 0 到 string Object.length - 1 。如果省略该参数,则将从字符串的首字符开始检索。

与之相似的还有 lastIndexOf() 方法,可返回一个指定的字符串值在字符串中 最后一次 出现的位置。

#. Array includes()

延伸一下,我们来看一下 JavaScript Array includes() 方法。

includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true ,否则 false

1: [1, 2, 3].includes(2);     // true
2: [1, 2, 3].includes(4);     // false
3: [1, 2, 3].includes(3, 3);  // false
4: [1, 2, 3].includes(3, -1); // true
5: [1, 2, NaN].includes(NaN); // true

具体语法如下:

arr.includes(searchElement)
arr.includes(searchElement, fromIndex)
参数 描述
searchElement 必须,需要查找的元素
fromIndex 可选,默认为 0 。从该索引出开始查找 searchElement 。如果为负值,则按升序从 array.length + fromIndex 的索引处开始搜索

Emacs 宏操作

[2020-02-28 Fri 12:02] https://www.jianshu.com/p/6ad946eb8ebc

Key/Command Description
C-x ( 开启宏记录
C-x ) 关闭宏记录
C-x e 执行刚录制的宏
C-u n C-x e 执行 n 次刚录制的宏
M-x name-last-kbd-marco 给刚记录的宏命名
M-x insert-kbd-marco 把刚命名的宏记录写入到文件中

可以设置一个专门的文件(如 ~/.emacs.d/macro.el )来记录宏,然后在 init.el 中加载改文件( (load-file "~/.emacs.d/macro.el") ), 如此便可以实现持久化。

如这个例子:用宏定义了下翻 15 行和上翻 15 行的快捷键。

1: ;; macro.el
2: (fset 'next-lines
3:     "\C-u15\C-n")
4: (fset 'previous-lines
5:     "\C-u15\C-p")
1: ;; init.el
2: 
3: ;; ...
4: ;; 加载 macro.el
5: (load-file "~/.emacs.d/macro.el")
6: ;; 绑定快捷键
7: (global-set-key (kbd "C-x n RET") 'next-lines)
8: (global-set-key (kbd "C-x p RET") 'previous-lines)
9: 

如何设置终端 256 色

[2020-02-28 Fri 11:37] https://stackoverflow.com/questions/63950/how-to-make-emacs-terminal-colors-the-same-as-emacs-gui-colors?r=SearchResults

设置 TERM.bashrc 文件中,如下:

1: export TERM=xterm-256color

如此,便设置好了。

加入我们使用在终端中使用 Emacs ,执行 M-x list-colors-display ,便可以看到 256 色已经全部激活,如此,终端下使用 Emacs 和 Emacs GUI 的颜色便相差无几了。

input 中 placeholder、disabled 状态样式修改

[2020-01-28 Tue 14:00]

问题场景:

  • 有时按业务需求更改 inputplaceholder 样式和 disabled 状态下的样式;
  • IOS 和安卓移动端样式兼容性问题,样式不一致。

处理如下:

 1: input::-webkit-input-placeholder {
 2:     color: #ccc;
 3:     -webkit-text-fill-color: #ccc;
 4:     opacity: 1;
 5:     -webkit-opacity: 1;
 6: }
 7: 
 8: input:disabled {
 9:     background: none;
10:     color: #333;
11:     -webkit-text-fill-color: #333;
12:     opacity: 1;
13:     -webkit-opacity: 1;
14: }
15: 
16: input:disabled::-webkit-input-placeholder {
17:     color: #ccc;
18:     -webkit-text-fill-color: #ccc;
19:     opacity: 1;
20:     -webkit-opacity: 1;
21: }

相关延伸:

  • ::-webkit-input-placeholder {} 使用 webkit 内核的浏览器
  • :moz-placeholder {} Firefox 版本 4-18
  • ::moz-placeholder {} Firefox 版本 19+
  • -ms-input-placeholder {} IE 浏览器

CSS 换行

[2020-01-28 Tue 13:59]

→ 参考链接

文本换行有很多方式:

  • <br/> 标签元素,能够强制使得所在位置文本换行;
  • <p> 元素, <div> 设定宽度,都可以对文本内容实现自适应换行;
  • 对于长单词或链接,默认不会断开换行,方式 2 就不能够在这些文本内部进行换行,此时需要 word-wrap: break-word;word-break: break-all; 实现强制断行。

1. 强制不换行

1: div {
2:     white-space: nowrap;
3: }
4: /*
5: white-space:
6: - normal  默认
7: - pre     换行和其他空白字符都将受到保护
8: - nowrap  强制在同一行内显示所有文本,直到文本结束或者遭遇 <br> 对象
9: */

2. 控制文本换行

 1: div {
 2:     word-break: normal;
 3:     word-break: break-all;
 4:     word-break: keep-all;
 5: }
 6: /*
 7: word-break:
 8: - normal        依据亚洲语言与非亚洲语言的文本规则,允许在字内换行
 9: - break-all     该行为与亚洲语言的 normal 相同,也允许非亚洲语言文本行的任意字内断开,该值适合包含一些非亚洲文本的亚洲文本
10: - keep-all      与所有非亚洲语言的 normal 相同,对于中文、韩文、日文,不允许字断开,适合包含少量亚洲文本的非亚洲文本
11: */

3. 强制单词内或链接内断行

1: div {
2:     word-wrap: break-word;
3: }
4: /*
5: word-wrap:      属性用来表明是否允许浏览器在长单词和链接内进行断句
6: - normal        只在允许的断字点换行
7: - break-word    在长单词或 URL 地址内部进行换行
8: */

JS 获取 DPI

[2020-01-28 Tue 13:59]

 1: //获取DPI
 2: function js_getDPI() {
 3:     var arrDPI = new Array();
 4:     if ( window.screen.deviceXDPI != undefined ) {
 5:         arrDPI[0] = window.screen.deviceXDPI;
 6:         arrDPI[1] = window.screen.deviceYDPI;
 7:     }
 8:     else {
 9:         var tmpNode = document.createElement( "DIV" );
10:         tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden";
11:         document.body.appendChild( tmpNode );
12:         arrDPI[0] = parseInt( tmpNode.offsetWidth );
13:         arrDPI[1] = parseInt( tmpNode.offsetHeight );
14:         tmpNode.parentNode.removeChild( tmpNode );
15:     }
16:     return arrDPI;
17: }
18: 
19: // 将 px 转成 mm
20: let mm = pxValue/dpi*2.54*10;   // dpi 是上面获取的,注意对应 XY 轴

时间日期的格式化

[2020-01-28 Tue 13:58]

 1: // 该插件用来格式化当前输入的时间/日期
 2: 
 3: // xxxx/xx/xx xx:xx:xx
 4: const formatTime = (date) => {
 5:     let year = date.getFullYear(),
 6:         month = date.getMonth() + 1,
 7:         day = date.getDate(),
 8:         hour = date.getHours(),
 9:         minute = date.getMinutes(),
10:         second = date.getSeconds();
11: 
12:     return [year, month, day].map(formatNumber).join('/') +
13:            ' '  +
14:            [hour, minute, second].map(formatNumber).jon(';');
15: }
16: 
17: // xxxx-xx-xx
18: const formatDate = (date) => {
19:     let year = date.getFullYear(),
20:         month = date.getMonth() + 1,
21:         day = date.getData();
22: 
23:     return [year, month, day].map(formatNumber).join('-');
24: }
25: 
26: const formatNumber = (n) => {
27:     n = n.toString();
28: 
29:     return n[1] ? n : '0' + n;  // 如 8 -> 08
30: }
31: 
32: // 导出方法
33: module.exports = {
34:     formatTime: formatTime,
35:     formatDate: formatDate
36: }

小程序跳转 H5 时 url 参数截断

[2020-01-28 Tue 13:57]

→ 参考链接

先来看一个例子,原来的 url 为 https://ultimavip.cn/m/mposter.html?source=gxw_001_t_mposter ,跳转后变为 https://ultimavip.cn/m/mposter.html ,参数 ?source=gxw_001_t_mposter 丢失了,为什么呢?编码问题。

 1: // 跳转到 H5 页面的小程序代码
 2: targetUrl: function() {
 3:     console.log(this.data.mod_textUrl);
 4:     wx.navigateTo({
 5:         url: '../webview/webview?url=' + encodeURIComponent(this.data.mod_textUrl) // 此处需要编码,因为有 '?' ,可能浏览器不认
 6:     })
 7: }
 8: 
 9: // 跳转到的 H5 页面进行解码
10: onLoad: function(options) {
11:     this.setData({
12:         targetUrl: decodeURIComponent(options.url); // 用 decodeURIComponent 进行解码
13:     })
14:     console.log(options.url);
15: }

FormData

→ 参考链接

FormData 类型是什么? FormData 类型是在 XMLHttpRequest Level 2 定义的,它为序列化表单以及创建与表单格式相同的数据(用于 XHR 传输)提供便利。

如何初始化一个 formData 对象实例呢?如下:

  • 创建一个空对象实例;
  • 使用已有表单来初始化一个对象实例。

1. 创建一个空对象实例

1: var formData = new FormData();

后续,可以调用 append() 方法来添加数据。

2. 初始化已有表单创建实例

假设已有表单如下:

1: <form id="myForm" action="" method="post">
2:   <input type="text" name="name" />名字
3:   <input type="password" name="psw" />密码
4:   <input type="submit" value="提交" />
5: </form>

下面是用这个表单元素作为初始化参数,来实例化一个 formData 对象,如下:

 1: // 获取页面已有的 form 表单
 2: let form = document.getElementById('myForm');
 3: // 用表单来初始化
 4: let formData = new FormData(form);
 5: 
 6: // 还可以根据 name 来访问表单中的字段
 7: let name = formData.get('name'); // 获取名字
 8: let psw = formData.get('psw');   // 获取密码
 9: 
10: // 还可以在此基础上,继续添加其他数据
11: formData.append('token', 'otherdata...');

3. 操作方法

formData 里面存储的数据形式是什么?一对 key/value 组成一条数据, key 是唯一的,一个 key 可能对应多个 value 。如果是使用表单初始化,每一个表单字段对应一条数据,它们的 HTML name 属性即为 key 值, value 属性对应 value 值。

key value
k1 [v1, v2, v3]
k2 v4

可以用如下方法操作数据:

  • 获取数据,通过 get(key)/getAll(key) 来获取对应的 value 值;
  • 添加数据,通过 append(key, value) 来添加数据,若 key 不存在会新增,若 key 已存在会添加到数据末尾;
  • 修改数据,通过 set(key, value) 来设置数据,若 key 不存在会新增,若存在会修改对应的 value 值;
  • 判断是否该数据,通过 has(key) 来判断是否对应的 key 值;
  • 删除数据,通过 delete(key) ,来删除数据;
  • 遍历,通过 entries() ,来获取一个迭代器,每条用一次 next() 返回一条数据,如此可以遍历所有的数据。
1: formData.get('name');       // 获取 key 为 name 的第一个值
2: formData.getAll('name');    // 返回一个数据,获取 key 为 name 的所有值

通过 XHR 来发送数据,如下:

1: let xhr = new XMLHttpRequest();
2: xhr.open('post', 'login');
3: xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
4: xhr.send(formData);

滚动懒加载的实现

[2020-01-28 Tue 13:55]

→ 参考链接

什么时候需要懒加载呢?数据量大,一页显示不完,网页渲染事件长,影响体验。如何解决?分页,或数据懒加载。

先设定了基础前提,假设视窗可以显示 30 数据,总共有 56 条数据要展示。

如何实现数据懒加载呢?先来看三个属性:

  • scrollHeight ,元素总高度,包含滚动条中的内容,只读;
  • scrollTop ,当元素出现滚动条时,向下拖动滚动条时,内容向上滚动的距离,可读写;
  • clientHeight ,元素内容及其边框所占的空间大小,即可视区域大小高度。

如何判断滚动条到底部了呢?很显然,当 scrollHeight - scrollTop - clientHeight = 0 时,滚动条就到底部了。

来看代码,在第一次请求数据的时候,先设置一个变量来记录请求次数(其实后台也是做分页的处理):

1: // 初始化首页页码
2: let currentPage = 1;            // this.currentPage = 1
3: 
4: // 获取首页数据,apiGetTableData 为定义的获取数据的接口
5: // data 为请求参数
6: this.apiGetTableData(data).then(res => {
7:     $this.totalPage = res.totalPage; // 这里需要知道总页数
8:     $this.tableData = res.data;      // 表格数据
9: })

监听表格 DOM 对象的滚动事件:

 1: let DOM = document.querySelector(targetDom);
 2: 
 3: DOM.addEventListener('scroll', function() {
 4:     let scrollDistance = DOM.scrollHeight - DOM.scrollTop - DOM.clientHeight;
 5: 
 6:     if(scrollDistance <= 0) {                      // 为 0 证明滚动条已经到底,可以请求接口
 7:         if(this.currentPage < this.totalPage) {   // 当前页数小于总页数继续请求
 8:             this.currentPage++;                   // 当前页数自增
 9: 
10:             // 请求接口代码
11:             // data 为请求参数
12:             this.apiGetTableData(data).then(res => {
13:                 this.tableData = $this.tableData.concat(res.data); // 将请求回来的数据和当前展示的数据合并
14:             })
15:         }
16:     }
17: })

如此,就实现表格滚动下拉时的数据懒加载。

JavaScript 中的 || 和 && 所遵循的短路现象

[2020-01-28 Tue 13:55]

|| 时,找到为 true 的分项就停止处理,并返回该分项的值,否则执行完,并返回最后的分项的值;

&& 时,找到为 false 的分项就停止处理,并返回该分项的值,否则执行完,并返回最后的分项的值。

刷新 DNS

[2020-01-28 Tue 13:54]

windows 下 刷新 DNS 的方法:打开 cmd → 输入 ipconfig /flushdns 。 Github 有时候,连接很慢,甚至有打不开的状况,此时,可以尝试刷新一下 DNS ,会有意象不到的效果哦。

Footnotes:

Date: 2020-01-28 Tue 14:02

Author: Jack Liu

Created: 2020-08-04 Tue 16:38

Validate