1.变量声明let 、const
之前声明变量使用var,es6使用let、const.
let是函数作用域,let和const是块级作用域。
var特性(最好不使用):
var定义的变量是全局变量,属于window,在全局使用,存在变量提升。
变量提升:Javascript将声明移至作用域scope(全局或者当前函数作用域)顶部的行为。
let特性:
(1) 没有预解析,不存在变量提升(其实也是存在变量提升的,只是存在临时性死区)
说明:在代码块内,只要let定义变量,在之前使用都是报错
应该先定义完在使用
(2) 是一个块级作用域
(2) 同一个作用域里,不能重复定义变量
(3) for循环,for循环里面是父级作用域,里面又一个
(4) let声明的变量是可以重新赋值的
const特性:
const特性和let一样
不同点是,const定义变量不能修改(常量)
const定义完变量必须有值,不能后赋值,不能修改
类似于:Object.freeze(对象)
但是如果const定义了一个对象(引用类型)并初始化完成,之后再去给对象赋值会报错,但是给对象的属性赋值是不会报错的,也就是说const对象的属性是可以重新赋值的,如果不想要被重新赋值,可以使用Object.freeze(const对象)。
2.解构赋值(特别好用,尤其是在数据交互时 ajax)
注意点:左右两边,结构要保持一致
let {name,age,job} = { //一一对应
name:"sss",
age:12,
job:"jjhhjk"
}
let {name n,age a,job j} = {//可以重命名
name:"sss",
age:12,
job:"jjhhjk"
}
// 解构的时候可以给默认值
// 只有值明确是undefined的时候才会使用默认值,如果值是false或者0或者null等都不会使用默认值
let [a,b,c="默认值"] = ["aaa","bbb"]
const Tom = {
name: "Tom Jones",
age: 23,
family:{
mothor: "Norah Jones",
father: "Richard Jones",
brother: "Howead Jones",
sister: undefined
}
}
const {mothor,father,brother,sister = "has no sister"} = {Tom.family}
console.log(sister) // has no sister
const Tom = {
name: "Tom Jones",
age: 23,
family:{
mothor: "Norah Jones",
father: "Richard Jones",
brother: "Howead Jones",
sister: false
}
}
const {mothor,father,brother,sister = "has no sister"} = {Tom.family}
console.log(sister) // false
let {floor,pow} = Math; //结构Math函数
console.log(floor(1.1));//1
console.log(pow(2,3)); //8
let {length} = 'yhgggg' //结构原生属性,不用定义的
console.log(length); //6
应用:交换数据
3. 字符串模板以及字符串新增的东西
3.1 字符串模板 ``(个人觉得非常好用)
`任何东西${变量}`
字符串模板是可以嵌套使用的。
3.1.1 标签模板字符串
标签模板字符串的最佳实践:过滤用户输入
https://github.com/cure53/DOMPurify
https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.8/purify.min.js
<script>
const list = [
{id:1,name:"aaaa"},
{id:2,name:"bbbb"},
{id:3,name:"cccc"},
{id:4,name:"dddd"}
]
let doc = document.getElementById("box");
// let htmlcontent = `
// <ul v-for="${(item,index) in list}" :key="${index}">
// <li id="${item.id}">${item.name}</li>
// </ul>
// `
//v-for不能正常渲染,报错如下
//template.html:26 Uncaught ReferenceError: item is not defined at template.html:26:23
let teplhtml = `<ul>
${list.map(item=>{
return `<li id="${item.id}">${item.name}</li>`
}).join('')
}
</ul>`
doc.innerHTML = teplhtml
</script>
3.2 字符串新增
字符串查找:
str.indexOf(要查找的东西) 返回索引(位置),没找到返回-1
str.includes(要查找的东西) 返回true / false
使用场景:判断浏览器includes
字符串以谁开头:
str.startsWith(检测东西)
使用场景:检测地址
字符串以谁结尾:
str.endsWith(检测东西)
使用场景:.png ........
重复字符串:
str.repeat(次数);
4. 三个点...
(1)扩展运算符 [1,2,3] ...[1,2,3] 1,2,3
(2)重置功能 a,b,c ...a [a,b,c]
(3)配合函数使用,做剩余参数
(4)用于变量的解构
5. 函数变化相关
5.1 函数默认参数
<script>
function show(a='欢迎',b='mmr'){
console.log(a,b)
}
show('welconme');
</script>
<script>
function show({x=0,y=0}={}){
console.log(x,y)
}
show();
</script>
注意:函数参数默认已经定义了,不能再使用let const 声明
function show(a=18){
let a = 12 //这是错的
console.log(a)
}
show()
5.2 剩余参数(...)
顾名思义,只能是最后一个参数,在中间或者开头是不能使用的。
解决了arguments的问题,arguments是一个伪数组,剩余参数会转成真正的数组。
show(1,2,3,4,5)
function show(...a){// 所有参数都保存在a中,以数组的形式
console.log(a)
}
function sum(...nums){
return nums.reduce((prev,curr)=> prev + curr,0)
}
console.log(sum(1,2,3,4,5))
5.3 箭头函数(=>)
特征:简洁明了,隐式返回,匿名函数
3.1 简洁明了,不需要再写function,换成=>,没有参数加(),多个参数在括号里用逗号分隔,一个参数时可以不使用括号。
3.2 隐式返回,去掉return
3.3 匿名函数
如果要在递归等循环中使用箭头函数时,通常是将它赋值给一个变量来使用
3.4 箭头函数的this指向
如上图所示,当一个函数独立运行的时候,函数的this应该指向window、或者global、或者严格意义上应该是undefined,在上图中,第一个this指向Jelly,函数内部的this指向window。
箭头函数没有自己的this值,它的this值是继承它的父作用域的,因为this值是在运行时候动态指定的,谁调用它,this就指向谁。箭头函数的this值是词法作用域,也就是说是在定义的时候就被指定了的,以后也不会随着它调用方法的改变而改变。
但是在有些时候是不适合使用箭头函数的,如下:
a.作为构造函数,一个方法需要绑定到一个对象
b.当你真的需要this的时候
c.需要使用arguments对象
6.数组热闹的循环
(0).arr.forEach() ,for...in , for...of
arr.forEach()代替普通for循环,简化代码,但是forEach的缺点是不能终止或者跳过;
for...in会将原型上的属性都遍历出来;
for...of可以终止或者跳过,只遍历数组的值,可以用来遍历可迭代对象
arr.forEach(function(val,index,arr){
console.log(val,index,arr)
})
arr.forEach(item => {
console.log(item)
})
const fruits = ["apple","banana","orange","mango"]
for(let fruit of fruits){
console.log(fruit) // apple banana orange mango
}
const fruits = ["apple","banana","orange","mango"]
for(let fruit of fruits.entries()){
console.log(fruit) // [0, 'apple'] [1, 'banana'] [2, 'orange'] [3, 'mango']
}
(1).arr.map()
非常有用,做数据交互 “映射”
正常情况下,需要配合return ,返回的是一个新的数组
若是没有return,相当于forEach
let arr= [
{title:"aaa",read:100,hot:true},
{title:"bbb",read:100,hot:true},
{title:"ccc",read:100,hot:false},
{title:"ddd",read:100,hot:true}
]
let newArr = arr.map((itemVal,index,arr)=>{
let json = {}
json.t = `^_^${itemVal.title}hhhhhh`
json.r = itemVal.read
json.h = itemVal.hot == true && '真棒!!!'
return json;
})
console.log(newArr)
效果:
let arr= [
{title:"aaa",read:100,hot:true},
{title:"bbb",read:100,hot:true},
{title:"ccc",read:100,hot:false},
{title:"ddd",read:100,hot:true}
]
let newArr = arr.map((itemVal,index,arr)=>{
let json = {}
json.t = `^_^${itemVal.title}hhhhhh`
json.r = itemVal.read
json.h = itemVal.hot == true || '非热点'
return json;
})
console.log(newArr)
(2).arr.filter()
过滤,过滤一些不合格“元素”,如果回调函数返回true,就留下来
let arr= [
{title:"aaa",read:100,hot:true},
{title:"bbb",read:100,hot:true},
{title:"ccc",read:100,hot:false},
{title:"ddd",read:100,hot:true}
]
let newArr = arr.filter((itemVal,index,arr)=>{
if(itemVal.hot){
return itemVal.hot
}
})
console.log(newArr)
(3).arr.some()
类似查找,数组里面某一个元素符合条件,返回true
(4).arr.every()
数组里面所有元素符合条件,返回true
重要:以上五个,所接收的的参数是相同的,都是当前元素val,当前下标index,数组本身arr;
其实他们都可以接收两个参数,一个是循环回调函数,一个是this指向谁。
而下面两个函数所接收的参数是:当前元素的前一个元素prev,当前元素cur,当前元素下index,
数组本身arr。
(5).arr.reduce()
求数组的和,阶乘,从左往右
let arr=[3,2,3]
let res = arr.reduce((pre,cur,index,arr)=>{
return Math.pow(cur,pre);// ES2017 新增幂运算符 ** return cur ** pre
//return cur + pre ///8
})
console.log(res) //6561
(6).arr.reduceRight()
求数组的和,阶乘,从右往左
7.ES6数组新增
for...of for(let val of arr)
arr.entries() 数组某一项
arr.keys() 数组下标
arr.find() 查找,找出第一个符合条件的数组成员,如果没有找到,返回undefined
arr.findIndex() 查找,找出第一个符合条件的数组成员的下标,如果没有找到,返回-1
arr.fill() 填充,语法:arr.fill(填充的东西,开始位置,结束位置)
Array.from() 把类数组(获取一组元素、arguments...)对象转成数组
*********具备length这个东西就靠谱
Array.of() 把一组值转成数组
在ES2016里面新增:arr.indexOf() arr.includes() str.includes()
8.对象简洁语法以及对象新增
(1)对象简洁语法
let name = 'ygx'
let age = 28
let json = {
name, //相当于name:name
age, //相当于age:age
showName(){
return this.name
},
showAge(){ //这里的函数注意一定不要使用箭头函数
return this.age
}
}
console.log(json.name,json.showName())
(2)新增方法
Object.is() 用来比较两个值是否相等
console.log(Object.is('a','a')) //true
console.log(NaN === NaN) //false
console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false
Object.assign() 用来合并对象
语法: let 新的对象 = Object.assign(目标对象,source1(源对象1),source2.....)
用途:1.复制一个对象
let arr= ['aaa','bbbb','ccc']
let newArr = Object.assign([],arr);
newArr.push('ddd');
console.log(newArr)
2.合并参数,有相同时,后面的覆盖前面的,例如:
let json1 = {
a:1
}
let json2 = {
b:2,
c:22
}
let json3 = {
a:12,
d:333
}
let newJson = Object.assign({},json1,json2,json3);
console.log(newJson)
ES2017引入:Object.keys()
Object.entries()
Object.values()
let {keys,values,entries} = Object
let json = {
a:1212,
b:223424,
c:'ffff'
}
for(let key of keys(json)){
console.log(key)
}
for(let item of values(json)){
console.log(item)
}
for(let entry of entries(json)){
console.log(entry)
}
for(let [key,val] of entries(json)){
console.log(key,val)
}
9.Promise
作用:解决异步回调问题
解决问题传统方式:大部分用回调函数、事件驱动的方式
例如:
ajax(url,()=>{
ajax(url,()=>{......
})
}) 这样就会一层一层,很麻烦。
Promise语法:
let promise = new Promise(function (resolve,reject) {
//resolve 成功调用
//reject 失败调用
})
promise.then(res=>{
console.log("成功信息")
},err=>{
console.log("失败信息")//可以不写,直接将catch点在后面
})
promise.catch(err=>{
console.log("失败信息") //相当于上面的err
})
Promise.resolve('aa'),将aa转换成一个promise对象,而且是resolve状态,即成功状态
等价于:new Promise(resolve=>{
resolve('aa');
})
Promise.reject('bb'),将bb转换成一个promise对象,而且是reject状态,即失败状态
等价于:new Promise((resolve,reject)=>{
reject('bb');
})
Promise.all([p1,p2,p3,p4]),把promise对象打包,扔到一个数组里,打包完还是一个promise对象,此时必须确保每一个promise对象都是resolve状态。
let p1 = Promise.resolve("aaa");
let p2 = Promise.resolve("bbb");
let p3 = Promise.resolve("ccc");
Promise.all([p1,p2,p3]).then(res=>{
console.log(res)
let [res1,res2,res3]=res
console.log(res1,res2,res3)
})
举个例子,模拟用户登录
用户登录 -> 获取用户信息
let status = 200
let userLogin = (resolve,reject) => {
//用定时器来模拟用户登陆接口
setTimeout(()=>{
if(status == 200){
resolve({
data:"用户登陆成功返回信息",
msg:"用户登陆成功",
token:"aaaaaaaaaaaaaaaaaa"
})
}else{
reject("用户登陆失败")
}
},2000)
}
let getUserInfo = (resolve,reject)=>{
用定时器来模拟获取用户信息接口
setTimeout(()=>{
if(status == 200){
resolve({
data:"用户信息",
msg:"获取用户信息成功",
token:"aaaaaaaaaaaaaaaaaa"
})
}else{
reject("获取用户信息失败")
}
},1000)
}
new Promise(userLogin).then(res=>{
console.log('用户登陆成功');
return new Promise(getUserInfo);
}).then(res=>{
console.log('获取用户信息成功');
console.log(res);
})
10.模块化
注意:模块化的使用需要放到服务器环境
(1)如何定义模块
export ......
export const a=12
export {
a as aaa,
b as bbb
}
(2)如何使用 import
import './modules/aaa.js'
import {a as aa,b as bb} from './modules/aa.js'
import * as mode from './modules/aa.js'
使用模块:<script type="modules"></script>
(3)import 特点
a.import可以是相对路径,也可以是绝对路径
b.import模块只会导入一次,无论你引入多少次
c.import './modules/aaa.js',如果这么用,相当于引入js文件
d.有提升效果,import会自动提升到顶部,首先执行
e.导出去模块内容,如果里面有定时器更改,外面也会改动,不像Common规范缓存
(4)import ()
类似node里面的require,可以动态引入,默认import语法不能写到if之类里面
返回值是个promise对象
例如:import("./modules/aa.js").then(res=>{
console.log(res.a + res.b)
})
优点:a.按需加载
b.可以写if中
c.路径也可以动态
关于import时,有{}与没有{}的区别:
只要是export default 的都不需要加{},其他直接export的都需要加{}
例如:
//modules/a.jsa.js
export default 12;
export const c = 12;
export const dd = 5;
//引入时
import a,{c,dd} from "./modules/a.js"
//import()按需引入
let sign = 1
function config(){
switch(sign){
case 1:
return "./modules/a.js"
break;
case 2:
return "./modules/b.js"
break;
}
}
import(config(1)).then(res=>{
........
})
ES2017加 async await
ES6默认执行严格模式 ‘use strict’
11.类与继承
12.Symbol & generator
数据类型:number、string、boolean、Object、undefined、function
ES6后加:symbol
定义: let syml = Symbol('aaa');
注意:
Symbol不能new。
Symbol()返回是一个唯一值,可以做一个key,定义一些唯一的或者私有的东西。
symbol是一个单独的数据类型,就叫symbol,属于基本类型。
如果symbol作为key,用for in 循环是出不来东西的
symbol最大的用处就是保证对象的属性不被更改(可以在局部修改,但是全局仍然是最初定义的值。)
例如:
let name = Symbol();
{
var person = {};
person.name = "111";
console.log(person.name)
}
{
let name = Symbol();
person.name = "2222"
console.log(person.name)
}
console.log(person.name)
let name = Symbol();
{
var person = {};
person[name] = "111";
console.log(person[name])
}
{
let name = Symbol();
person[name] = "2222"
console.log(person[name])
}
console.log(person[name])
此处疑问,点方法与中括号的区别(以下来自于百度):
所以,因为name是变量,所以应该用[],第二个示例才是正确的。
generator函数:解决异步、深度嵌套问题,async
语法:
function * show(){
yiled
}
<script>
//定义
function* gen() {
yield 'welcome',
yield 'to',
yield "mumaren"
return "zhixingwanle"
}
//调用
let g1 = gen();
console.log(g1);
console.log(g1.next());
console.log(g1.next());
console.log(g1.next());
console.log(g1.next());
</script>
上述调用是手动调用,可以使用for...of遍历,但是需要注意的是:return 的东西不会遍历;也可以通过解构赋值、配合扩展运算符和Array.from()使用。
如:
<script>
//定义
function* gen() {
yield 'welcome',
yield 'to',
yield "mumaren"
return "zhixingwanle"
}
//调用
let g1 = gen();
for (const value of g1) {
console.log(value)
}
</script>
<script>
//定义
function* gen() {
yield 'welcome',
yield 'to',
yield "mumaren"
return "zhixingwanle"
}
//调用
let g1 = gen();
let [a,b,c] = g1;//解构
console.log(a,b,c)
</script>
通过axios举例:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
//定义
function * gen() {
let name = yield 'aaa';
yield axios.get(`https://api.github.com/users/${name}`);
}
//调用
let g1 = gen();
let userName = g1.next().value;
g1.next(userName).value.then(res=>{
console.log(res.data);
})
</script>
异步同步问题:
异步:不连续,上一个操作没有执行完,下一个操作照样开始
同步:连续执行,上一个操作没有执行完,下一个没法开始
关于异步,解决方案:
1.回调函数
2.事件监听
3.发布/订阅
4.Promise对象
13.async、await
使用node.js读取文件 fs.readFile()
使用三种方式,(1)Promise
const fs = require('fs');
const readFile = function(filename) {
return new Promise((resolve,reject)=>{
fs.readFile(filename,(err,data)=>{
if(err) reject(err);
resolve(data);
});
});
}
readFile("a.txt").then(res=>{
console.log(res.toString())
return readFile("b.txt");
}).then(res=>{
console.log(res.toString())
return readFile("c.txt");
}).then(res=>{
console.log(res.toString())
})
(2)generator
const fs = require('fs');
const readFile = function(filename) {
return new Promise((resolve,reject)=>{
fs.readFile(filename,(err,data)=>{
if(err) reject(err);
resolve(data);
});
});
}
//genenator的方式
function * gen() {
yield readFile('a.txt');
yield readFile('b.txt');
yield readFile('c.txt');
}
let g1 = gen();
g1.next().value.then(res=>{
console.log(res.toString());
return g1.next().value;
}).then(res=>{
console.log(res.toString());
return g1.next().value;
}).then(res=>{
console.log(res.toString());
})
(3)async
const fs = require('fs');
const readFile = function(filename) {
return new Promise((resolve,reject)=>{
fs.readFile(filename,(err,data)=>{
if(err) reject(err);
resolve(data);
});
});
}
//async的方式
async function fn() {
let f1 = await readFile('a.txt');
console.log(f1.toString());
let f2 = await readFile('b.txt');
console.log(f2.toString());
let f3 = await readFile('c.txt');
console.log(f3.toString());
}
fn();
async的特点:
a.await只能放在async 函数中
b.相比于generator语义化更强
c.await后面可以是promise对象,也可以是数字、字符串、布尔值
d.asyns函数返回的是一个promise对象
e.只要await语句后面promise的状态变成reject,那么整个async函数就会中断执行。
14.set、WeakSel
set是ES6新添加的一个数据结构
set用法:
let setArr = new Set(['a','b']);
setArr.add('c'); //往setArr里面添加一项
setArr.delete('b'); //删除一项
setArr.clear(); //清空
setArr.size 个数
setArr.has('a'); //判断setArr里面有没有此项
注意点:new Set([]) 存储数组,不重复
new WeakSet({}) 存储json,不靠谱,没有size,也没有clear
15.Map和WeakMap
(1)Map
类似于json,但是json的key只能是字符串,但是Map的key可以是任何类型。
使用:
let map = new Map();
map.set(key,value); //设置一个值
map.get(key); //获取一个值
map.delete(key) //删除指定元素
map.has(key) //判断有没有
map.clear(); //清空
循环:
for(let [key,value] of map){}
for(let key of map.keys()){}
for(let value of map.values()){}
for(let [k,v] of map.entries()){}
map.forEach((value,key)=>{
console.log(value,key);
})
(2)WeakMap
WeakMap,它的key只能是对象
16.数字变化及Math新增的东西
(1)二进制:0b
(2)八进制:0o
(3)十六进制 :#ccc
对于数值的转换,之前:Number()、parseInt()、....
现在,都给归到了Nmuber
如:Number.isNaN(12) =====> false
Number.isNaN(NaN) =====> true
Number.isNaN()
方法确定传递的值是否为 NaN,并且检查其类型是否为 Number。它是原来的全局 isNaN() 的更稳妥的版本。
Number.isFinite()
方法用来检测传入的参数是否是一个有穷数。
console.log(Number.isFinite(1 / 0));
// expected output: false
console.log(Number.isFinite(10 / 5));
// expected output: true
console.log(Number.isFinite(0 / 0));
// expected output: false
Number.isInteger()
方法用来判断给定的参数是否为整数。
Number.isSafeInteger()
方法用来判断传入的参数值是否是一个“安全整数”(safe integer)。
安全整数范围为 -(253 - 1)到
253 - 1
之间的整数,包含 -(253 - 1)和
253 - 1
。
Number.parseFloat()
方法可以把一个字符串解析成浮点数。该方法与全局的 parseFloat() 函数相同,并且处于 ECMAScript 6 规范中(用于全局变量的模块化)。
Number.parseInt()
方法依据指定基数 [ 参数 radix 的值],把字符串 [ 参数 string 的值] 解析成整数。
Math.trunc()
方法会将数字的小数部分去掉,只保留整数部分。
Math.trunc(13.37) // 13
Math.trunc(42.84) // 42
Math.trunc(0.123) // 0
Math.trunc(-0.123) // -0
Math.trunc("-1.123") // -1
Math.trunc(NaN) // NaN
Math.trunc("foo") // NaN
Math.trunc() // NaN
Math.sign()
函数返回一个数字的符号, 指示数字是正数,负数还是零。
Math.sign(3); // 1
Math.sign(-3); // -1
Math.sign("-3"); // -1
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(NaN); // NaN
Math.sign("foo"); // NaN
Math.sign(); // NaN
Math.cbrt()
函数返回任意数字的立方根.
Math.cbrt(NaN); // NaN
Math.cbrt(-1); // -1
Math.cbrt(-0); // -0
Math.cbrt(-Infinity); // -Infinity
Math.cbrt(0); // 0
Math.cbrt(1); // 1
Math.cbrt(Infinity); // Infinity
Math.cbrt(null); // 0
Math.cbrt(2); // 1.2599210498948734
17.ES2018(ES9)新增
(1)命名捕获(语法:?<名称>)
let aaa= "2021-11-05";
let reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; //注意是\d
let {year,month,day} = aaa.match(reg).groups;
console.log(aaa.match(reg))
console.log(year,month,day)
反向引用命名捕获(语法:\k<名称>)
18.Proxy的使用(代理)
扩展(增强)对象的一些功能
语法:new Proxy(target,handler);
let obj = new Proxy(被代理的对象,对代理对象做什么操作);
let user = new Proxy({},{
get:function (obj,property) {
switch (property) {
case "fullname":
return obj.fname +"" + obj.lname;
break;
default:
return "ddddd";
break;
}
}
});
user.fname = "yang"
user.lname = "gx"
console.log(user.fullname); //yanggx