let 和 const
类似var定义变量,但是let修饰的变量仅在声明的代码块中有效;
var声明的变量,在全局有效
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
js中的for循环声明循环变量的部分也作为一个父作用域,即(let i = 0; 😉, 而代码块部分的let i = ‘abc’为一个子作用域。
let修饰的变量必须先声明再使用。
let 和 var 使用对比:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
const lis = document.querySelector('ul').querySelectorAll('li')
for (var i = 0; i < lis.length; i++) {
lis[i].addEventListener('click', function () {
console.log(lis[i]); // undefined
})
}
</script>
这里使用var作为循环变量的声明,但由于使用的是var,所以 i 为全局变量,当循环结束 i 数值为 lis.length, 超过数组下标;当触发事件的时候 lis[i] 下标越界因此为 undefined;
解决方案为使用 let 替代 var,这样含义为在每一次绑定事件时声明一个 let i 记录当前下标。
只要使用let 进行声明变量后,那么该变量之前的部分称为 “死区”,再死区部分使用let变量会报错
temp = "abc"
let temp; // 该代码之前为死区部分,无法访问
let修饰的变量,不允许重复声明。
ES6中的块级作用域中允许声明函数。
const修饰的变量表示常量,一旦声明,不能改变。维护的是变量指向的内存地址是不变的。
字符串
字符串多行定义
ES6中可以使用反引号(`)标识,当作普通的字符串或者定义多行字符串,或者嵌入变量
new Vue({
el: '#app',
computed: {
b() {
let [name, age] = ['righteye', 18]
return `this is a template string, username = ${name}, age = ${age}`
}
}
})
标签模板
一种函数调用的特殊形式,以 tag`content` 的形式进行展现,其中tag为函数名,后面的content为反引号包裹的模板字符串。
参数:param会接收被${} 变量分割的普通的字符串信息,变量会被后面的其他参数接收。
new Vue({
el: '#app',
methods: {
f(param, ...val) {
// param: ['hello', 'world']
// val: ['righteye', 18]
// typeof val: object
console.log(param, val, typeof val)
return param + val
}
},
computed: {
a() {
let name = 'righteye'
let age = 18
return this.f`hello ${name} world ${age}`
},
b() {
let [name, age] = ['righteye', 18]
return `this is a template string, username = ${name}, age = ${age}`
}
}
})
箭头函数
ES6可以使用箭头函数简化代码的书写:
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
对于普通函数,内部的this指向运行时的所在的对象,即调用者;而对于箭头函数,this指向定义时所在的作用域, 函数的所用者
<p id="banner"></p>
<p id="demo"></p>
<p id="res"></p>
<script>
function Hello() {
this.s1 = 2;
this.s2 = 0;
// setTimeout(function() {
// console.log(this); // Window
// document.getElementById("banner").innerText = "我是普通函数"
// document.getElementById("demo").innerText = ++this.s1 + " " + this.s2; // NaN undefine
// }, 1000);
setTimeout(() => {
console.log(this); // Hello
document.getElementById("banner").innerText = "我是箭头函数"
document.getElementById("demo").innerText = ++this.s1 + " " + this.s2; // 3 0
}, 1000);
}
h = new Hello();
document.getElementById("res").innerText = h.s1 + " " + h.s2;
</script>
上述案列中在传统 function() 定义方式中, 定时器执行指向调用者为 window 对象; 而使用箭头函数执行时 this 执行为声明时作用域, 即 funciton Hello() {}, 因此返回 Hello。
另外的, 箭头函数中不存在 arguments 参数,不能作为构造函数声明对象。对于 arguments 参数, es6 中使用 rest 参数进行替代
let fn = (...args) => {
console.log(args); // (5) [1, 2, 3, 4, 5]
}
fn(1, 2, 3, 4, 5);
使用 ‘…参数名称’ 并放在所有形参的末尾,参数类型为数组。
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
迭代器的使用
使用迭代器的对象,内部包含 Symbol.iterator 属性,此类对象可以使用 for … of 进行遍历,相对 for … in 获得 key ,该遍历用于获取 value。
const arr = {
name: 'abc',
status: [
'lufei',
'suolong',
'shanzhi',
'qiaoba'
],
// 1. 迭代对象需要包含 Symbol.iterator 属性
// 2. Symbol.iterator 返回一个对象
// 3. [2]中返回对象需要包含 next 方法
// 4. 返回的对象需要包含 value 和 done 属性
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.status.length) {
return {
value: this.status[index++],
done: false
}
} else {
return { value: undefined, done: true }
}
}
};
}
}
// 遍历 arr, 直接返回 status 中的值
for (let value of arr) {
console.log(value);
}
Promise
-
异步编程的一种解决方案;通过new Promise()可以创建对象,该对象具有状态:进行中,已成功,已失败,一旦使用resolve()状态便定性不可以再更改,后续的回调方法也会理解得到当前状态下的结果。
通过Promise可以将异步的操作同步的表现出来。 -
Promise中的then()添加再Promise.prototype的原型链上,可以为Promise实例添加状态改变时的回调函数;也就是可以通过返回Promise对象继续再then()的方法中改变当前Promise的状态(resolve/reject),以链的形式调用then()方法
-
Promise.prototype.catch可以捕获reject抛出的异常;但是在调用resolve()后再次抛出异常是无效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test'); // 无效的抛出异常
});
- Promise的错误具有冒泡的性值,可以一直上传直到被捕获。
<div id="app">
<span> {{ value }} </span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
value: ''
},
methods: {
onPromiseHandle(data) {
return new Promise((resolve, reject) => {
resolve(data); // resolve的调用会继续进入then语句中
// reject("fail"); // reject会被catch捕获,如果没有继续向上抛出异常
}).then((data) => {
return Promise.resolve(data + 'js');
}).then((data) => {
return Promise.resolve(data + "xxx");
}).then((data) => {
this.value = data;
return this.value;
})
// .catch((err) => {
// console.log(err)
// })
}
},
mounted() {
this.onPromiseHandle("hello").then((data) => {
console.log("=>", data)
}).catch((err) => {
console.log("error=>", err)
})
}
})
</script>
其中 Promise.resolve()等价于:
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Generator
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
通过yield定义不同的状态,使用* 号定义Generator返回迭代器对象;每一次需要以hw.next()的方式进行获取yield的结果
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
yield类似暂停的函数,遇到yield后会将yield后的表达式结果作为返回值,并且阻塞后面的操作,只有在调用next()后才会继续执行。
一个函数中可以执行多个yield,但是只能有一个return
async函数
相比于Generator,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await
new Vue({
el: '#app',
data: {
},
methods: {
*testGenerator() { // 定义一个Generator
for (let i = 0; i < 5; i++) {
yield i + 1
}
}
},
computed: {
a() {
let res = ""
let taskObj = this.testGenerator();
let task;
while (!(task = taskObj.next()).done) { // 消费task的value
res += task.value;
}
return res
}
}
})
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
类似的ES6 还有 class 扩展, 类比 Java, 包含 set 和 get 方法, 类继承等