let声明变量
- 在js中,使用var声明的变量往往会越域,而使用let声明的变量,会形成独立的块级作用域。
- var可以给一个变量声明多次,而let只能声明一次。
- var会变量提升,let不存在变量提升
//let 形成独立的块级作用域,在当前内部没有值,会向上找
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
//var 可以声明多次
// let 只能声明一次
var m = 1
var m = 2
let n = 3
let n = 4
console.log(m) // 2
console.log(n) // Identifier 'n' has already been declared
// var 会变量提升
// let 不存在变量提升
console.log(x); // undefined
var x = 10;
console.log(y); //ReferenceError: y is not defined
let y = 20;
暂时性死区
if(true){
// temp = 'abc'; // 暂时性死区 简称 TDZ(temporal dead zone)
// console.log(temp);
let temp; // TDZ 结束
console.log(temp); // undefined
temp = 123;
console.log(temp); // 123
}
const声明变量
- 声明之后不允许改变
- 一旦声明必须初始化,否则会报错
- 类似于let
const a = 1;
a = 3; //Uncaught TypeError: Assignment to constant variable
解构表达式
数组解构
let arr = [1,2,3];
// 以前想要获取其中的值,只能通过下标,在ES6中可以这样
const [x,y,z] = arr; // x,y,z将与arr中的每个位置对应来取值
// 打印
console.log(x,y,z);//1,2,3
对象解构
基本用法
const person = {
name: "jack",
age: 21,
language: ['java', 'js', 'css']
}
//对象解构 解构表达式获取值
const { name, age, language } = person;
// 等价于下面的表示方式
// const name = person.name;
// const age = person.age;
// const language = person.language;
console.log(abc, age, language)
解构赋值
// 如果想要将name的值赋给其他变量,可以使用如下形式,username是新的变量名
const { name:username, age, language } = person;
console.log(username);
字符串扩展
新增API
- includes(str):返回布尔值,表示是否找到了指定的字符串
- startsWith(str):返回布尔值,表示指定字符串是否在原字符串的头部
- endsWith(str):返回布尔值,表示指定字符串是否在原字符串的尾部
let str = "hello.vue";
console.log(str.startsWith("hello"));//true
console.log(str.endsWith(".vue"));//true
console.log(str.includes("e"));//true
console.log(str.includes("hello"));//true
字符串模板
模板字符串相当于加强版的字符串,用反引号 ` ,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式
const person = {
name: "jack",
age: 21
}
const { name, age} = person;
let info = `我是${name},今年${age}了`;
console.log(info);
函数优化
函数参数的默认值
//在ES6以前,我们无法给一个函数参数设置默认值,只能采用变通写法:
function add(a, b) {
// 判断b是否为空,为空就给默认值1
b = b || 1;
return a + b;
}
// 传一个参数
console.log(add(10));
//现在可以这么写:直接给参数写上默认值,没传就会自动使用默认值
function add2(a, b = 1) {
return a + b;
}
console.log(add2(20));
rest参数
es6引入的rest参数,形式为(…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了,rest搭配的的变量是一个数组,这个变量,将多余的参数放入数组中
function push(array,...items){
items.forEach(function(item){
array.push(item);
console.log(array);
})
}
var a = [];
push(a,1,2,3,4);
// 注意:rest参数之后不能再有其他的参数(也就是rest参数只能是最后一个参数)
箭头函数
// 基本用法
var f = function (v) {
return v;
}
console.log(f(1));
// 箭头函数
var t = v => v;
console.log(t(9));
var y = () => 5; // var y = function () { retiunr 5 }
console.log(y());
var sum = (num1, num2) => num1 + num2; // var sum = function(num1,num2){ return num1 + num2 }
console.log(sum(1, 2));
let getS = id => ({ id: id, name: '张三' });
let num = () => { a: 1 }; // undefined
const fll = ({ a, b }) => a + '--' + b;
console.log(fll({ a: 1, b: 2 }));
// 1、如果是一个参数,可以不使用小括号,直接传入
// 2、 如果是返回一个值,可以省略return (只有一行代码的时候)
// 3、如果没参数,或者有多个参数,就使用小括号
// 4、如果返回的是一个对象 必须在对象外面加上圆括号 ,否则会报错
var handler = {
id: '123456',
init: function () {
document.addEventListener('click', function (event) { this.doSomeing(event.type) }, false)
},
doSomeing: function (type) {
console.log((type + '--' + this.id));
}
}
handler.init();
// 箭头函数中的this的指向,为什么是固定化的
// 答案:不是因为箭头函数中内部有绑定的this,实际上是因为,箭头函数,根本就没有this
// 导致内部的this,就是外层代码的this
不适用箭头函数的场合
第一个场合,定义对象的方法,并且对象的方法内包括this
const obj = {
ai:9,
// lack:function(){
// console.log(this);
// }
lack:()=>{
console.log(this); // window
}
}
obj.lack();
之前使用的是动态的this,箭头函数是静态的this
第二种场合,需要使用动态this的时候,不能使用箭头函数
var btn = document.getElementById('button');
btn.addEventListener('click',()=>{
this.classList.toggle('on'); // window
})
如果函数体很复杂,有很多行,有大量的操作,不是为了计算值,这个时候,也不要使用箭头函数,使用普通函数就可以了
Generator函数
这个是es6提供的一种异步编程的解决方案,语法行为和传统函数不一样
在理解上,可以理解为是一种状态机,里面封装的是多个内部状态 ,Generator函数会返回一个遍历器对象,这个对象可以依次遍历Generator函数内部的每一个状态。
Generator函数和传统函数比较:
- 在function关键字后面多了一个星号
- 在函数体内部,使用的是yield表达式,定义不同的内部状态
可以解决回调地狱问题
function* hello() {
yield 'hello'
yield 'world'
return 'edning'
}
var hw = hello();
// console.log(hello());
// 调用方式
//done属性的值是true,表示遍历就结束了
console.log(hw.next());//{done:false,value:"hello"}
console.log(hw.next());//{done:false,value:"world"}
console.log(hw.next());//{done:true,value:"edning"}
console.log(hw.next());//{done:true,value:undefined}
对象优化
新API
ES6给Object扩展了许多新方法:
- keys(obj):获取对象的所有key形成的数组
- values(obj):获取对象的所有value形成的数组
- entries(obj):获取对象的所有key和value形成的二维数组。
- assign(dest,…src):将多个src对象的值拷贝到dest中。(第一层为深拷贝,第二层为浅拷贝)
const person = {
name: "jack",
age: 21,
language: ['java', 'js', 'css']
}
console.log(Object.keys(person));//["name", "age", "language"]
console.log(Object.values(person));//["jack", 21, Array(3)]
console.log(Object.entries(person));//[Array(2), Array(2), Array(2)]
// assign
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
//{a:1,b:2,c:3}
Object.assign(target, source1, source2);
console.log(target);//["name", "age", "language"]
声明对象简写
const age = 23
const name = "张三"
// 之前写法
const person1 = { age: age, name: name }
// 简写(如果属性名与属性值的变量名一样,就可以简写,例如person的属性名age,变量age,名称一样,所以可以简写)
const person2 = { age, name } // 意思是:person2有一个属性age,它的属性值为age变量,name同理
console.log(person2);
对象扩展运算符
扩展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象
// 1、拷贝对象(深拷贝)
let p1 = { name: "Amy", age: 15 }
let someone = { ...p1 }
console.log(someone) //{name: "Amy", age: 15}
// 2、合并对象
let age1 = { age: 15 }
let name1 = { name: "Amy" }
p2 = { ...age1, ...name1 } // 将age1对象的所有的属性值拷贝给p2,将name1对象的所有的属性值拷贝给p2对象
console.log(p2)
注意:如果两个对象的字段名重复,后面对象的字段值会覆盖前面对象的字段值
数组的新方法
map函数
接收一个函数,将原数组的所有元素用这个函数处理后放入一个新的数组返回
let arr = ['1', '20', '-5', '3'];
// 将数组中的每个元素 x2 返回
arr = arr.map(item=> item*2);
console.log(arr);
Promise
Promise可以封装异步操作
-
Promis 创建
const promise = new Promise((resolve, reject) =>{ // 异步处理 // 处理结束后、调用resolve 或 reject }); //Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。
-
Promise Ajax
function ajax(URL) { return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.send(); }); } var URL = "/try/ajax/testpromise.php"; ajax(URL).then(function onFulfilled(value){ document.write('内容是:' + value); }).catch(function onRejected(error){ document.write('错误:' + error); });
-
Promise.then()方法:链式操作
Promise.then()方法返回的是一个新的Promise对象,因此可以采用链式写法
getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // proceed });
-
Promise.catch()方法:捕获错误
Promise.catch()方法时Promise.then(null,rejection)的别名,用于指定发送错误时的回调函数。
getJSON("/posts.json").then(function(posts) { // some code }).catch(function(error) { // 处理前一个回调函数运行时发生的错误 console.log('发生错误!', error); });
-
Promise.all()方法
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。const p = Promise.all([p1, p2, p3]);
(1)只有
p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。(2)只要
p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。 -
Promise.finally()方法
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});
Promise的all、any、race、allSettled方法的区别
一、共同点
- 这些方法的参数都接受一个Promise的iterable类型(Array,Map,Set都属于ES6的iterable类型)的输入
- 这些方法都返回一个Promise的实例
二、各方法之间的区别
then和catch都会返回一个新的promise
catch不管连接到哪里,都能捕获上层未捕捉过的错误
then和catch可以被调用多次,但如果promise内部的状态一经改变,并且有一个值,那么后续每次调用then或者catch的时候都会直接拿到该值
then和catch返回的值不能是promise的本身,否则会造成死循环
then和catch的参数都是函数,传入非函数会发生透值
finally方法返回的也是一个promise,在promise结束的时候无论结果为resolved还是rejected,都会执行里面的回调函数
finally方法不管是promise对象的状态是什么都会执行
finally()
方法的回调函数不接受任何的参数,也就是说你在.finally()
函数中是没法知道Promise
最终的状态是resolved
还是rejected
的
它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise
对象。
Set
-
基本使用
s6提供了新的数据结构Set,类似于数组,但是成员值是唯一的,也就是没有重复的值
Set是一个构造函数,用来生成set的数据结构
是可以接受一个参数的 参数:可以是一个数组,或者是iterable接口的其他数据结构
自动去重
const set = new Set([1, 5, 3, 4, 6, 2, 2, 4]); // 参数是数组 const set1 = new Set(document.querySelectorAll('div')); // 参数是类似数组的对象 console.log(new Set('abababa')); // 字符串去重
注意:跟普通的数据类型是一样的,比如6和’6’是不一样的
let set2 = new Set(); // 相当于运行了 === let a = NaN; let b = NaN; set2.add(a); set2.add(b); console.log(set2); //Set(1) {NaN} console.log(NaN === NaN);//false // 对象总是不相等的 let set3 = new Set(); set3.add({ name: "李四" }); set3.add({ name: '张三' }) console.log(set3); //Set(2) {{…}, {…}} set3.forEach(x => console.log(x.name))//李四 张三
-
属性
Set.prototype.constructor: 构造函数,默认就是Set函数
Set.prototype.size: 返回的是Set实列成员的个数
-
操作方法 – 用于操作数据
Set.prototype.add(value): 添加某个值,返回的是Set结构本身
Set.prototype.delete(value): 删除某一个值,返回值是一个布尔值,表示是否删除成功
Set.prototype.has(value): 返回一个布尔值,表示值是否是Set的成员
Set.prototype.clear(): 清除所有的数据,没有返回值 – 使用请慎重
let s = new Set(); let s2 = new Set(['A','B','C','D']) //元素个数 console.log(s2.size); //添加新的元素 s2.add('E'); //删除元素 s2.delete('A') //检测 console.log(s2.has('C')); //清空 s2.clear() console.log(s2);
-
遍历方法 – 用于遍历数据成员
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach(): 使用回调函数遍历每个成员
let set5 = new Set(['red', 'green', 'blue']); for (let key of set5.keys()) { console.log(key);//red green blue } for (let key of set5.values()) { console.log(key);//red green blue } // 因为Set结构,没有键名,只有键值(理解为键名何键值是同一个值) for (let key of set5.entries()) { console.log(key);// ['red', 'red'] ['green', 'green'] ['blue', 'blue'] } let set6 = new Set([1,3,5]) set6.forEach( (value,key)=> console.log(value + '---' + key))//1---1 3---3 5---5
Map
javascript对象(Object),本质上是键值对的集合(Hash结构)
为了解决对象的键是一个字符串,es6提供了Map() 数据结构
类似对象,也是键值对的集合,但是要注意的是,键的范围 不限制于是字符串,各种类型的值,都可以做 键 名
Object实现了 字符串 – 值 方式,但是Map结构实现了 值 - 值 的对应,是一种更加完善的Hash结构
let m = new Map();
m.set('name','ran');
m.set('change',()=>{
console.log('改变!')
})
let key={
school:'atguigu'
}
m.set(key,['成都','西安']);
//size
console.log(m.size);//3
//删除
console.log( m.delete('name'));//true
//获取
console.log(m.get('change'));
//()=>{
// console.log('改变!')
// }
// //清空
// m.clear()
//遍历
for(let v of m){
console.log(v);//['change', ƒ]
}