升级PHP8,博客出现“有点尴尬诶!该页无法显示”

最近迁移服务器,将 PHP 升级到 8.0.10 之后,且关闭 PHP warning 报错,博客首页、文章页显示“有点尴尬诶!该页无法显示”。

一开始以为数据库导入有问题,文章没有导入。进入后台查看,文章虽然都还在,但查看文章具体页面,均是“有点尴尬诶!该页无法显示”。

恰好点开评论,遇到函数报错,无法打开评论管理界面,猜测是受 PHP 8。

回退到 PHP 7.4.23 之后,文章正常显示。

阻止浏览器根据 EXIF 信息自动修正图像旋转方向 image-orientation

CSS 属性 image-orientation 用来修正某些图片的预设方向。

当需要对图像原始状态进行操作时,这种自动修正会带来错误。尤其是在新版浏览器中该属性默认值从不生效(none)被改成了 from-image,此时导致在 Chrome v81 浏览器上处理一些带角度的图像时产出错误的信息。

/* 正确的默认值应该是0 */
image-orientation: 0deg /* 实际该值无效 */
image-orientation: none /* 实际该值生效,可以阻止浏览器自动旋转图像(disable html img auto rotation) */

/* 非 90 度的整数倍, 所以会被四舍五入到 0 度 */
image-orientation: 6.4deg

/* 相当于 270deg */
image-orientation: -90deg

/* 使用图片的 EXIF 数据 */
image-orientation: from-image

/* 旋转 90deg, 再水平翻转 */
image-orientation: 90deg flip

/* 不旋转, 只进行水平翻转 */
image-orientation: flip

/* 继承 */
image-orientation: inherit

该属性还未进入标准文档,在未来版本的浏览器中该功能的语法和行为可能随之改变。

最近发现一款优惠券神器 —— 蜜源APP,专门搜集淘宝天猫优惠券

蜜源APP这款神器搜集了很多淘宝天猫的优惠券,按照下面的步骤,有很大概率领取到大额优惠券(可能是渠道限制,在淘宝上不一定能找到):

  1. 在淘宝天猫商品详情页长按复制“商品标题”或“ 商品链接 ”;
  2. 打开蜜源APP,自动弹出搜索界面,提示搜索同款商品;
  3. 有优惠券的商品会优先展示;
  4. 软件除了搜集优惠券,在订单完成后还有返现(有点像返利网);
  5. 返现是自动的,我目前最大一笔订单返现50块,相当于打9折。

这款软件注册时需要输入邀请码,我是别人推荐注册的。通过邀请码注册,可以免费升级永久vip,神奇-_-||。不通过邀请码,就不能注册

蜜源邀请码 FBEMYW,注册时填写该邀请码,邀请人和被邀请人都有额外奖励,不过非常少,主要还是通过这款APP领取淘宝和天猫优惠券。

一般只要不是特别小众的商品,基本上都会搜索到淘宝上的同一家店。当然不是每款商品都有优惠券。

(商品无关,纯举例)假设在淘宝上看到某款商品:

  • 1. 在淘宝上浏览商品,有购买意向,才去搜一下;
  • 2. 复制商品标题或链接,打开蜜源,自动弹出搜索界面;
  • 3. 点击搜索刚才的商品,在搜索结果列表中,第一个是刚刚在淘宝店里看到的便携式折叠车,其它是相似商品;预估收益是购买后能够拿到的最高返现,只有真的需要时才去购买,其他时候看看就好
  • 4. 点击第一个结果,查看和领取蜜源搜集的优惠券;点击立即领券,会跳转到淘宝领取优惠券,购买也是在淘宝内完成的。

使用下来,这款APP不提供商品,但搜集了很多商品的优惠券。

  • 5. 如果有通过蜜源APP在淘宝上购买商品,那么能在我的收益里看到记录。
  • 6. 提现是直接转账到支付宝,最新一次提现记录,(*^▽^*)。优惠券在下单时就能使用,返现结算周期有点长,统一在月底结算和提取上个月收益,结算后就能提现。

最后再来一下,蜜源官方APP下载二维码和蜜源客服微信,最新找到的图片。会跳转到应用宝进行下载。

蜜源 APP 下载二维码 ,跳转到应用宝进行下载。

蜜源 APP 注册有效邀请码 FBEMYW

除了领取优惠券,这款 APP 里还有大家发的购买攻略,尤其在电商平台做活动期间能够有效避免被坑。

也可以在蜜源里把商品分享给别人购买,这个还没试过。

为什么有些人开车到家后坐在车里发呆?

作者:刘喜汪

转载自http://mp.weixin.qq.com/s/Jw8GCoFJSol4m-uhOz1cvA

01

知乎上其实有这样一个问题:为什么那么多人开车回家,到了楼下还要在车里坐好久。有个回答点赞特别高。他说:

很多时候我也不想下车,因为那是一个分界点。推开车门你就是柴米油盐、是父亲、是儿子、是老公,唯独不是你自己;在车上,一个人在车上想静静,抽颗烟,这个躯体属于自己。

有个女生告诉我,每次和男朋友吵架了伤心了难过了不怕没去处,油门一蹬四处晃荡,哭一场可以撑半年,然后补个妆回去,厚着脸皮嬉皮笑脸地继续把游戏玩下去。

  • 是啊,活着好累啊,每天扮演各种角色忍着脾气面对各种人,不断给自己灌输“成熟”“高情商”的行为准则。

如果是一个男人,他可能是一个父亲,一个丈夫,一个儿子,一个朋友眼里的成功人士,可是只有在车里的时候是他自己,一个幽暗狭小的空间,一支忽明忽暗的香烟,晚上fm主持人轻飘飘的话语,才会让你意识到“自我”的存在,那种感觉真的很好。

02

成年人的生活里没有容易这两个字,有一件事我印象特别深刻。小时候,我们那个城市进驻了第一家肯德基。我爸带我去尝鲜。他给我买了汉堡,薯条,橙汁。作为一个小朋友,这就是人生中最幸福的时刻了。当然,我也不是白眼狼,还是拿着薯条给爸爸说,您尝一下,可好吃了。

我爸只是慈祥地看着我说,我不饿,你吃就行了。但一到家,他就热了两个馒头,把昨晚的剩饭吃了。

他是饿的。只是他作为父亲的身份,没办法让他去争夺属于我的薯条。

曾经有张动图流传很广,在日本地铁里,有一个男生,坐在那里啃着面包,强忍着委屈,眼泪似乎就要夺眶而出。谁也不知道他发生了什么,但那份心酸,每个人都理解。谁身后都有一堆不可说的故事。但他的那身打扮,给他定位了一个体面的身份。这个身份,让他除了忍住不哭,毫无办法。

《这个杀手不太冷》里有一句特别著名的台词,马蒂尔德问,生活是一直这么艰辛,还是只有童年如此。里昂说,一直如此。

成年的代价就是会失去自我,因为,我们只能把真实的自己,藏在车里,打开车门走出去,就必须得微笑着面对每一个人。

03

我有个朋友Amy,是一个特别容易欢脱的姑娘。如果一个人有关键词,她的关键词就是笑。特别喜欢讲段子,给我们推荐的电影都是喜剧,就算工作和生活中遇到啥问题了,她都特别乐观,导致所有人一有负能量就跟她倾诉。

昨天晚上十一点多,她扛着大包小包来找我时,一脸疲惫。她加班很晚回到家,被房东赶出来了。说租期到了,不租了。

为什么不提前通知?房东任性。Amy一点招都没有。

我没见过她这么不开心的样子,所以都不知道该怎么安慰她。而她却反过来和我开了个玩笑:你说我这波水逆是不是过了点。

她的轻描淡写,让我心里发堵。

这时候,她电话突然响了。她说,我挺好的……已经吃过了……工作蛮开心的……新租的房子离公司很近,十分钟就到了……哎呀妈你就放心吧,我都这么大了,能照顾好自己。

接电话的时候,她身上背着的那个大旅行包还没放下。

看着她坚强的样子,我都想哭了。

我说,累了一天,早点睡。她依旧笑嘻嘻地说,你先睡,我还有个方案明天交。那时候已经十二点多。

我先去睡了。半夜起来想喝点水,发现Amy坐在沙发上,没开灯,只有腿上笔记本的光印在她的脸上,满脸泪水。

我不知道她这样哭了多久。甚至看着她木木的表情,我都觉得,她可能都不知道自己哭了。

一个人最彻底的崩溃,就是这样,悄无声息地,毫无生机地默默流泪。

这让我想起来,以前看过一个热门微博:“现代人的崩溃是一种默不作声的崩溃。看起来很正常,会说笑、会打闹、会社交,表面平静,实际上心里的糟心事已经积累到一定程度了。不会摔门砸东西,不会流眼泪或歇斯底里。但可能某一秒突然就积累到极致了,也不说话,也不真的崩溃,也不太想活,也不敢去死。”

04

曾经有个文章刷爆了朋友圈,叫《I’m CEO,Bitch》。里面用调侃的语气,说出了很多创业者的辛酸。我当时把这篇文章,转给了一个创业狗朋友,还没两分钟他就给我发过来一串哭着的表情,然后发过来一段文章中的话:

我睡得像个婴儿,每两个小时就大哭一次。我经常头一天还觉得拥有整个世界,但是第二天我会觉得世界正在离我而去。

他说,这就是我现在的生活。

这个朋友,从来在我们面前,都是一副意气风发的样子,充满正能量,甚至,当我们有不痛快,熬不下去的时候,都喜欢和他聊天,因为他太积极了。

可这些积极的背后,却是头发都快掉光的焦虑,和每晚默默地哭泣。人生中,最恐怖的孤独,不是没人理解,而是被人误解。

我有个学长,大学毕业就开始创业,三年之后跟我说的最多的一句话就是年轻人别老是想不开去创业,也别老是自己想当老板,以前总觉得有梦想一定能靠自己努力实现,现在发现没钱根本没资格谈梦想,每天一睁眼就是房租、水电、物业费,公司的各种开销比想象中大多了,有一次他跟我说,第二天要发工资,没钱,跟所有亲戚朋友借了个遍,他说当老板,最怕的是对不起员工。

我还有个朋友,终于在给人打了十年工后,觉得自己准备好了,可以创业了。找了几个朋友共同开了个广告公司。

度过了前三个月蜜月期,有个合伙人,非得说大家理念不合,要拆伙。本来都是兄弟朋友的,因为利益原因,撕逼撕到人尽皆知。不仅人走了,还把自己管的客户和团队都带走了。

创个业连当年一起尿过炕的哥们都翻脸了。朋友却根本没空难受,还有下个月的工资要发,还得给供应商结账……他必须得赶紧找新的客户,投资人,给人心惶惶的员工们打鸡血。

他想哭,想吼,甚至想和那个哥们打一架,但是他什么都不能做。只能在每天下班后,去家附近的小花园,坐一会,看看大妈大爷跳广场舞,收拾好自己的心情,回家笑着面对父母妻子,陪孩子玩。

他后来给我说,就是在看大爷大妈跳广场舞的时候,他哭了。因为他看着他们满脸的高兴,特别羡慕。

他在别人眼中,是负责的爸爸,有担当的丈夫,孝顺的儿子,赚大钱的老板。但他自己知道,他就是一个快被榨干的,面临中年危机的男人,连跳广场舞的幸福都没有。

每个人的生活,从来都是不容易的。不容易在,你明明知道,真正的自己,早就被这些社会身份包装到被遗弃,甚至埋葬。但你却没有退路,没有第二个选择,你只能哭着爬着把那些被人寄予厚望的身份扮演下去。

可每个人,无论爬得多辛苦,演得多艰难,内心深处,还都会有一个微弱到快熄灭的声音,不停地拷问自己:生活的意义到底是什么?是身在红尘的体验,还是看破红尘的顿悟。山的那头,到底有什么?我们只有爬过去才知道。

05

我特别喜欢一首歌《What’ up》有句歌词:25年的人生就这样过去了,我仍要努力去翻越那希望的高山,为了让人生有意义。

我不想说,泥沼总会过去,星辰大海在向你招手。甚至,我都不觉得吃苦是有必要的。但是,找到生活的意义的时刻,恰恰是在,当你熬过去,撑下去后,可以用上帝视角去审视当年那个奋斗到呲牙列嘴的自己的时候。

人生就真的像爬山一样。山脚下的我们,就是小时候,天真无邪,井底之蛙。爬到半山腰才发现,体力费光,下山已经没有路,还恐高不敢回头看;往上爬,手脚并用都不见得能再挪一步。但是能爬过去的,就是那些,能挺住的人。

  • 挺住,意味着一切。

MySQL 批量插入、批量更新

MySQL 批量插入/更新含有主键/唯一索引或者的解决方案是使用 on duplicate key update

解决方案

insert into database.table (id, key, field)
    values (v1, v2, v3), (v1, v2, v3), (v1, v2, v3)
    on duplicate key update
    id=values(id),key=values(key),field=values(field);

该方案适用范围广,
既可以批量插入,
也可以批量更新,
同时能保证避免插入重复数据,
还能避免影响自增 id,
又不需要创建临时表权限,
更不会修改已存在记录的其他字段。


实际应用中 update 后跟随的字段可以进行调整,保留只需要更新的字段


如果表中有自增 id,这个方案还有一点需要注意,insert into … values 跟随的记录数 (numberA)即为此次自增数 (numberA),即使存在重复唯一索引导致实际插入记录数(numberB)少于跟随的记录数(numberA)。


其他插入方案

1. insert ignore into

insert ignore 遇到重复数据会忽略,可以有效保证不插入重复数据,但也会忽略其他错误。
批量插入之后再进行单条更新。

# 批量插入,需要保证 NOT NULL 字段有值
insert into database.table (id, key, field)
    values (v1, v2, v3), (v1, v2, v3), (v1, v2, v3);

# 单条更新
update database.table set field = v3 where key = v1;
update database.table set field = v3 where key = v1;
update database.table set field = v3 where key = v1;

2. replace into

如果表中有主键或唯一索引,replace into 会删除冲突行(已存在记录),再新插入新行。因此导致 replace into 可能会导致主键自增 1。
如果表中没有主键和唯一索引,replace into 没有意义。

如果表中没有自增主键,使用 replace into 是一个不错的选择。

使用 replace 需要注意,如果 更新的字段 不全,会被设置为缺省值,也就是会清除其他字段。

replace into database.table (id, key, field)
    values (v1, v2, v3), (v1, v2, v3), (v1, v2, v3);

最后,推荐使用 insert into … on duplicate key update

on duplicate key update 是 mysql 特有语法。

MySQL root 密码丢失重置

前几周百度云同步时把重要文件误删,包括 mysql root 密码。

MySQL root 密码丢失,需要以类似 mysqld_safe –skip-grant-tables 方式运行,才能让 mysql -uroot 顺利执行,登录到 mysql 命令行。

如果 mysql 在运行,先将 mysql 运行方式记录下来

> ps aux | grep mysql
// 假设当前 mysql 运行命令如下,实际参数可能更复杂
/mysql/bin/mysqld --pid-file=/mysql/mysql.pid --socket=/tmp/mysql.sock

1. 关闭 mysql 进程

不要使用 kill 命令直接结束,而是通过 stop 参数结束 mysql 进程。

// 假设 mysql 启动命令是
// /etc/init.d/mysql start
// 则应该通过如下方式结束 mysql 进程
> /etc/init.d/mysql stop

2. 以 –skip-grant-tables 方式启动 mysql

在原有 mysql 运行命令基础上加上 –skip-grant-tables &,& 表示放入当前会话 background。

/mysql/bin/mysqld --pid-file=/mysql/mysql.pid --socket=/tmp/mysql.sock --skip-grant-tables &

3. 登录 mysql 修改 root 密码

> mysql -uroot
> update mysql.user set password=PASSWORD('MysqlRoot') where user='root';

4. 参考第 1 步,结束 mysql 进程

5. 执行原有 mysql 启动命令

// 假设 mysql 启动命令如下
> /etc/init.d/mysql start

使用 root 新密码登录 mysql

> mysql -uroot -p
Enter password: 

密码重置完成。

重置过程有两点需要注意:

  1. 不推荐使用 kill 结束 mysql 进程;
  2. 以 –skip-grant-tables 方式运行 mysql 需要保留原有参数

Linux 常见解压与压缩

Linux 压缩与解压命令

格式 解压 压缩
 
tar
(仅打包不压缩)
tar -xvf [原文件名].tar tar -cvf [目标文件名].tar [原文件名/目录名]
 
tar.bz2 bunzip2 [原文件名].tar.bz2 bzip2 [原文件名].tar
tar.bz2
(通过 tar 调用)
tar -jxvf [原文件名].tar.bz2 tar -jcvf [目标文件名].tar.bz2 [原文件名/目录名]
 
tar.gz gunzip [原文件名].tar.gz gzip [原文件名].tar
tar.gz
(通过 tar 调用)
tar -zxvf [原文件名].tar.gz tar -zcvf [目标文件名].tar.gz [原文件名/目录名]
 
tar.xz unxz [原文件名].tar.xz xz [原文件名].tar
tar.xz
(通过 tar 调用)
tar -Jxvf [原文件名].tar.xz tar -Jcvf [目标文件名].tar.xz [原文件名/目录名]
 
tar.Z
(已过时)
uncompress [原文件名].tar.Z compress [原文件名].tar
tar.Z
(通过 tar 调用)
tar -Zxvf [原文件名].tar.Z tar -Zcvf [目标文件名].tar.Z [原文件名/目录名]
 
7z 7z x [原文件名].7z 7z a [目标文件名].7z [原文件名/目录名]
 
jar jar -xvf [原文件名].jar jar -cvf [目标文件名].jar [原文件名/目录名]
 
zip unzip [原文件名].zip zip -r [目标文件名].zip [原文件名/目录名]

JavaScript 浮点数计算问题

JavaScript 浮点数神坑当属 0.1 + 0.2 == 0.3false

> 0.1 + 0.2 == 0.3
false
> 0.1 + 0.2
0.30000000000000004
>

还有一些比较隐蔽的问题,比如 Math.roundNumber.prototype.toFixed 也都不是能完全正常工作的。

> Math.round(1.105 * 100)
111
> Math.round(1.015 * 100)
101
> Math.round(1.025 * 100)
102
> 0.25.toFixed(1)
'0.3'
> 0.35.toFixed(1)
'0.3'

Math.round、toFixed 计算出错主要因为浮点数不能精确表示。

在这里 0.35 和 1.015 的值都不准确,一个办法是转换成整数计算再除以对应的十百千;另一个办法是采用现有的 lib,比如 accounting

> 1.015 * 100
101.49999999999999

代码运行环境

D:\node -v
v8.1.0

安装 Nginx、Lua,使用 Lua 扩展 Nginx

使用 lua-nginx-module 可以让 Nginx 通过一些指令如 content_by_lua、content_by_lua_file 运行 Lua 脚本,非常方便。

先下载所需文件

1. 安装 luajit,官网 http://luajit.org/install.html

解压之后直接 make

make PREFIX=/usr/local/luajit

2. 下载 ngx_devel_kit(github)、nginx_lua_module(github

3. 进入 nginx 源码目录准备 configure

如果已经安装 nginx,也可以再次 configure,但一定要涵盖上次使用的参数。

4. 导出 LUAJIT_LIB、LUAJIT_INC 环境变量

export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0

LUAJIT_LIB 对应 libluajit-5.1.so 文件所在目录
LUAJIT_INC 对应 lua.h 文件所在目录

目录可能不一样,但参考 LUAJIT_LIB、LUAJIT_INC 含义。

5. 在 nginx-1.10.2 目录执行 ./configure

./configure \
--prefix=/usr/local/nginx \
--with-http_flv_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_v2_module \
--with-ipv6 \
--with-ld-opt="-ljemalloc,-Wl,-rpath,/usr/local/luajit/lib" \
--with-openssl=/opt/src/openssl-1.0.2l \
--with-pcre=/opt/src/pcre-8.40 \
--with-pcre-jit \
--add-module=/opt/src/ngx_devel_kit-0.3.0 \
--add-module=/opt/src/lua-nginx-module-0.10.8

这里用到了 ljemalloc、openssl、pcre 可以预先安装

安装 jemalloc,进入 jemalloc-4.2.1

./configure
make
make install

安装 openssl pcre,或者进入源码目录安装

yum install openssl openssl-devel
yum install pcre pcre-devel

6. 测试执行 lua

在 Nginx 某个 server 下增加

location /hello { 
    content_by_lua 'ngx.say("Hello, Lua")'; 
}

尝试访问

[root@CentOS-58 06]# curl lua.zhengxianjun.com/hello
Hello, Lua!

安装成功并且正常运行。

如果对 nginx 安装目录进行版本管理,会发现 make install 完成之后只有 nginx/sbin/nginx 文件有改动。

安装完成之后切记要执行 sbin/ngxin -t 检查语法是否正确。
一定要安装正确才能执行 sbin/ngxin -s reload 。

因为对于已经安装的 nginx,如果 ./configure 没有覆盖上一次的参数,会导致某些功能失效。
第一次 configure 时没有添加 –with-http_ssl_module 导致 nginx 提示 unknown directive “ssl”。

JavaScript const、let、var 对比

ECMAScript 6 新增 const 和 let 命令,用来声明变量。

声明方式 变量提升 作用域 初始值 重复定义
const 块级 需要 不允许
let 块级 不需要 不允许
var 函数级 不需要 允许

变量提升:const 和 let 必须先声明再使用,不支持变量提升

console.log(c1, l1, v1);
// 报错
// Uncaught ReferenceError: c1 is not defined

const c1 = 'c1';
let l1 = 'l1';
var v1 = 'v1';

作用域:const,let 支持块级作用域,有效避免变量覆盖

const c21 = 'c21';
let l21 = 'l21';
var v21 = 'v21';

if (0.1 + 0.2 != 0.3) {
    const c21 = 'c22';
    let l21 = 'l22';
    var v21 = 'v22';

    console.log(c21, l21, v21);
    // 输出 c22 l22 v22
}

console.log(c21, l21, v21);
// 输出 c21 l21 v22

块级作用域,在外层不能直接访问内层变量

if (0.1 + 0.2 != 0.3) {
    const c22 = 'c22';
    let l22 = 'l22';
    var v22 = 'v22';

    console.log(c22, l22, v22);
    // 输出 c22 l22 v22
}

console.log(c22, l22, v22);
// 报错
// Uncaught ReferenceError: c22 is not defined
// 同样地, l22 is not defined

const 定义常量,该常量不能赋值,但该常量的属性可以赋值

const c231 = {};
const c232 = [];

c231.name = 'seven';
c232.push(27);

console.log(c231, c232);
// 输出 {name: "seven"} [27]

// 禁止给对象赋值,应该使用 Object.freeze

const c233 = Object.freeze({});
const c234 = Object.freeze([]);

c233.name = 'seven';
// 普通模式下不报错
// 严格模式下报错
// Uncaught TypeError: Cannot add property name, object is not extensible
    
c234.push(27);
// 普通模式下就会报错
// Uncaught TypeError: Cannot add property 0, object is not extensible

console.log(c233, c234);
// 输出 {} []

全局变量不再设置为顶层对象(window)的属性,有效避免全局变量污染

const c24 = 'c24';
let l24 = 'l24';

console.log(c24, l24);
// 输出 c24 l24

console.log(window.c24, window.l24);
// 输出 undefined undefined

符合预期的 for 循环

for (var i = 0; i != 3; i++) {
    setTimeout(function() {
        console.log(i);
    },10);
}
// 依次打印
3
3
3

for (let i = 0; i != 3; i++) {
    setTimeout(function() {
        console.log(i);
    },10);
}
// 依次打印,为啥呢
0
1
2

可以看到在 for 循环中使用 let 方式声明变量才是符合预期。
在 for 中每一次循环,let 都是重新声明变量,并且因为 JavaScript 引擎会记住上一次循环的值,初始化 i 时在上一轮的基础上计算。

可以看到在 for 循环中至少有两层作用域,看下面的例子更容易理解。

for (let i = 0; i != 3; i++) {
    let i = 'seven';
    console.log(i);
}
console.log('eight');
// 依次打印
seven
seven
seven
eight

初始值:const 声明的变量必须设置初始值,且不能重复赋值。

const c3 = 'c3';
let l3 = 'l3';
var v3 = 'v3';

console.log(c3, l3, v3);
// 输出 c3 l3 v3

c3 = 2; // Uncaught TypeError: Assignment to constant variable
l3 = 2;
v3 = 2;

console.log(c3, l3, v3);
// 输出 c3 2 2

const c32;
// 报错
// Uncaught SyntaxError: Missing initializer in const declaration

重复定义:const 和 let 不支持重复定义

const、let 缩小了变量作用域,完美避免变量污染;const 固定变量(即固定变量类型),对于弱类型 JavaScript 来说,可以明显提升性能。推荐在应用中使用 const、let 声明变量。