ES6新特性
let和const
- let 和const都是块级作用域
- let和const在同一个块中不能申明同名变量,不会覆盖会报错
- const不能修改值
- 如果想要让定义的对象中的值无法修改
<script>
const person = {
name: 'Jelly',
age: 20
};
const array = [1,2,3,4,5];
// 对于object来说,使用freeze无法修改
const Jelly = Object.freeze(person);
Object.freeze(array)
person.age = 21;
array[0] = 2;
console.log(array)
console.log(person)
</script>
- 同名不覆盖
<script>
// var name = 10; // 使用var声明的变量如何和window中的名字重复会覆盖
let name = 10; // 即使和window中的名字重复也不会覆盖
{let name = 10;} // 如果想要在页面中console.log(name) 是window的name,这样就可以
</script>
- 函数内部的问题
for (let i = 0; i<10 ; i++) {
console.log(i);
setTimeout(function(){
console.log(`${i}`);
// 如果使用var的话,会把for循环执行完后再执行这个函数,也就是会打印10个10
// 如果使用let的话,因为它的作用域是在{}中,所以会打印1-10
}, 1000)
}
如果我将window.name改变了,那么只要我这个页面不关闭,name值无论刷新多少次,都是改变后的那个值
箭头函数 Array Function
- 简单使用
<script>
const numbers = [2, 4, 6, 8, 10];
// 返回numbers中每一个元素*2的新数组
// const doubleNumber = numbers.map(
// function(number){
// return number*2;
// }
// )
// 等价于上面的那个函数
// const doubleNumber = numbers.map(
// (number) => {
// return number*2;
// }
// )
// 如果只有一个参数,那么可以省略(),多个参数,必须加()
// const doubleNumber = numbers.map(
// (number, i) => {
// console.log(`${i}:${number}`)
// }
// );
// 如果要返回一些简单的值,也等价于上面的那个
const doubleNumber = numbers.map(number => number*2);
console.log(doubleNumber);
</script>
- this的作用域
<script>
const Jelly = {
name: 'Jelly',
hobbies: ['Coding', 'Sleeping', 'Reading'],
printHobbies: function(){
console.log(this); // {name: 'Jelly', hobbies: Array(3), printHobbies: ƒ}
// let _this = this;
// this.hobbies.map(function(hobby){
// // 这里的this指向的是window本身
// // 这里运行的map回调函数是独立于Jelly的,这样它的this指向是window/global,在严格模式下他是undefined
// // console.log(this); //Window {window: Window, self: Window, document: document, name: '10', location: Location, …}
// // console.log(`${this.name}:${hobby}`);
// // 如果想要指向Jelly,现将this赋值,再使用即可
// console.log(`${_this.name}:${hobby}`);
// })
// 箭头函数的this 是没有自己的this值,是定义的时候定好的,是指向他的父级作用域的,所以这里是指向this.hobbies的this的
this.hobbies.map(hobby => console.log(`${this.name}:${hobby}`));
}
}
Jelly.printHobbies();
</script>
- 函数参数可以使用=来赋予默认值
- 箭头不适用场景
<script>
// const person = (name, age) => {
// console.log(this);
// this.name = name;
// this.age = age;
// }
// js的构造函数,是需要通过new来创建对象的
// js的构造函数不能使用箭头函数,因为它的this没有父级,所以会指向window
const person = function (name, age) {
this.name = name;
this.age = age;
}
// 原型的构造函数也是不能使用箭头函数,会指向window
person.prototype.updatePoints = function(){
this.points++;
console.log(this.points);
}
const button = document.querySelector('.zoom');
// 箭头函数没有绑定click的方法,绑定方法就是用原本的function函数
button.addEventListener('click', function(){
this.classList.add('in');
// 这里只能使用箭头函数,因为其回调函数是使用的window的,但是通过箭头函数绑定父亲,可以绑定到
setTimeout(() => {
this.classList.remove('in');
},2000)
})
// 箭头函数中没有定义arguments对象,若要使用内置的arguments对象,则需要使用function函数
// const sum = function() {
// return Array.from(arguments).reduce((prevSum, value) => prevSum+value, 0)
// //Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组,也是ES6的新增方法。
// }
// const sum = function(){
// return Array.from(arguments).reduce((prevSum, value) => prevSum+value, 0)
// }
// ...args 就是以数组的形式存放的参数
const sum = (...args) => {
return args.reduce((prevSum, value) => prevSum+value, 0)
}
const Jelly = new person("lili", 18);
console.log(Jelly);
</script>
使用箭头函数的时候,避免直接调用的this情况和需要使用function的arguments对象的情况,其他时候随意使用箭头函数即可
字符串模板
- 简单使用:便于书写字符串,便于书写vue模板
<script>
const name = 'Sakura';
const age = 5;
const centence = `${name} is ${age * 5} years old.`
console.log(centence);
const template = `
<div class="greet">
<p>${name}</p>
</div>
`;
console.log(template);
</script>
- 标签模板字符串
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo1</title>
<style>
.highlight{
padding: 2px,5px;
background: red;
color: white;
}
</style>
</head>
<body>
<div class="zoom">
</div>
</body>
</html>
<script>
const user = 'Mary';
const topic = 'Learn to markdown';
const a ='a';
const sentence = hightlight`a ${user} has commented on your topic ${topic} b`;
// 在标签模板字符串中,标签函数的返回值就是其返回值
const zoom = document.querySelector('.zoom');
console.log(sentence);
zoom.innerHTML = sentence;
function hightlight(strings, ...values){
// ...values :将剩余参数传入values数组中并保存
// strings:返回一个被传入参数分割的数组
// console.log(strings)
// console.log(values)
const hightlighted = values.map((value) => `<span class="highlight">${value}</span>`);
// console.log(hightlighted)
// let str = '';
// strings.forEach((string, i) => str += `${string}${hightlighted[i] || ''}`);
// return str;
// 使用reduce方法, prev是之前操作后的值,curr是当前的值, i是索引
return strings.reduce((prev, curr, i) => `${prev}${curr}${hightlighted[i] || ''}`, '');
// 为什么后面要加'': Current Index (idx) (当前索引)如果传入了initialValue,索引从0开始,没传入从1开始
}
</script>
- 防止xss攻击:这里就不写了,xss前端感觉没什么意义,主要还是后端做吧
对象解耦
只有当对象中的值是undefined的时候才会使用默认值
<script>
const Tom = {
name: "Tom",
age: 18,
parent: {
father: "Father",
mother: "Mother"
}
};
// 这样可以批量赋值
// 如果前面有已经定义的变量需要覆盖加个()
let name = "tt";
( {name,age} = Tom);
console.log(name);
// 可以嵌套赋值
// 如果之前声明过了,则可以使用:进行重命名
const father = 'a';
const {father: f, mother} = Tom.parent;
console.log(f);
console.log(mother);
</script>
数组解耦
// const numbers = ['one', 'two', 'three', 'four'];
// others一定要在数组数组的最后面,存放的是剩余数据形成的列表
// const [one, two, ...others] = numbers;
// 跳过某一元素赋值
// const [one, , three] = numbers;
// 给不存在元素赋予默认值
// const [one, , , , five = 5] = numbers;
// 交换值
let a = 10;
let b = 20;
[a, b] = [b, a];
// console.log(one, two, others);
// console.log(one, three);
// console.log(five);
console.log(a,b);
四种for循环
const numbers = ['one', 'two', 'three', 'four'];
// 普通for循环
// for (let i = 0; i < numbers.length; i++){
// console.log(numbers[i]);
// }
// foreach:循环中不能终止或者跳过
numbers.forEach(number => {
console.log(number);
});
// for in :会将原型中属性和后面添加的属性也遍历出来
// for (const number in numbers) {
// if (numbers.hasOwnProperty(number)) {
// const element = numbers[number];
// console.log(element);
// }
// }
// forof :ES6中添加的,解决了上述缺点
// for (const number of numbers) {
// console.log(number);
// }
forof的使用实例
// 这样遍历的话可以同时遍历出索引值和其值
// for (const [index, fruit] of numbers.entries()) {
// console.log(index, fruit);
// }
// arguments不是一个数组,而是一个类数组对象,所以要是想让传入的参数求和的话,需要先转为字符串或者使用forof
// 转为字符串
// function sum(){
// const numbers = Array.from(arguments);
// let sum = 0;
// numbers.forEach(number => {
// sum += number;
// });
// return sum;
// }
// // 使用forof方法
// function sum(){
// let total = 0;
// for (const iterator of arguments) {
// total += iterator;
// }
// return total;
// }
// console.log(sum(1, 2, 3, 4));
// forof 遍历字符串
// let name = "adasd";
// for (const iterator of name) {
// console.log(iterator);
// }
// forof 遍历列表元素
let l1 = document.querySelectorAll('li');
for (const iterator of l1) {
iterator.addEventListener('click', function(){
// console.log(this.classList);
this.classList.toggle('completed');
console.log(this.classList);
})
}
ES6队列
// 遍历时需要先将类数组对象转换为数组对象,不然无法调用Array的map方法
// const names = Array.from(l1).map(iterator => iterator.textContent);
// const names = Array.from(l1, iterator => iterator.textContent); // 等价于上面辣个
// console.log(names);
// 将字符串转换为字符数组
// const name = "sad";
// const l1 = Array.from(name);
// console.log(l1);
//Array.of():
// const arr = new Array(1); // 创建一个长度为1的空数组
// const arr = new Array(4); // 创建一个长度为4的空数组
// const arr = new Array(1,2,3); // 创建一个[1,2,3]数组
const arr = Array.of(1); // 创建一个[1]数组
console.log(arr);
Array新方法
const arr = [
{name: "jack", age: 18},
{name: "tom", age: 19},
{name: "smith", age: 30},
]
// array.find():查找并返回第一个符合要求的元素,在回调函数内部符合条件返回true,不符合返回false
// element 当前数组元素, index:当前元素的索引, array:整个的arr数组
// const tom = arr.find((element, index, array) => {})
// const tom = arr.find(element => element.name === 'tom');
// array.findIndex():使用和find()完全一样,只不过返回的索引
// const tom = arr.findIndex(element => element.name === 'tom');
// array.some();当数组中有元素符合其条件,则返回true,并停止下面的执行,否则返回false
// const tom = arr.some(element => element.name === 'tom');
// array.every();当数组中所有元素符合其条件,则返回true,否则返回false
const age = arr.every(Element => Element.age > 19);
// console.log(tom);
console.log(age);
剩余参数
// 在任何能使用参数的地方,都可以使用...表示剩余参数
function sum(...numbers){
return numbers.reduce((prev, curr) => prev + curr, 0);
}
console.log(sum(1,2,3,4));
拓展运算符
简单理解
// 拓展运算符: 将数组中的元素遍历出来并存放在一个新的数组中(地址改变)
// 字符数组遍历
// const str1 = "dasdda";
// const arr1 = [...str1];
// console.log(arr1);
// 数组的叠加和添加新的元素
// const arr1 = [2,54,5,6];
// const arr2 = [3,4,4,2];
// const newArr = [...arr1, 23, ...arr2];
// console.log(newArr);
// 复制数组到一个新的地址
// 如果这样复制数组的话,指向同一个地址,只要一个改变,则另一个也改变,类似于java浅拷贝
// const arr1 = [2, 4, 2, 3];
// const arr2 = arr1;
// arr2[0] = 3;
// console.log(arr1,arr2);
// 复制数组是新建一个新的数组,java深拷贝
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
arr2[0] = 2;
console.log(arr1,arr2);
一个简单的demo
// 将每一个字母套一个span标签
const root = document.querySelector(".root");
// console.log(root.textContent);
const arr = [...root.textContent];
function drift(arr){
// 去除‘,’直接使用join即可:
// join() 方法用于把数组中的所有元素转换一个字符串。
// 元素是通过指定的分隔符进行分隔的。
return arr.map(value => `<span>${value}</span>`).join('');
}
const rootDrift = drift(arr);
root.innerHTML = rootDrift;
常用场景
- 替换Array.from()
- 数组元素是对象的时候,直接使用slice,进行分割删除会出现生成的数据元素每一项是一个Array,为了将其转换为Object,所以可以使用…
- 函数中的使用
// 拼接数组
// const arr1 = ['a', 'b', 'c'];
// const arr2 = ['d', 'e'];
// 如果直接使用push函数,会将arr2以数组的形式拼接到第四个元素
// arr1.push.apply(arr1, arr2);
// 如果使用,这样就可以不用使用apply方法了
// arr1.push(...arr2);
const arr1 = [2022, 9, 5];
// Date函数编译后等价于2022.10.5
// 批量传入参数
// const date = new Date(arr1[0], arr1[1], arr1[2]);
const date = new Date(...arr1); // 等价于上面那个
console.log(date);
对象字面量
// const name = "jack";
// const age = 18;
// const sex = "m";
// 简化定义对象
// const jack = {
// name, // 等价于name = name
// age,
// sex,
// getname(){} // 等价于getname: function(){}
// }
// 计算属性,在对象中可以使用计算属性进行属性名和属性的自动计算,如下面实现id的自增
// let id = 0;
// const userIds = {
// [`user-${++id}`]: id,
// [`user-${++id}`]: id,
// [`user-${++id}`]: id,
// [`user-${++id}`]: id,
// }
// console.log(userIds);
// 将两个数组存放在key:value对应的对象中
const keys = ['name', 'age', 'sex'];
const values = ['tim', 18, 'm'];
const dict = {
[keys.shift()]: values.shift(),
[keys.shift()]: values.shift(),
[keys.shift()]: values.shift(),
}
console.log(dict);
Promise
axios
// let user;
// 理论上应该先发送第一个请求,然后赋值后在发送第二个请求,但是直接使用ajax,它的顺序是不固定的
// 获取所有用户,并赋值给第一个用户
// $.get('https://api.github.com/users', data => {
// console.log("fetched all users");
// user = data[0].login;
// });
// // 获取用户的repos
// $.get(`https://api.github.com/users/${user}/repos`, data => {
// console.log("fetch user repos");
// console.log(data);
// })
// 第一种解决方法,把第二个请求写在第一个的回调函数之中
// $.get('https://api.github.com/users', data => {
// console.log("fetched all users");
// user = data[0].login;
// $.get(`https://api.github.com/users/${user}/repos`, data => {
// console.log("fetch user repos");
// console.log(data);
// })
// });
// 第二种方法:使用axios,也就是封装了promise的ajax
let username;
const usersPromise = axios.get('https://api.github.com/users'); // 封装到promise中
console.log(usersPromise);
usersPromise.then(response => {
console.log(response.data);
// username = Array.from(response.data).map(element => element['login']);
username = response.data[0].login;
console.log(username);
console.log(response.data[0].login);
return axios.get(`https://api.github.com/users/${username}/repos`).then(
response => {
console.log(response);
}
).catch(err => console.log(err));
});
Symbol
// Symbol用来解决命名冲突的
// 当出现同名属性的时候,会覆盖,如下面
// const stu = {
// 'jelly': {grade: 90, gender: "f"},
// 'jelly': {grade: 80, gender: "f"},
// };
const stu = {
[Symbol('jelly')]: {grade: 90, gender: "f"},
[Symbol('jelly')]: {grade: 80, gender: "f"},
};
// console.log(stu);
// Symbol是不能遍历的,取值用下面的方法
// 取key
// const syms = Object.getOwnPropertySymbols(stu);
//取values
const syms = Object.getOwnPropertySymbols(stu).map(syms => stu[syms]);
console.log(syms);