Leetao's Blog

Talk is cheap, show me the code

0%

What is Arrow function

关于什么是箭头函数,让我们看一下 MDN 给出的定义

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

不难看出上面的定义说出了箭头函数的一些特点:没有自己的this,arguments,super或 new.target,更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。也就是意味着,如果在没有使用 this,arguments 或者 new 等前提下,你可以使用箭头函数替换原本的函数声明。

Are arrow function and funciton declaration equivalen

结合上述的说明,很容易得知,箭头函数和函数声明并不相同。通过几个简单的例子说明这一点:

this

  1. 箭头函数 默认指向定义它时,它所处的上下文对象,而不是执行期间的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var exampleObj = {
a: 'test',
arrowOutputA: () => {
console.log(this.a);
console.log(this);
},
funcOutputA: function () {
console.log(this.a);
console.log(this);
}
}

exampleObj.arrowOutputA();
exampleObj.funcOutputA();

输出结果如下:

关于 this 如果不是特别了解可以阅读下述链接 this

  1. 箭头函数 this 不可变. call(),apply(),bind() 这些方法也 无法改变 箭头函数 this 的指向

让我们通过一组代码对照一下:

1
2
3
4
5
6
7
8
9
10
11
window.name = 'window';

var exampeObj = {
name: 'example'
}

function showName() {
console.log(this.name)
}

showName.call(exampeObj)

输出结果如下:

从上图我们可以看到,上述代码的 this 指向由从 window 变成了 exampleObj
kan chu

1
2
3
4
5
6
7
8
9
10
11
window.name = 'window';

var exampleObj = {
name: 'example'
}

var showName = () => {
console.log(this.name)
}

showName.call(exampleObj);

输出的结果如下:

从上述结果我们不难看出,箭头函数的 this 指向没有发生任何改变

arguments

上述内容提到箭头函数无法绑定 arguments 参数对象,因此不能通过arguments对象访问传入参数,但是可以用 rest 参数实现。关于 rest 参数具体内容可以访问 Rest parameters去进一步了解。

1
2
3
4
var example = (...params) => {
console.log(params);
}
example(1,2,3);

结果如下:

new

箭头函数内部并没有 Construct 方法,所以没有原型属性(prototype),想开头说的那样没法当构造函数,所以很显然也不能用 new 关键字来实例化对象。

1
2
var exampleNew = (x) => {this.name = x};
var example = new exampleNew();

结果如下图:

Compatibility

最后看一下箭头函数的兼容性:

References

What’s the meaning of “=>” (an arrow formed from equals & greater than) in JavaScript?

Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?

What is X-Frame-Options

X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在 frame, iframe 或者 object 中展现的标记。网站可以使用此功能,来确保自己网站的内容没有被嵌到别人的网站中去,也从而避免了点击劫持 (clickjacking) 的攻击。

How to use X-Frame-Options

X-Frame-Options 有三个值: DENY, SAMEORIGIN, ALLOW-FROM uri

DENY

表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。

SAMEORIGIN

表示该页面可以在相同域名页面的 frame 中展示。

ALLOW-FROM uri

表示该页面可以在指定来源的 frame 中展示。

总结

如果设置为 DENY,不光在别人的网站 frame 嵌入时会无法加载,在同域名页面中同样会无法加载。另一方面,如果设置为 SAMEORIGIN,那么页面就可以在同域名页面的 frame 中嵌套。

配置

Apache 配置

配置 Apache 在所有页面上发送 X-Frame-Options 响应头,需要把下面这行添加到 ‘site’ 的配置中:

1
Header always append X-Frame-Options SAMEORIGIN

Nginx 配置

配置 nginx 发送 X-Frame-Options 响应头,把下面这行添加到 ‘http’, ‘server’ 或者 ‘location’ 的配置中:

1
add_header X-Frame-Options SAMEORIGIN;

IIS 配置

配置 IIS 发送 X-Frame-Options 响应头,添加下面的配置到 Web.config 文件中:

1
2
3
4
5
6
7
8
9
10
11
<system.webServer>
...

<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>

...
</system.webServer>

Java 代码

1
response.addHeader("x-frame-options","SAMEORIGIN");

Browser compatibility

References

X-Frame-Options 响应头

X-Frame-Options

X-Frame-Options Compatibility Test

我的最终启动失败的原因是:No Module named ‘pywintypes’ 缺失引起的 Anaconda Navigator 启动失败,但是解决问题的方法是通用的.

定位问题

找到应用,点击打开文件位置

右键快捷方式,将箭头所在栏的内容复制下来

你会得到类似如下的命令:

pathto\Anaconda3\pythonw.exe pathoto\Anaconda3\cwp.py pathto\Anaconda3 pathto\Anaconda3\pythonw.exe pathto\Anaconda3\Scripts\anaconda-navigator-script.py

打开命令行

打开命令行,将上述命令中的 pythonw.exe 换成 python.exe, 然后执行,出现 No Module named ‘pywintypes’ 的错误

解决问题

在当前命令行使用 conda 激活 base 环境,然后执行下述命令

1
pip install pypiwin32

安装完成之后,再次打开Anaconda Navigator成功.

记得当初写作文的时候,老师强调三w的原则,接下来我们讨论 call 也按照这个原则。

What is call

没有什么比官方手册更权威的东西了,看一下官方是如何解释的

Called when the instance is “called” as a function; if this method is defined, x(arg1, arg2, …) is a shorthand for x.call(arg1, arg2, …). —— [ 1 ]

用调用函数一样调用实例; 如果定义了此方法,则x(arg1,arg2,…)是x .__调用__(arg1,arg2,…)的简写形式。

how to use call

我们简单举三个例子

1
2
3
4
5
6
7
8
class SumTwo:
def __init__(self):
pass
def __call__(self,one,two):
return (one+two)

sum_two = SumTwo()
print(sum_two(1,2))

看一下结果:

然后试着实现一下斐波那契数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Fib:

def __init__(self):
self.fib = [0,1]

def __call__(self,n):
if n <= 1:
return self.fib[n]
else:
for i in range(2,n):
tmp_value = self.fib[i-1] + self.fib[i-2]
self.fib.append(tmp_value)
return self.fib


fib = Fib()

for i in fib(10):
print(i)

输出如下图:

最后以我们十分熟悉的阶乘再收尾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Factorial:
def __init__(self):
self.cache = {}
def __call__(self, n):
if n not in self.cache:
if n == 0:
self.cache[n] = 1
else:
self.cache[n] = n * self.__call__(n-1)
return self.cache[n]

factorial = Factorial()

for i in range(10):
print("{}! = {}".format(i, factorial(i)))

输出如下图:

when to use call

解释了是什么和如何用,最后就到重点了,什么时候去用? 其实关于 call 的使用,很多框架都有所涉及,最常见的就是 Flask 和 Django中关于表单验证中的使用,比如说 DataRequired, Length, InputRequired,EqualTo 等等,从这些我们不难看出:

  1. 当你的对象装饰、模拟或抽象函数的概念,这个时候你就可以考虑使用 call 这个魔术方法了。
  2. 当然我觉得当你的类有一个比较基础的操作,但是想不到好的命名的时候,使用 call 也不失为一个好办法,比如:用 run() , doSth() 这些命名的时候

stackoverflow 上还提到了另外的一些使用情况,有兴趣可以点击链接 When is using call a good idea? 进一步去了解。除此之外 SegmentFault 上也有相关问题可以一看 Python2.7总的__call__方法在实际环境中有什么实用的地方?

References

[ 1 ] object.call(self[, args…])

What is Flask?

用了这么久的 Flask 框架,那么 Flask 究竟是什么呢?借用官方文档的一句话来说明这个问题:

Flask is a lightweight WSGI web application framework.

不难理解上面的一句话: Flask 是一个基于 WSGI 的轻量级的应用框架.那么 WSGI 又是什么?

What is WSGI ?

The Web Server Gateway Interface (WSGI) is a simple calling convention for web servers to forward requests to web applications or frameworks written in the Python programming language. ——[ 1 ]
WSGI is the Web Server Gateway Interface. It is a specification that describes how a web server communicates with web applications, and how web applications can be chained together to process one request.
WSGI is a Python standard described in detail in PEP 3333. ——[ 2 ]

上面三段话翻译过来的大致意思是:

Web 服务器网关接口(WSGI)是一种简单的调用约定,用于 Web 服务器将请求转发到用 Python 编程语言编写的 Web 应用程序或框架。 WSGI 是 Web 服务器网关接口。它描述了 web 服务器如何与 web 应用程序通信,以及如何将 web 应用程序链接在一起处理一个请求。 WSGI 是在 [**PEP 3333**](https://www.python.org/dev/peps/pep-3333) [3]中详细描述的 Python 标准。

Why is WSGI necessary?

最初的传统的 Web 服务器是没有办法运行 Python 的应用程序的。直到 1990年末,一个 Grisha Trubetskoy 的开发者提出了一个 mod_python 的 Apache module。然后再随后的几年和2000年初,apache 装载了 mod_python 模块,然后绝大多数的 python web 应用程序都得以运行。然而 mod_python 并不是一个标准的规格,随着 mod_module 开发的停滞不前和安全隐患被发现, python 社区意识到必须要有一个始终如一运行 python 代码的方法,因此 wsgi 应运而生。直到现在,WSGI仍然是运行 python web 应用程序的公认方法。

如上图所示,WSGI 服务器只是调用在 PEP 3333 标准中定义的 WSGI 应用程序上的可调用对象。

WSGI’s Purpose

所以为什么不直接将 WSGI 服务器直接指向 WSGI 的应用框架呢?
1. WSGI 能够提供可好的可拓展性. 基于 WSGI,开发人员可以随时将 Web 组件替换为其他的。比如说由 Green Unicorn 无缝切换到 uWSGI。
2. WSGI服务器提升了负载规模 一次为千个请求提供动态内容数是WSGI服务器,而不是框架。 WSGI服务器处理来自Web服务器的处理请求,并决定如何将这些请求传递给应用程序框架的进程。

What is PEP 3333 ?

PEP 3333 一直出现在上文中,那么 PEP 3333 的究竟说了什么呢,有兴趣的读者可以直接点击PEP 3333查看原文。嫌麻烦的话,可以直接看下图,一图胜千言,我花了一点时间将 PEP 3333 中的主要内容整理成了一个简单的脑图

References

[ 1 ] Web Server Gateway Interface

[ 2 ] What is WSGI?

[ 3 ] PEP 3333

[ 4 ] WSGI Servers

问题原因

最近有个需求用Java调用nginx -V并返回输出,查了一下决定使用Runtime.getRuntime().exec()去执行命令.代码如下:

1
process = Runtime.getRuntime().exec(cmd, null, dir);

最初调用函数将正确路径和cmd都传入进去,其中cmd为nginx -V,但是返回下列错误:

Cannot run program “nginx -V”: CreateProcess error=2, 系统找不到指定的文件

解决办法

查了一下相关资料,只需要将原有的cmd命令由nginx -V改为cmd /c nginx -V即可

参考链接

CreateProcess error=2, The system cannot find the file specified

Runtime.getRuntime().exec -> Cannot run program CreateProcess error=2, The system cannot find the file specified

问题

看下面一段代码,如果你能够很清楚的知道结果是什么,就不需要阅读本篇文章了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$array = [
'name' => 'php',
'age' => 123,
];

foreach ($array as $key => &$value) {
echo "key=$key, value=$value" . "</br>";
}

echo "<br>"

foreach ($array as $key => $value) {
echo "key=$key, value=$value" . "</br>";
}
?>

正确输出如下:

1
2
3
4
5
key=name, value=php
key=age, value=123

key=name, value=php
key=age, value=php

解答

在解释产生上述结果原因之前我们先看下面一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$array = [
'name' => 'php',
'age' => 123,
];

foreach ($array as $key => $value) {
echo "key=$key, value=$value" . "</br>";
}

echo "<br>"

foreach ($array as $key => $value) {
echo "key=$key, value=$value" . "</br>";
}

?>

输出结果就不用说了,下面通过一个图简单了解一下执行的过程

那么最开始的那段代码执行情况是什么样子的呢?

参考链接

php issue with looping over an array twice using foreach and passing value by reference

php issue with looping over an array twice using foreach and passing value by reference

原因

docker默认镜像拉取地址为国外仓库下载速度较慢,则会报错”net/http: TLS handshake timeout”

解决办法

将镜像源切换位国内的.右键 Docker 图标 => Settings

然后按照下图的步骤

其中Registry mirrors 填入内容为:

http://69292621.m.daocloud.io

然后 Apply 之后等待重启,然后大功告成.

由于微信小程序请求api接口协议是https的,为了我的小程序,我只好把网站协议由http改成https.在介绍这一过程之前,让我们先看一下什么是https.

HTTPS

先看维基百科的定义

HTTPS (HTTP Secure) is an adaptation of the Hypertext Transfer Protocol (HTTP) for secure communication over a computer network, and is widely used on the Internet.In HTTPS, the communication protocol is encrypted by Transport Layer Security (TLS), or formerly, its predecessor, Secure Sockets Layer (SSL). The protocol is therefore also often referred to as HTTP over TLS,or HTTP over SSL.
The principal motivation for HTTPS is authentication of the accessed website and protection of the privacy and integrity of the exchanged data. It protects against man-in-the-middle attacks. The bidirectional encryption of communications between a client and server protects against eavesdropping and tampering of the communication.In practice, this provides a reasonable assurance that one is communicating without interference by attackers with the website that one intended to communicate with, as opposed to an impostor.
Historically, HTTPS connections were primarily used for payment transactions on the World Wide Web, e-mail and for sensitive transactions in corporate information systems.[citation needed] In the late 2000s and early 2010s, HTTPS was increasingly used for protecting page authenticity on all types of websites, securing accounts and keeping user communications, identity and web browsing private. ————[1]

其翻译过来的大意如下:

Https(HTTPSecure)是超文本传输协议(HTTP)在计算机网络上进行安全通信的一种改编,在互联网上得到了广泛的应用。在HTTPS中,通信协议由传输层安全性(TLS)加密,或者以前的前身安全套接字层(SSL)加密。因此,该协议也经常被称为HTTP over TLS,或HTTP over SSL.

HTTPS的主要动机是对被访问的网站进行身份验证,以及保护所交换数据的隐私和完整性。它能防止中间人的攻击。客户端和服务器之间通信的双向加密保护通信不被窃听和篡改。在实践中,这提供了一个合理的保证,即一个人是在不受攻击者干扰的情况下与他沟通的网站,而不是一个冒名顶替者.

历史上,HTTPS连接主要用于万维网上的支付交易、电子邮件和公司信息系统中的敏感交易.在2000年代末和2010年代初,HTTPS越来越多地被用于保护所有类型网站的页面真实性,保护帐户安全,并保持用户通信、身份和网页浏览的隐私.

准备工作 [2]

这里采用的证书是某云的免费的SSL证书

申请证书

  1. 登录:阿里云控制台,产品与服务,证书服务,购买证书。
  2. 购买:证书类型选择 免费型DV SSL,然后完成购买。
  3. 补全:在 我的证书 控制台,找到购买的证书,在操作栏里选择 补全。填写证书相关信息。
  4. 域名验证:可以选择 DNS,如果域名用了阿里云的 DNS 服务,再勾选一下 证书绑定的域名在 阿里云的云解析。
  5. 上传:系统生成 CSR,点一下 创建。
  6. 提交审核,审核速度很快

申请证书要注意的是验证域名,就是你要验证你想绑定证书的域名是你自己的,如果选择使用 DNS 验证,你需要在域名的管理里,添加一条特定的 DNS 记录,这样就可以证名这个域名是你自己的。使用了阿里云的云解析服务,这个步骤可以自动完成,会自动为你添加一条 DNS 验证的记录。

输入证书要绑定的域名:

填写个人信息:

下载证书

在阿里云的证书管理那里,如果申请的证书审核通过,你就可以下载了,点击 下载,可以选择不同的类型,可以选择 NGINX,或 Apache 之类的服务器。根据自己网站的 Web 服务器类型,下载对应的证书。解压以后,你会得到两个文件一个是 *.key,一个是 *.pem.

然后通过办法将解压后的文件上传到服务器目录下,我这里就上传到了 /etc/nginx/ssl/wechat 目录下

配置 NGINX 的HTTPS

接下来这一段默认你已经完成 Flask 应用的部署,如果没有的话,建议先食用后再阅读下面的步骤.

使用命令行打开配置文件 wechat

1
nano /etc/nginx/sites-available/wechat

原本的内容如下:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name server_domain_or_IP;
location / {
include proxy_params;
proxy_pass http://unix:/home/py/wechat/wechat.sock;
}
}

现修改为如下内容:

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

server {
listen 443;
server_name www.shareu.club;
ssl on;
root /home/py/wechat;
ssl_certificate /etc/nginx/ssl/wechat/214452870720595.pem; # 之前证书解压后的得到的文件
ssl_certificate_key /etc/nginx/ssl/wechat/214452870720595.key; # 之前证书解压后的得到的文件
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;
ssl_prefer_server_ciphers on;

location / {
include proxy_params;
proxy_pass http://unix:/home/py/wechat/wechat.sock;
}
}

server {
listen 80;
server_name www.shareu.club;

return 301 https://$server_name$request_uri;
}

然后按照惯例测试一下文件语法有没有错误

1
nginx -t 

如果看见 succssful 等成功单词就说明配置文件没有任何问题,然后重新加载 nginx 服务

1
service nginx reload

然后这个时候访问网址就发现协议已经由http变成https了

参考连接

[1]HTTPS

[2]用阿里云的免费 SSL 证书让网站从 HTTP 换成 HTTPS

记录Amazon EC2环境配置nginx + jekyll + https过程

永久免费的SSL证书 - Let’s Encrypt 证书部署 HTTPS 并自动续期