01【let和const】
1.let
1.1块级作用域
什么是块级作用域?
声明的变量只在代码块{}中有效
外部无法访问到这个代码块中的变量。
与var区别:
var是全局作用域
经典案例:点击每一个li输出索引值
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
let lis = document.querySelectorAll("ul li");
for(var i=0;i<lis.length;i++){
lis[i].addEventListener('click',function (){
console.log(i); //都是3
})
}
</script>
</body>
此代码的执行思路是,先执行for循环,执行完后i为3,因为点击事件属于异步操作,所以for循环执行后再执行点击事件,又由于var声明的变量是全局变量,相当于整个执行过程中的i只有一个,此时点击任何一个li输出的i都为3。
此时改用let声明:
let lis = document.querySelectorAll("ul li");
for(let i=0;i<lis.length;i++){
lis[i].addEventListener('click',function (){
console.log(i);
})
}
由于let声明的变量只在代码块中执行,所以以上代码中,每执行一次for循环,就会产生一个新的变量i,而每一个变量i都没有被释放掉,相当于此时会产生3个i,并且for循环相当于一个父作用域,点击事件相当于一个子作用域,每点击一次,程序就会去寻找父级中的i并输出来。
因此用let解决此类问题就更加方便。
1.2不允许重复声明
var 可以重复声明,后声明的会覆盖先声明的。
var a =1;
var a =2;
console.log(a); //2
let不允许重复声明
let a =1;
let a =2;
console.log(a); //报错Uncaught SyntaxError: Identifier 'a' has already been declared
1.3没有变量提升
var有变量提升
console.log(num); //undefined
var num =10;
let 没有变量提升
let a =10;
function test(){
console.log(a); //报错Uncaught ReferenceError: Cannot access 'a' before initialization
let a =10;
}
test();
1.4不与顶层对象挂钩
let a = 10;
console.log(window.a); //undefined
而var定义的变量是全局变量,可通过window来查找
var a = 10;
console.log(window.a); //10
2.const(与let相似)
1.1常量必须初始化,并且不允许更改
1.2不能重复定义
1.3块级作用域
1.4没有变量提升
1.5不与顶层对象挂钩
1.6用const修饰的变量一定不能被改吗?
const obj = {
name:"张三",
age:"18"
}
obj = "fgjs" //这样会报错,因为常量不允许被修改
obj.name = "李四";
console.log(obj); //此时obj中的name被修改了,因为对象是复杂数据类型,存的是地址
//如果想让对象不被更改,可使用Object.freeze()方法
const obj = Object.freeze({
name:"张三",
age:"18"
})
obj.name = "王五";
console.log(obj); //不报错,但此时obj里的name还是‘张三’
02【解构赋值】
从数组和对象中提取值,对变量进行赋值,这被称为解构。
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
如果解构不成功,变量的值就等于undefined。
let [x, y] = ['a'];
x // "a"
y // undefined
案例1:快速交换两个变量的值,不借助第三个变量
let x = 1;
let y = 2;
[y,x]=[x,y];
console.log(x,y);
1.默认值的基本用法**
const [a, b] = [];
console.log(a, b); // undefined undefined
// ---------------------------------------
const [a = 1, b = 2] = [];
console.log(a, b); // 1 2
当后端返回的值是空的,又不希望这个变量是undefined,就可以给它一个默认值来解决这个问题
let [x=1]=[];
console.log(x); //1
2.数组解构赋值
3.对象解构赋值
对象的解构和数组基本类似,对象解构的变量是在 {}中定义的。
var obj = {
name: 'imooc', age: 7
};
var { name, age } = obj; // name: imooc, age: 7
3.1重命名属性
在对象解构出来的变量不是我们想要的变量命名,这时我们需要对它进行重命名。
var {a:x = 8, b:y = 3} = {a: 2};
console.log(x); // 2
console.log(y); // 3
3.2获取对象中嵌套的值
let code = "AAAA"
let res = {
code:200,
data:{
list:["aaa","bbb","ccc"]
}
}
let {data:{list:[x,y,z]},code:co,err="没有错误"} = res;
console.log(x,co,err)
4.函数传参时解构赋值
function getData(){
let res = {
code:100,
data:{
list:["a","b"]
}
}
test(res)
}
function test({code,data:{list:[x]}}){
console.log(code,x); //100 'a'
}
getData();
5.对于字符串的解构赋值
let a = "sdaffa";
let [x,y] = a;
console.log(x,y);
let {length} = a;
console.log(length);
//如果名字冲突,也可给他重命名
// let {length:len} = a;
// console.log(length);
03【模板字符串】
解决代码换行的美观性和方便性
let name ="xiaomei";
// let oli = "<li>\
// <b>"+name+"</b>\
// </li>"
let oli =`<li>
<b>${name}</b>
</li>`
console.log(oli);
案例:
let arr =["tiechui", "kerwin" ,"gangdan"];
let newlist = arr.map(function(item,index){
return `<li>
<b>${item}</b>
</li>`
// //${} 括号里不仅可放变量,还可放表达式,函数等
// return `<li class="${index===0?'active':' '}">
// <b>${item}</b>
// </li>`
})
console. log(newlist);
let oul = document . querySelector("ul" );
oul.innerHTML = newlist. join("");
04【字符串扩展】
1.include函数
判断字符串中是否存在指定字符
let a = "dsgh";
console.log(a.includes("g")) //true
console.log(a.startsWith("d")) //判断以字母开头 true
console.log(a.endsWith("gh")) //判断以字母结尾 true
console.log(a.includes("g",1)) //判断索引从1后开始查找 false
console.log(a.startsWith("s",1)) //true
console.log(a.endsWith("g",3)) //true
2.repeat函数
repeat()方法返回一个新字符串,表示将原字符串重复n次。
let myname = "kerwin"
console.log(myname.repeat(3)) //kerwinkerwinkerwin
console.log(myname.repeat(0)) //""
console.log(myname.repeat(3.5)) //kerwinkerwinkerwin
console.log(myname.repeat("3"))//kerwinkerwinkerwin
05【数值扩展】
1.二进制和八进制表示法
let count1 = 100
let count2 = 0x100
let count3 = 0o100
let count4 = 0b100
2.isFinite与isNaN方法
减少全局性方法,使得语言逐步模块化
let num1 = Number.isFinite(100) //true
let num2 = Number.isFinite(100/0) //false
let num3 = Number.isFinite(Infinity) // false
let num4 = Number.isFinite("100") //false
let num1 = Number.isNaN(100) // false
let num2 = Number.isNaN(NaN) //true
let num3 = Number.isNaN("kerwin") //false
let num4 = Number.isNaN("100") // false
它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。
3.isInteger方法
用来判断一个数值是否为整数。
let num1 = Number.isInteger(100) // true
let num2 = Number.isInteger(100.0) //true
let num3 = Number.isInteger("kerwin") //false
let num4 = Number.isInteger("100") // false
4.极小常量Number.EPSILON
它表示1与大于1的最小浮点数之间的差。2.220446049250313e-16
function isEqual(a,b){
return Math.abs(a-b)<Number.EPSILON
}
console.log(isEqual(0.1+0.2,0.3)) //true
console.log(0.1+0.2===0.3) //false
5.Math.trunc
将小数部分抹掉,返回一个整数
console.log(Math.trunc(1.2)) //1
console.log(Math.trunc(1.8))// 1
console.log(Math.trunc(-1.8)) //-1
console.log(Math.trunc(-1.2))//-1
6.Math.sign
Math. sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
Math.sign(-100) // -1
Math.sign(100) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign("kerwin") // NaN
06【数组扩展】
1.扩展运算符
(1)浅复制
let arr = [1,2,3];
let arr2 = [...arr];
arr2.pop(); //删除数组最后一个,不会影响arr
console.log(arr,arr2);
(2)快速合并
let arr = [1,2,3];
let arr2 = [4,5,6];
console.log(...arr,...arr2);
(3)和解构赋值一起使用
let arr = [1,2,3];
let arr2 = [1,2,3,4,5,6];
let [a,b,...c] = arr2;
console.log(a,b,c);
2.Array.from
将类数组对象转换为真正数组
function test(){
console.log(Array.from(arguments))
}
test(1,2,3)
let oli = document.querySelectorAll("li")
console.log(Array.from(oli))
3.Array.of
将一组值转化为数组,即新建数组
let arr =Array(1,2); //此时输出数组[1,2]
let arr1 = Array(2); //只传一个时会认为是数组的长度,输出的是empty
let arr2 = Array.of(2); //将值转换为数组
console.log(arr,arr1,arr2);
4.find方法
1)该方法主要应用于查找第一个符合条件的数组元素
2)它的参数是一个回调函数。在回调函数中可以写你要查找元素的条件,当条件成立为true时,返回该元素,如果没有符合条件的元素,返回值为undefined
// find findIndex()
let arr = [11,12,13,14,15]
let res = arr.find(function(item){
return item>13
}) //14
let res = arr.findIndex(function(item){
return item>13
}) //3
//findLast findLastIndex() ES2022
let res = arr.findLast(function(item){
return item>13
}) //15
let res = arr.findLastIndex(function(item){
return item>13
}) //4
5.fill方法
使用自己想要的参数替换原数组内容,但是会改变原来的数组
let arr1 = new Array(3).fill("kerwin")
let arr2 = ['a', 'b', 'c'].fill("kerwin", 1, 2)
console.log(arr1)//['kerwin', 'kerwin', 'kerwin']
console.log(arr2)// ['a', 'kerwin', 'c']
6.flat与flatMap方法
按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回
简而言之就是扁平化数组,将二维变为一维
let arr = [1,2,3,[4,5,6]]
let arr1 = arr.flat();
console.log(arr,arr1);
let obj = [{
name: "A",
list: ["鞍山", "安庆", "安阳"]
},
{
name: "B",
list: ["北京", "保定", "包头"]
}
]
let res = obj.flatMap(function (item){
return item.list;
})
console.log(res);
07【对象扩展】
1.对象简写
let name ="moduleA"
let obj = {
name, //name:name
test1(){ }, //test1:function(){}
test2(){ }, //test2:function(){}
}
2.对象属性表达式
l0et name ="moduleA"
let obj = {
[name]:a, //moduleA:a
[name+"test1"](){ }, //moduleAtest1(){}
[name+"test2"](){ } //moduleAtest2(){}
}
3.扩展运算符... es2018
let obj1 ={
name:"a",
age:"18"
}
let obj2 = {
sex:"女",
age:"20"
}
console.log({...obj1,...obj2}); //obj2的age会覆盖obj1的age {name:"a",sex:"女",age:"20"}
4.Object.assign
Object.assign(target, object1,object2)的第一个参数是目标对象,后面可以跟一个或多个源对象作为参数。
target:参数合并后存放的对象
object1:参数1
object2:参数2
let obj1 = {
name: "kerwin"
};
let obj2 = {
name:"tiechui"
};
let obj3 = {
age:100
};
console.log(Object.assign(obj1, obj2, obj3)); // {name: 'tiechui', age: 100}
console.log(obj1); //{name: 'tiechui', age: 100}
console.log(obj2); //{name: 'tiechui'}
console.log(obj3); //{age: 100}
//最终结果会影响obj1,若不想影响obj1,定义一个空的obj
//obj = {}
//console.log(Object.assign(obj,obj1, obj2, obj3));
5.Object.is
判断两个值是否是相同的值
主要是解决NaN===NaN为false的判断
console.log(NaN==NaN) //false
console.log(NaN===NaN) //false
console.log(+0===-0) //true
console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false
08【函数的扩展】
1.参数默认值
function ajax(url,method="get",async=true){
console.log(url,method,async);
}
ajax("/aaa"); ///aaa get true
ajax("/aaa","post"); ///aaa post true
2.rest剩余参数
function test(...data){
console.log(data); // [1, 2, 3, 4, 5, 6]
}
test(1,2,3,4,5,6);
//name属性
console.log(test.name); //test
3.箭头函数
箭头函数是 ES6 里面一个简写函数的语法方式
重点: 箭头函数只能简写函数表达式,不能简写声明式函数
function fn() {} // 不能简写
const fun = function () {} // 可以简写
const obj = {
fn: function () {} // 可以简写
}
语法: (函数的行参) => { 函数体内要执行的代码 }
let fn = function () { }
// 可以使用箭头函数写成
let fn = () => { }
return语句的箭头函数(return前没有其他逻辑代码):
let fn = function () {
return "11"
}
// 可以使用箭头函数写成
let fn = () => "11"
如果return的是一个对象,需要注意:
let test2 = function(){
return {
name:"a",
age:18
}
}
//箭头函数写成
let test2 = ()=>({
name:"a",
age:18
}) //括号必须加
如果只有一个参数,可以省略掉参数括号,两个参数则必须要括号
let newlist = arr.map(function(item){
return `<li>
<b>${item}</b>
</li>`
})
// 可以使用箭头函数写成
let newlist = arr.map((item)=>`<li> <b>${item}</b></li>`)
let newlist = arr.map(item=>`<li> <b>${item}</b></li>`)
4.无法访问arguments,无法new
let test3 = ()=>{
console.log(arguments)
}
test3(1,2,3,4) //Uncaught ReferenceError: arguments is not defined
new test3() // test3 is not a constructor
5.箭头函数没有this
箭头函数内部没有 this,箭头函数的 this 指向父作用域
<body>
模糊搜索:
<input type="text" id="mysearch">
<script>
let osearch = document.querySelector("#mysearch")
osearch.oninput = function(){
// console.log(this.value)
// let _this = this
setTimeout(()=>{
console.log(this.value)
console.log(`发送${this.value}到后端,获取列表数据`)
},1000)
}
</script>
</body>
由于箭头函数没有this,他里面的this指向父作用域也就是指向了osearch的this,这就解决了异步操作时this的指向问题。
09【symbol】
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它属于 JavaScript 语言的原生数据类型之一,其他数据类型是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
1.基础知识
let s1 = Symbol() //生成了一个symbol类型数据
不能进行运算
console.log(s1>"aaaaa")
显示调用toString()
console.log(s1.toString() + "aaaa")
隐式转换boolean
if (s1) {
console.log("执行")
}
2.和对象属性表达式一起使用
let keys = {
name: Symbol("name"),
age: Symbol("age"),
location: Symbol("location"),
test: Symbol("test")
}
let obj = {
[keys.name]: "kerwin",
[keys.age]: 100,
[keys.location]: "dalian",
[keys.test]() {
console.log("test")
}
}
console.log(obj[keys.name])
// obj[keys.test]()
obj.name = "aaaaaaa"
console.log(obj)
防止别人修改自己的代码属性,symbol可接受传参,目的是为了将每个属性进行区分
3.不能进行for in遍历问题
for(let i in obj){
console.log(i)
} //只能打印出加的普通属性
console.log(Object.getOwnPropertySymbols(obj))//打印symbol属性,不能打印普通属性
既要打印出普通属性又要打印出symbol属性用Reflect.ownKeys()方法:
Reflect.ownKeys(obj).forEach(item=>{
console.log(item,obj[item])
})
4.作为常量
统一代码的一致性
const VIDEO = Symbol();
const AUDIO = Symbol();
const IMAGE = Symbol();
function play(type){
switch(type){
case VIDEO:
console.log("视频播放")
break;
case AUDIO:
console.log("音频播放")
break;
case IMAGE:
console.log("图片播放")
break;
}
}
play(IMAGE)
10【Iterator迭代器】
Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of循环
let arr = ["kerwin", "tiechui", "gangdaner"]
for(let i of arr){
console.log(i)
}
Iterator 的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
let i = arr[Symbol.iterator]()
console.log(i.next())
console.log(i.next())
console.log(i.next())
console.log(i.next())
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。
也就是说只要添加了Symbol.iterator属性,就可以进行for of遍历。
原生默认具备 Iterator 接口的数据结构如下。
Array
Set
Map
String
arguments 对象
NodeList 对象
如果满足iterator迭代器接口的,就可以用扩展运算符(...)。
案例:
封装了一个obj2,当里面的属性不可访问时,遍历里面的属性
这时需要写一个迭代器,可遍历list属性
let obj2 = {
code: 200,
name: "obj2",
list: ["kerwin", "tiechui", "gangdan"],
//迭代器
[Symbol.iterator]() {
let index = 0
// console.log(this.list)
return {
next: () => {
// console.log(this)
return {
value: this.list[index++],
done: index === (this.list.length + 1) ? true : false
}
}
}
}
}
for (let i of obj2) {
console.log(i)
}
// let iter = obj2[Symbol.iterator]()
// console.log(iter)
// console.log(iter.next())
// console.log(iter.next())
// console.log(iter.next())
// console.log(iter.next())
//immutable.js
11【Set结构】
它类似于数组,但成员的值都是唯一的,没有重复的值。
存储值不一样的结构时首选Set
1. 初识Set
数组去重:
let s1 = new Set([1, 2, 3, 2, 3])
console.log(s1) //{1,2,3}
console.log([...s1]) //[1,2,3]
console.log(Array.from(s1)) //[1,2,3]
2.实例的属性和方法
size:返回Set实例的成员总数(set数组的长度相当于length)。
Set.prototype.add(value):添加某个value。
Set.prototype.delete(value):删除某个value,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear():清除所有成员,没有返回值。
3 遍历
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
set结构是没有索引的,set里面的键名和键值都是一样的,所以key()和values()遍历出来的值一样
let s2 = new Set([1, 2, 3])
for(let i of s2){
console.log(i) //1 2 3
}
for(let i of s2.keys()){
console.log(i) //1 2 3
}
for(let i of s2.values()){
console.log(i) //1 2 3
}
Set.prototype.entries():返回键值对的遍历器
let s2 = new Set([1, 2, 3])
for(let i of s2.entries()){
console.log(i) //[1,1] [2,2] [3,3]
}
//for of 搭配entries遍历数组
let arr = ["aa","bb","cc"]
for(let i of arr.entries()){
console.log(i) //[0, 'aa'] [1, 'bb'] [2, 'cc']
}
//for of 搭配entries以及解构赋值遍历数组
for(let [index,item] of arr.entries()){
console.log(index,item) //0 'aa' 1 'bb' 2 'cc'
}
Set.prototype.forEach():遍历每个成员
s2.forEach((item,index)=>{
console.log(item,index) //1 1 2 2 3 3
})
4 复杂数据结构去重案例
let list = [1,2,2,"kerwin","kerwin",[1,2],[3,4],[1,2],{name:"kerwin"},{age:100},{name:"kerwin"},undefined,undefined,NaN,NaN]
function uni(arr){
let res = new Set();
return arr.filter((item)=>{
//判断 has trun false
// 没有 return true
let id = JSON.stringify(item);
if(res.has(id)){
return false;
}else{
res.add(id);
return true;
}
})
}
console.log(uni(list));
JSON.stringify()方法介绍:
JSON.stringify 方法是将一个 JavaScript 对象或值转换为 JSON 字符串,默认该方法其实有三个参数:第一个参数是必选,后面两个是可选参数非必选。第一个参数传入的是要转换的对象;第二个是一个 replacer 函数,比如指定的 replacer 是数组,则可选择性地仅处理包含数组指定的属性;第三个参数用来控制结果字符串里面的间距,后面两个参数整体用得比较少。
//该方法的语法为:
JSON.stringify(value[, replacer [, space]])
具体参照:https://blog.csdn.net/CRMEB/article/details/118904748
12【Map结构】
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
存取键值对首选Map
1.初识Map
let m1 = new Map()
m1.set("name","kerwin")
m1.set({a:1},"大连")
console.log(m1)
let m2= new Map([
["name","kerwin"],
[{a:1},"大连"]
])
console.log(m2)
2 实例的属性和方法
size:返回 Map 结构的成员总数(length)。
Map.prototype.set(key,value):添加key对应得value,返回 Map 结构本身。
Map.prototype.get(key):获取key对应的value
Map.prototype.delete(key):删除某个键(键名+键值)
Map.prototype.has(key):某个键是否在当前 Map 对象之中。
Map.prototype.clear():清除所有成员,没有返回值。
3 遍历
Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 Map 的所有成员。
13【Proxy代理】
Proxy如其名, 它的作用是在对象和和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值, 以及实例化等等多种操作, 都会被拦截住, 经过这一层我们可以统一处理,我们可以认为它就是“代理器”
1.拦截数据什么时候被修改
<div id="box"></div>
<script>
let obj = {
data:"11",
}
obj.data = "22";
//怎样拦截到data什么时候被修改了?
let obj = {}
Object.defineProperty(obj,"data",{
get(){
console.log("get")
return box.innerHTML
},
set(value){
console.log("set",value)
//设置dom
box.innerHTML = value
}
})
console.log(obj)
//通过 Object.defineProperty()可以把obj的某一个属性进行一个get和set的拦截,只要访问这个属性,get方法就会调用,只要修改这个属性set方法就会调用
</script>
Object.defineProperty()拦截缺点:
每次只能拦截一个属性
只能拦截对象
2.proxy代理操作
通过操作proxy来控制操作的内容
优点:
可拦截多个属性
可拦截多种数据类型
<div id="box"></div>
<script>
let obj = {}
let proxy = new Proxy(obj,{
//get()访问属性
get(target,key){
console.log("get",target[key])
return target[key] //get必须有return值
},
//set()修改属性
set(target,key,value{
console.log("set",target,key,value)
if(key==="data"){
box.innerHTML = value
}
target[key] = value
},
//has()判断代理的对象中有没有某个属性,不常用
has(){
return false
}
})
3.this问题
let s = new Set()
let proxy = new Proxy(s,{
get(target,key){
//判断如果式方法,修正this指向
let value = target[key]
if(value instanceof Function){
//call apply bind
return value.bind(target)
}
return value
},
set(target,key,value){
target[key] = value
console.log("set")
}
})
call apply bind 改变this指向问题请参照:https://blog.csdn.net/m0_67880202/article/details/128719416?spm=1001.2014.3001.5502
14【Reflect对象】
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
Reflect 用于改变默认行为
1.代替Object的某些方法
const obj = {
};
Reflect.defineProperty(obj, 'name', {
value: 'kerwin',
writable: false,
configurable:false
});
2.修改某些Object方法返回结果
// 老写法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// fail
}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// fail
}
//Reflect好处:在处理异常错误的时候,不用try catch ,可直接通过判断true false ,保证代码不被打断
3.命令式变为函数行为
const obj = {
name:"kerwin"
};
//老写法
console.log("name" in obj) //true
//新写法
console.log(Reflect.has(obj, 'name')) //true
//老写法
delete obj.name
//新写法
Reflect.deleteProperty(obj, "name")
4.配合Proxy
let obj = {
name:"kerwin"
}
//set
Reflect.set(obj,"age",100)
//get
console.log(Reflect.get(obj,"name"))
let s = new Set()
let proxy = new Proxy(s, {
get(target, key) {
//判断如果式方法,修正this指向
//target[key]
let value = Reflect.get(target,key)
if (value instanceof Function) {
//call apply bind
return value.bind(target)
}
return value
},
//用reflect修改本身的默认行为
set(target, key, value) {
Reflect.set(...arguments)
}
})
//代理数组
let arr = [1,2,3]
let proxy = new Proxy(arr,{
get(target,key){
console.log("get",key)
return Reflect.get(...arguments)
},
set(target,key,value){
console.log("set",key,value)
return Reflect.set(...arguments)
}
})
15【Promise】
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数, 更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象 。
指定回调函数方式更灵活易懂。
解决异步 回调地狱 的问题。
1.回调地狱
当一个回调函数嵌套一个回调函数的时候
就会出现一个嵌套结构
当嵌套的多了就会出现回调地狱的情况
比如我们发送三个 ajax 请求
第一个正常发送
第二个请求需要第一个请求的结果中的某一个值作为参数
第三个请求需要第二个请求的结果中的某一个值作为参数
ajax({
url: '我是第一个请求',
success (res) {
// 现在发送第二个请求
ajax({
url: '我是第二个请求',
data: { a: res.a, b: res.b },
success (res2) {
// 进行第三个请求
ajax({
url: '我是第三个请求',
data: { a: res2.a, b: res2.b },
success (res3) {
console.log(res3)
}
})
}
})
}
})
回调地狱,其实就是回调函数嵌套过多导致的
当代码成为这个结构以后,已经没有维护的可能了
2.Promise使用
语法:
new Promise(function (resolve, reject) {
// resolve 表示成功的回调
// reject 表示失败的回调
}).then(function (res) {
// 成功的函数
}).catch(function (err) {
// 失败的函数
})
let p = new Promise(function(resolve,reject){})
//then接收两个回调
p.then(function(){
//成功
},function(){
//失败
})
//then接收一个回调
p.then(function(){
//成功
}).catch(function(){
//失败
})
3.Promise 对象的状态
Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。
异步操作未完成(pending)
异步操作成功(fulfilled)
异步操作失败(rejected)
这三种的状态的变化途径只有两种。
从“未完成”到“成功”
从“未完成”到“失败”
一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。
因此,Promise 的最终结果只有两种。
异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。
4.封装简单ajax验证promise
function ajax(url){
return new Promise((resolve,reject)=>{
let xhr = new XMLHttpRequest()
xhr.open("get",url,true)
xhr.send()
xhr.onreadystatechange = function(){
if(xhr.readyState===4){
if(xhr.status>=200&&xhr.status<300){
resolve(JSON.parse(xhr.responseText))
}else{
reject(xhr.responseText)
}
}
}
})
}
ajax("1.json").then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
5.回调地域解决方案
let p = new Promise(function(resolve,reject){})
//then接收一个回调
`p.then(function(){
//p.then()返回的是一个promise对象,所以后面可再接then()
//如果return 非promise类型,pending-fulfilled
//如果return promise类型,根据这个新的promise对象的结果,决定pending-fulfilled pending-rejected
})`.then(function(){
})
6.promise对象方法
Promise 是一个对象,也是一个构造函数。
1.Promise.resolve
将现有对象转为 Promise 对象
Promise.resolve('kerwin')
// 等价于
new Promise(resolve => resolve('kerwin'))
2.Promise.reject
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
const p = Promise.reject('error');
// 等同于
const p = new Promise((resolve, reject) => reject('error'))
3.promise.all(es6)
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
p的状态由p1,p2,p3 决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
4.Promise.race(es6)
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
5.Promise.allSettled
Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。
const promises = [ ajax('/200接口'), ajax('/401接口') ];
Promise.allSettled(promises).then(results=>{
// 过滤出成功的请求
results.filter(item =>item.status === 'fulfilled');
过滤出失败的请求
results.filter(item=> item.status === 'rejected');
})
6.Promise.any
只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。
16【Generator函数】
Generator 函数是 ES6 提供的一种异步编程解决方案
Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
1.基本语法
function *gen(){
console.log(1)
yield;//'产出' 遇到yield暂停
console.log(2)
yield;
console.log(3)
}
let g = gen()
g.next()
g.next()
g.next()
yield(产出)表达式是暂停执行的标记,而next方法可以恢复执行。
function* gen() {
// console.log(11)
yield "aaa"//产出
// console.log(22)
yield "bbb"
// console.log(33)
return "ccc"
}
let g = gen()
let res1 = g.next()
console.log(res1) //{value: 'aaa', done: false}
let res2 = g.next()
console.log(res2) //{value: 'bbb', done: false}
let res3 = g.next()
console.log(res3) //{value: 'ccc', done: true}
//可用for of 遍历产出值
// for(let i of g){
// console.log(i)
// }
2.next()传参
为何第一个参数没有作用,详解见代码
function* gen() {
//②遇到yield代码就暂停了,此时还未进行赋值操作
let res1 = yield "aaa"
console.log("gen内部", res1) //输出为:gen内部 传入-22222
let res2 = yield "bbb"
console.log("gen内部", res2) //输出为:gen内部 传入-33333
}
let g = gen()
//①代码执行g.next("传入-11111")
let res1 = g.next("传入-11111")
//③当再一次执行next进行赋值操作,将next中的参数 "传入-22222" 赋值给 res1,所以传入的第一个参数 "传入-11111" 没有作用
let res2 = g.next("传入-22222")
let res3 = g.next("传入-33333")
3.异步
function ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open("get", url, true)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText))
} else {
reject(xhr.responseText)
}
}
}
})
}
手动版本
function *gen(){
let res1 = yield ajax("1.json")
console.log(res1)
let res2 = yield ajax("2.json")
console.log(res2)
}
let g = gen()
//g.next().value是一个promise对象
g.next().value.then(data=>{
//将data赋值给 res1
g.next(data).value.then(res=>{
//将data赋值给 res2
g.next(res)
})
})
自动版本
function *gen(){
let res1 = yield ajax("1.json")
console.log(res1)
let res2 = yield ajax("2.json")
console.log(res2)
}
//自动版本(递归),前提是yield后必须接的是promise对象
function AutoRun(gen) {
let g = gen();
function next(data) {
let res = g.next(data);
if (res.done) return
res.value.then(function (data) {
next(data);
});
}
next();
}
AutoRun(gen)
17【Class语法】
1.类的写法
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name,this.age)
}
}
let obj = new Person("kerwin",100)
console.log(obj)
2.结合属性表达式
let s = Symbol('say')
class Person {
constructor(name,age){
this.name = name
this.age =age
}
[s](){
console.log(this.name,this.age)
}
}
let obj = new Person("kerwin",100)
console.log(obj.__proto__===Person.prototype)
obj[s]()
3.getter与setter
操作dom
<body>
<ul id="list"> </ul>
<script>
class Person {
constructor(name, age, id) {
this.name = name;
this.age = age;
this.ele = document.querySelector(`#${id}`)
}
get html() {
return this.ele.innerHTML
}
set html(data) {
this.ele.innerHTML = data.map(item => `<li>${item}</li>`).join("")
}
}
let obj = new Person("kerwin", 100, "list")
</script>
</body>
4.静态属性和静态方法
class Person {
static name = "Person这个类"
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name,this.age)
}
//es6静态属性和静态方法关键字 static
static eat(){
console.log("eat")
}
}
let obj = new Person("kerwin",100)
//es6里静态属性和静态方法相当于
//Person.eat = function(){console.log("eat")}
//静态属性和静态方法只能通过类名来访问,不能通过实例对象访问
console.log(Person.name)
Person.eat()
5.继承
ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象。
class Person {
static myname = "person类的名字"
static mymethod = function () {
console.log("mythod")
}
constructor(name, age) {
this.name = name
this.age = age
}
say() {
console.log(this.name, this.age)
}
}
//继承关键字 extends
class Student extends Person {
static myname = "Student类的名字"
static mymethod = function () {
console.log("Student- mythod", this.myname)
}
constructor(name, age, score) {
super(name, age)//调用了父类中的构造函数
this.score = score
}
say() {
super.say()
console.log(this.score)
}
getScore() {
console.log(this.score)
}
}
let obj = new Student("kerwin", 100, 150)
将类、继承用再页面渲染上 :
<body>
<div class="box1">
<h1></h1>
<ul></ul>
</div>
<div class="box2">
<h1></h1>
<img src="" alt="" style="width:100px;">
<ul></ul>
</div>
<script>
var data1 = {
title: "体育",
list: ["体育-1", "体育-2", "体育-3"]
}
var data2 = {
title: "综艺",
url: "https://pic.maizuo.com/usr/movie/5011ee407fb407d47e333a3935ec33d1.jpg?x-oss-process=image/quality,Q_70",
list: ["综艺-1", "综艺-2", "综艺3"]
}
class CreatBox{
constructor(select,data){
this.ele = document.querySelector(select)
this.title = data.title
this.list = data.list
this.render()
}
render(){
let oh1 = this.ele.querySelector("h1")
let oul = this.ele.querySelector("ul")
oh1.innerHTML = this.title
oul.innerHTML = this.list.map(item=>
`<li>${item}</li>`
).join("")
}
}
new CreatBox(".box1",data1)
class CreateImgBox extends CreatBox{
constructor(select,data){
super(select,data)
this.imgUrl = data.url
this.render()
}
render(){
// console.log("111111",this.imgUrl)
super.render()
let oimg = this.ele.querySelector("img")
oimg.src= this.imgUrl
}
}
new CreateImgBox(".box2",data2)
</script>
</body>
18【Module语法】
JavaScript 现在有两种模块。一种是 ES6 模块,简称 ESM;另一种是 CommonJS 模块,简称 CJS。
CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。语法上面,两者最明显的差异是,CommonJS 模块使用require()和module.exports,ES6 模块使用import和export。
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
使用模块化好处:
异步加载
私密不漏
重名不怕
依赖不乱
写法1:
//导出A1
export default A1
//导入1.js里的A1(导入时的名字可自己命名),from后接相对路径
import A1 from "./1.js"
写法2:
两种导出方式,三种导入方式
//①导出A1,A2
export {A1,A2}
//②可直接在需要导出的函数前加export
export function A1(){
console.log("A1")
}
//① 导入A1,A2 ,此方法导入导出名字必须一致
import {A1,A2} from "./1.js"
//② as重命名,将A1重命名为a1
import {A1 as a1,A2 as a2} from "./1.js"
//③ *as将1.js里需要导出的全部导入
import * as obj from "./1.js"
混合使用:
//两种导出方式可混合使用
export {A1}
export default A2
//导入时根据各自的特点进行导入
import A2,{A1} from "./1.js"