郭减

前端开发工程师,前端爱好者

本文主要讲解 ES6 中 简单 常用 的一些简单语法,不做过深介绍,深入介绍可异步阮一峰大神的 ECMAScript 6 入门

let 和 const

es6新增了两个声明变量的命令分别为 letconst

let

let声明变量,在声明的时候才定义

特点:只在当前代码块中有效(注意是“代码块”,而不是“作用域”)

用处

为何要用 let :当我们希望一个变量只在当前代码块有效,而不影响当前作用域的时候使用,例如以下两种情况

1
2
3
4
5
for(let i = 0;i<100;i++){

}
// 我们希望 i 仅仅在for循环里面使用
// 而且仅仅在当前的for 循环里面使用
1
2
3
4
5
for(let key in obj){

}
// 我们希望 key 仅仅在for循环里面使用
// 而且仅仅在当前的for 循环里面使用,如果使用 var,最终会留下一个变量 key 在内存中

用法解析

1
2
3
4
5
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// i is not defined

i 是在 for 循环里面声明使用的,故无法在外面使用

1
2
3
4
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}

上面代码正确运行,输出了3次 abc ,这表明循环内部(花括号内部)的变量 i 与循环变量 i 不在同一个作用域,有各自单独的作用域。

暂时性死区

1
2
3
4
5
6
var a = 123;
if (true) {
a = 'abc'; // ReferenceError
let a;
}
// a is not defined

上面的代码中,按原有逻辑分析,a 开始为 123 ,在 if 里面使用的时候,会到上一级去寻找 a 的定义和赋值的地方 ,于是能赋值成功才对。
但该赋值会报错,是因为 let 导致的

ES6 明确规定,如果区块中存在 letconst 声明变量,那么在一开始就在当前代码块形成了一个块级封闭作用域,凡是在声明之前就使用这些变量,都会报错

于是就产生了一个很有意思的问题

1
2
3
typeof a12345
let a12345
typeof a123456

会发现 a12345 是报错,而一个没有声明的变量 a123456 却是 undefined

const

其实在一般使用的时候 const 和 let 区别不大

const特点

const 旨在定一个固定不变的变量

注意:这个“固定不变”指的是内存地址

举例:

1
2
3
4
5
const num = 10;
num = 20 // 报错

const obj = {age:12}
obj.age = 14 // no problem

字符串模板

这个的用处真的是太大了

我曾见过这样的代码:

不知道你们看到这样的代码的时候,内心是不是很激动啊😂

在 ES6 里面,如果有些地方不得不用字符串拼接的时候,可以这么使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a,b,c,d,e = 10;
var html = '';
for(let i = 0;i<20;i++){
html+= `
<div>
<span>${a}</span>
<span>${b}</span>
<span>${c}</span>
<span>${d}</span>
<span>${e*i}</span>
</div>
`
}
$(".xxx").append(html)

在ES6中可以用 ` ` 来包裹字符串,

  1. 在其中可以肆无忌惮的使用单引号和双引号。
  2. 变量或者表达式 用 ${} 包裹起来

箭头函数

ES6 里面提供了函数的简写,也就是箭头函数

1
2
3
var ad = ()=>{
console.log(123)
}

用处

1
2
3
4
5
6
var sum = 0;
[1,2,3].map((item)=>{sum+=item})
// sum 6

// 也可以简写为
[1,2,3].map( item =>{sum+=item})

如果需要 return ,可以直接省略后面的花括号

1
2
var arr = [1,2,3].map( item => item * 2 )
// [2,4,6]

例子

这里有个有意思的小例子

注意的是,箭头函数里面 this 的作用域,是在定义的时候生效的,而不是在运行的时候生效,具体看阮一峰大神的注意点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj2 = {
location:'博文',
add:function(){
console.log(this.location)
}
}
// obj2.add() == '博文'
var obj2 = {
location:'博文',
add:()=>{
console.log(this.location)
}
}
// obj2.add() != '博文'

上面代码里面 obj2 里面的 this 发现指向的是 window 对象

数组和对象

数组和对象里面,用到的比较多的就是迭代吧

以前我们复制一个数组或者对象的方法是以下这样

1
2
3
4
var arr1 = [1,2,3].concat([])
var arr2 = [1,2,3].splice()
var arr3 = Object.assign([],[1,2,3])
var obj1 = Object.assign({},{age:1})

方法很多就不一一列举了

在ES6中就方便多了

1
2
3
4
5
6
var arr = [1,2,3]
var arr1 = [...arr]

var obj0 = {age:123}
var obj1 = {name:'xxx'}
var obj2 = {...obj0,...obj1}

不过要注意的是,这些复制的都是浅拷贝

数组常用方法

以下四个方法,平时用的比较多的方法

map

.map 不改变原数组,生成一个新的数组

1
2
var arr = [1,2,3,4]
var sum = arr.map( item => item*2)

reduce

.reduce 不改变原数组,第一个参数为计算后的值,也是最后返回的值,后面才是 item , index

1
2
3
var arr = [1,2,3,4]
var arrSum = arr.reduce( (sum,item) => sum+item)
// arrSum == 10

filter

.map 不改变原数组,生成一个新的数组

1
2
3
var arr = [1,2,3,4]
var arr = arr.reduce( item => item >= 3)
// [3,4]

他跟 .map 方法很像,一般要对数据进行处理就用 .map 方法,而仅仅是过滤数据就用 .filter

includes

.includes 判断数组是否存在某值

1
2
3
var arr = ['a','s','d','f']
arr.includes('s') // true
arr.includes('s',3) // true

includes 第二个参数,查询的起始位置

什么是“变量提升”

首先上个例子

1
2
3
console.log(a)
var a = 10
// undefined

上面这段代码估计大家都知道,打印的是undefined,因为js在加载的时候存在变量提升。上面的这段代码其实等同于这个:

1
2
3
var a;
console.log(a)
a = 10

再举一个例子大家看看

1
2
3
4
5
if(false){
var a = 10
}
console.log(a)
// undefined

我们可以发现,明明 if 语句里面的代码没有执行,但 a 的值依然是 undefined,说明预解析的过程并不受到 if 判断语句的影响

这就是变量提升导致的

  • 变量提升的原因:在当前作用域下,js运行之前,会把带有var和function关键字的事先声明,并在内存中安排好,这个过程叫做 预解析。然后再从上到下执行js语句。
  • 注意:预解析只会发生在通过var定义的变量和function上。

变量提升之 – var和function区别

刚刚提到了 “有varfunction关键字的事先声明”,那么 functionvar 都是变量提升,他俩在提升的时候,是并列的顺序往下提升呢,还是有一定的规则呢,我们看个例子

1
2
3
4
5
function add(){
console.log(2345)
}
var add;
console.log(add)

打印 add 之后发现了什么呢? 结果是 f add(){console.log(2345)} 还是 undefined 呢?大家可以自行将声明的顺序调换一下,发现打印的还是一样的结果

这其中就说明了两个问题

  1. js在预解析的时候, function 声明的变量,会覆盖 var 声明的变量
  2. function ,不但声明了变量,而且定义了变量的内容(因为刚刚的add打印的是 f add(){console.log(2345)} ,而不是 function(){}

拓展

let声明变量

1
2
console.log(a)
let a = 10

以上代码会报错 a is not defined ,说 a 未定义, let 是 ECMAScript 2015(es6)中声明变量的方式之一

说明 let 定义的变量,不存在变量提升,即 在哪定义变量,即在哪声明变量

测试题

试题如若不理解,请再次读一下预解析的定义:

在当前作用域下,js运行之前,会把带有var和function关键字的事先声明,并在内存中安排好,这个过程叫做 预解析。然后再从上到下执行js语句。

试题1

1
2
3
4
5
6
7
8
function add(){
var b;
console.log(a)
return;
function a(){
console.log(b)
}
}

试题2

1
2
3
console.log(a)
a = 10
// 不声明而直接使用的变量是全局变量

前端页面性能优化一 之 打包

说明:我这里用的是vue项目,vue-cli 3.0 ,本文以此为例

解决问题的方式,都直接在“方法”里面写了,有不懂的,欢迎大家交流😂

去sourceMap

方法:找到 vue.config.js 文件,找到 productionSourceMap 将其改为 false ,完

效果:

总结:可以看到,打包前,48个文件,其中有四个是以 .map 结尾的文件,这个时候,整个包大概有 6M,在将其改为false之后打包,只有 2.7M 的大小了,是不是很开心😯

打包 gzip

方法:

  • npm install compression-webpack-plugin –save-dev
  • 找到 vue.config.js 引入该包,var CompressionWebpackPlugin = require("compression-webpack-plugin")
  • module.exports 里面配置该包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
configureWebpack: (config)=>{
if(process.env.NODE_ENV == 'production'){
return {
plugins:[
new CompressionWebpackPlugin({
test:/\.js$|\.css$\.html$/,
threshold:10240,
deleteOriginalAssets:false // 不删除原js文件
})
]
}
}
},
}
  • 服务端也要配置加载才行,这里我用的 node.jsexpress ,所以配置如下
1
2
3
4
5
6
7
8
9
var express = require('express');
var compression = require('compression') // 引入此包来加载
var app = express()
app.use(compression());
app.use("/blog",express.static(__dirname+"/public"))
app.use("/",express.static(__dirname+"/dist"))
app.listen(3000, () => {
console.log(`loaclhost:3000`);
});

打包效果:

页面加载速度效果:

总结:这个需要后端配合加载才行。前端打包之后,会生成 .gz 结尾的文件,直接丢到服务端加载就好。 deleteOriginalAssets 也可以选为 true 这样打出的包更小,但由于直接用的是 .gz 的文件,所以源文件是否上传到服务器,影响不大,看个人喜好吧😂