目录
本次笔记的视频教程:腾讯课堂
1、let:
-
没有预解析,不存在变量提升(未定义使用会报错)
var a=12; function a(){ console.log(a); let a=5; } a(); //输出报错,Uncaught TypeError: a is not a function
-
同一作用域不能重复定义变量
let a=12; let a=5; console.log(a); //输出报错:Uncaught SyntaxError: Identifier 'a' has already been declared
-
for循环,for循环里面是父级作用域,里面又一个
for(let i=0;i<2;i++){ let i='abc'; console.log(i); } console.log(i); //1、for循环里面的i输出两次‘abc’ //2、for循环外面的i会报错,Uncaught ReferenceError: i is not defined
2、const(常量)特性和let一样
-
定义变量不允许修改
const a=123; let a=456; console.log(a); //输出报错:Uncaught SyntaxError: Identifier 'a' has already been declared
-
定义变量后必须有值,不能之后修改
const a console.log(a); //输出报错:Uncaught SyntaxError: Missing initializer in const declaration const a a=12; console.log(a); //输出报错:Uncaught SyntaxError: Missing initializer in const declaration
-
数组等引用类型,可以通过本身的方法修改
const arr=['apple','banana']; arr=[]; console.log(arr); //输出报错:Uncaught TypeError: Assignment to constant variable const arr=['apple','banana']; arr.push('orange'); console.log(arr); //输出["apple", "banana", "orange"]
-
应用场景:
const http=require('http');
3、数组不可修改,可以使用freeze方法
const arr=Object.freeze(['apple','banana']);
arr.push('orange');
console.log(arr);
//输出报错:Uncaught TypeError: Cannot add property 2, object is not extensible
4、解构赋值 (非常有用,特别在做数据交互,ajax)
- 解构赋值
let [a,b,c]=[10,12,15]; console.log(a,b,c); //输出:10 12 15
- 左右两边,结构保持一致
/* * 结构不一致 */ let [a,b,c]=[10,[12,15]]; console.log(a,b,c); //输出:10 (2) [12, 15] undefined /* * 结构一致 */ let [a,[b,c]]=[10,[12,15]]; console.log(a,b,c); //输出:10 12 15
- json
let json={ name:'xxx', age:18, } let{name,age}=json; console.log(name,age); //输出:xxx 18
- 可以起别名
let json={ name:'xxx', age:18, } let{name:n,age:a}=json; console.log(n,a); //输出:xxx 18
- 可以给默认值
let [a,b,c]=['aa','bb']; console.log(a,b,c); //输出:aa bb undefined let [d,e,f='暂无数据']=['aa','bb']; console.log(d,e,f); //输出:aa bb 暂无数据 let [j,h,k='暂无数据']=['aa','bb','cc']; console.log(j,h,k); //输出:aa bb cc let [i,g,l='暂无数据']=['aa','bb',null]; console.log(i,g,l); //当对应的值为null时,输出:aa bb null //可以增加判断 let [i,g,l='暂无数据']=['aa','bb',null]; console.log(i,g,(l==null?'暂无数据':l)); //输出:aa bb 暂无数据
- 可以交互两个数的值
let a=15; let b=2; [a,b]=[b,a]; console.log(a,b); //输出:2 15
5、字符串模板:
- 字符串模板:``,优点:1、可以随意换行2、${变量名称}
let name='xxx'; let age=16; let person=`我叫${name},年龄是${age}`; console.log(person); //输出:我叫xxx,年龄是16
- 字符串查找:includes()返回true或false
let str='apple banana pear'; console.log(str.includes('apple')); //输出:true //可以用来判断浏览器 if(navigator.userAgent.includes('Chrome')){ console.log('yes'); }else{ console.log('no'); } //谷歌浏览器,输出yes //火狐浏览器,输出:no //360浏览器,输出:yes,以上判断对于双核浏览器不怎么有用
- 检测字符串以谁开头:startsWith();检测字符串以谁结尾:endsWith()
let str='aaaa.png'; console.log(str.startsWith('a'),1); //true 1 console.log(str.startsWith('b'),2); //false 2 console.log(str.endsWith('png'),3); //true 3 console.log(str.endsWith('jpg'),4); //false 4
- 重复次数:repeat(次数)
let str='我是lxq'; console.log(str.repeat(2),1); //输出:我是lxq我是lxq 1
- 填充字符串:padStart(整个字符串长度,'填充内容');padEnd(整个字符串长度,'填充内容')
let str='我是'; let padStr='你好'; let padEnd='lxq'; console.log(str.padStart(padStr.length+str.length,padStr),2); //输出:你好我是 2 console.log(str.padEnd(padEnd.length+str.length,padEnd),3); //我是lxq 3
6、函数
函数新变化
- 函数可以有默认参数
/*无默认值*/ function show(a,b){ console.log(a,b); } show('xx',2); //输出:xx 2 show('xx',''); //输出:xx show('xx'); //输出:xx undefined show( ,2); //输出报错:Uncaught SyntaxError: Unexpected token , show('',2); //输出:空格 2 /*有默认值*/ function show(a='wo',b=1){ console.log(a,b); } show('xx',2); //输出:xx 2 show('xx',''); //输出:xx show('xx'); //输出:xx 1 show( ,2); //输出报错:Uncaught SyntaxError: Unexpected token , show('',2); //输出:空格 2
- 函数参数默认已经定义,不能使用let,const声明
function show(a=18){ let a=101; // var a=101; console.log(a); } show(); //输出报错:Uncaught SyntaxError: Identifier 'a' has already been declared //可以使用var 声明,但是会引起作用域的问题
- 扩展运算符、reset运算符('...':展开数组)
let arr=['apple','banana','cx']; console.log(arr); //输出:(3) ["apple", "banana", "cx"] console.log(...arr); //输出:apple banana cx /*在函数中的应用*/ function show(...a){ console.log(a); } show(1,2,3,4,5); //输出:[1, 2, 3, 4, 5] /*在函数中的应用*/ function show(...a){ console.log(a.sort()); } show(9,8,3,4,5); //输出:[3, 4, 5, 8, 9] /*在函数中的应用*/ function show(a,b,c){ console.log(a,b,c); } show(...[1,2,3]); //输出:1 2 3 /*在函数中的应用,剩余运算符必须放在最后一个参数*/ function show(a,b,...c){ console.log(a,b); console.log(c); } show(1,2,3,4,5); //输出:1 2 //输出:(3) [3, 4, 5]
- 箭头函数
注意事项:箭头函数可以改变this指向,没有arguments,不能当构造函数/* * 箭头函数 * let show=()=>1此种形式实用性不高,箭头左边(变量),箭头右边相当于return返回值 */ let show=()=>1; console.log(show()); //输出:1 let show=(a,b)=>a+b; console.log(show(12,5)); //输出:17 let show=(a,b)=>{ console.log(a,b,a+b); return a+b; }; show(12,5); //输出:12 5 17
/* * 1、箭头函数可以改变this指向 * this:定义函数所在的对象,不在是运行时所在的对象 */ var id=1; var j={ id:21, show:function(){ setTimeout(function(){ console.log(this.id); },2000); } } j.show(); //输出:1 var id=1; var j={ id:21, show:function(){ setTimeout(()=>{ console.log(this.id); },2000); } } j.show(); //输出:21 /* * 2、箭头函数没有arguments * 解决方法:使用'...'+参数,我在此处仍然使用了arguments */ function show(){ console.log(arguments); } show(1,2,3,4,5); //输出:Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ] let show=()=>{ console.log(arguments); } show(1,2,3,4,5); //输出:js.html:15 Uncaught ReferenceError: arguments is not defined let show=(...argument)=>{ console.log(argument); } show(1,2,3,4,5); //输出:(5) [1, 2, 3, 4, 5] /* * 3、箭头函数不能当构造函数 */ function show(){ this.name='abc'; } let s=new show(); console.log(s.name); //输出:abc let show=()=>{ this.name='abc'; } let s=new show(); console.log(s.name); //输出:Uncaught TypeError: show is not a constructor
- ES2017(ES8)规定:函数参数最后的逗号可以有
function show(a,b,c,d,){ console.log(a,b,c,d); } show(1,2,3,4,); //输出:1 2 3 4
7、数组
- 数组循环:
- 常用for、while
- ES5新增循环:数组方面forEach、map、filter、some、every、reduce、reduceRight
/* * forEach(无返回值,方法最后输出undefined) * 1、不指定this */ let arrary=['a','b']; let newArrary=arrary.forEach(function(val,index,arrary){ console.log(this,val,index,arrary); }); console.log(newArrary); //输出1:Window "a" 0 (2) ["a", "b"] //输出2:Window "b" 1 (2) ["a", "b"] //输出3:undefined /*指定this,不经常用*/ let arrary=['a','b']; arrary.forEach(function(val,index,arrary){ console.log(this,val,index,arrary); },123); //输出1:Number {123} "a" 0 (2) ["a", "b"] //输出2:Number {123} "b" 1 (2) ["a", "b"] /* * 还可以使用箭头函数 * 使用箭头函数后,第二个参数this的指向就不生效了 */ let arrary=['a','b']; arrary.forEach((val,index,arrary)=>{ console.log(this,val,index,arrary); }); //输出1:Window "a" 0 (2) ["a", "b"] //输出2:Window "b" 1 (2) ["a", "b"] let arrary=['a','b']; arrary.forEach((val,index,arrary)=>{ console.log(this,val,index,arrary); },123); //输出1:Window "a" 0 (2) ["a", "b"] //输出2:Window "b" 1 (2) ["a", "b"]
/* * map:非常有用,可以做数据交互"映射" * 正常情况下,需要配合return,返回的是一个新数组,否则与forEach无异 * 没有返回值,会返回[undefined, undefined, undefined] */ let arr=[ {'title':'aaa',num:12}, {'title':'bbb',num:13}, {'title':'ccc',num:14} ]; let newArr=arr.map((val,index,arr)=>{ console.log(val,index,arr); //没有返回值,会返回[undefined, undefined, undefined] }); console.log(newArr); //输出: [undefined, undefined, undefined] /* * map:非常有用,可以做数据交互"映射" * 有返回值,会返回一个新数组 */ let arr=[ {'title':'aaa',num:12,hot:true}, {'title':'bbb',num:13,hot:false}, ]; let newArr=arr.map((val,index,arr)=>{ let obj={}; obj.t=val.title; obj.n=val.num; obj.h=val.hot==true&&'good!'; return obj; }); console.log(newArr); //输出:(3) [{…}, {…}] //展开1:0: {t: "aaa", n: 12, h: "good!"} //展开2:1: {t: "bbb", n: 13, h: false}
/* * filter:过滤,过滤一些不合格元素,如果回调函数返回true */ let arr=[ {'title':'aaa',num:12,hot:true}, {'title':'bbb',num:18,hot:false}, {'title':'ccc',num:11,hot:true}, {'title':'ddd',num:18,hot:false}, ]; let newArr=arr.filter((val,index,arr)=>{ return !val.hot; }); console.log(newArr); //输出:(2) [{…}, {…}] //展开1:0: {title: "bbb", num: 18, hot: false} //展开2: 1:{title: "ddd", num: 18, hot: false}
/* * some:类似查找,数组里面某一个元素符合条件,返回true * every:要数组里面的所有元素都符合条件,才返回true */ let arr=['a','xx','c']; let newArr=arr.some((val,index,arr)=>{ return val==='xx'; }); console.log(newArr); //输出:true let arr=['a','xx','c']; let newArr=arr.some((val,index,arr)=>{ return val==='x'; }); console.log(newArr); //输出:false /*every*/ let arr=[1,3,5,7]; let newArr=arr.every((val,index,arr)=>{ return val%2==1; }); console.log(newArr); //输出:true let arr=[1,3,5,7,10]; let newArr=arr.every((val,index,arr)=>{ return val%2==1; }); console.log(newArr); //输出:false
/* * reduce接收参数('前一个值','当前值','当前索引','循环数组'),从左往右计算 * reduceRight 从右往左计算 */ let arr=[1,3,5,7]; let newArr=arr.reduce((prev,cur,index,arr)=>{ return prev+cur; }); console.log(newArr); //输出:16 let arr=[2,2,3]; let newArr=arr.reduce((prev,cur,index,arr)=>{ return Math.pow(prev,cur); //ES2017新增求幂的运算符 // return Math.pow(2,3);//输出:8 // return 2**3; //输出:8 }); console.log(newArr); //输出:64 /*reduceRight*/ let arr=[2,2,3]; let newArr=arr.reduceRight((prev,cur,index,arr)=>{ return Math.pow(prev,cur); }); console.log(newArr); //输出:81
- ES6 数组新增
- for...of...循环
/* * for...of...循环 */ let arr=['a','b']; //默认循环 arr.values()可不写,直接写成for(let val of arr){} for(let val of arr.values()){ console.log(val); } //输出:a,b for(let index of arr.keys()){ console.log(index); } //输出:0,1 for(let item of arr.entries()){ console.log(item); } //输出1:(2) [0, "a"] //输出2:(2) [1, "b"] for(let [key,val] of arr.entries()){ console.log(key,val); } //输出1:0 "a" //输出2:1 "b"
- from Array.from()作用:将类数组对象转成数组
/* * Array.from() * 作用:将类数组对象转成数组 */ show(1,2,3,4); function show(a,b,c,d){ console.log(arguments,'类数组'); //输出:Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ] "类数组" let arr=Array.from(arguments); console.log(arr,'from后'); //输出:(4) [1, 2, 3, 4] "from后" let arr2=[...arguments]; console.log(arr2,'...后'); //(4) [1, 2, 3, 4] "...后" }
- of Array.of()作用:把一组值,转成数组
let arr3=Array.of('aa','bb','cc'); console.log(arr3); //输出:["aa", "bb", "cc"]
- find\findIndex:查找\查找位置
/* * Array.find()作用:查找,找出第一个符合条件的成员,如果有,返回该值,没有,返回undefined; * Array.findIndex()作用:查找位置,找出第一个符合条件的成员位置,如果有,返回该值下表,没有,返回-1; */ let arr=[23,90,102,80,105]; let newArr=arr.find((val,index,arr)=>{ return val>1000; }) console.log(newArr); //输出:102 let index=arr.findIndex((val,index,arr)=>{ return val>100; }) console.log(index); //输出:2
- fill:填充内容
/* * arr.fill('填充内容','开始位置','结束位置'); * 规定起始位置的,填充内容包含开始位置,不包含结束位置 */ let arr=new Array(3); arr.fill('默认值',1,2); console.log(arr); //输出:(3) [empty, "默认值", empty]
- includes:
let arr=['a','b','c']; console.log(arr.includes('a')); //输出:true console.log(arr.includes('aa')); //输出:false
8、对象
- 对象简洁语法
let name='ss'; let age=18; let obj={ name, //等价于name:name, age, //等价于age:age showA(){ return this.name; },//等价于 function showA(){return this.name;},最好不要用箭头函数,会引发作用域问题 showB(){ return this.age; }//等价于 function showB(){return this.name;},最好不要用箭头函数,会引发作用域问题 }; console.log(obj.showA(),obj.showB());
- is:Object.is() 作用:比较两个值是否相等
console.log(NaN==NaN); //输出:false console.log(Object.is(NaN,NaN)); //输出:true console.log(+0==-0); //输出:true console.log(Object.is(+0,-0)); //输出:false console.log(Object.is('aaa','aa'));//输出:false console.log(Object.is('aaa','aaa'));//输出:true
- assign:Object.assign() 作用:合并对象(复制一个对象,合并参数)
let o={ a:1 } let o1={ b:2, a:100 } let o2={ c:3 } console.log(Object.assign({},o,o1,o2)); //输出:{a: 100, b: 2, c: 3},注意a的值被o1的a的值覆盖 /**/ let arr=['a','b','c']; let arr2=Object.assign([],arr); arr2.push('d'); console.log(arr2); //输出:["a", "b", "c", "d"] console.log(arr); //输出: ["a", "b", "c"]
-
Object的keys(),values(),entries()
let {keys,values,entries}=Object; //解构 let obj={ a:1, b:2 } for(let key of keys(obj)){ console.log(key); } //输出1:a //输出2:b for(let val of values(obj)){ console.log(val); } //输出1:1 //输出2:2 for(let entr of entries(obj)){ console.log(entr); } //输出1:(2) ["a", 1] //输出2:(2) ["b", 2]
-
Object的扩展运算符(...)的使用
let obj={ a:1, b:2 } let obj2={...obj}; console.log(obj2); //输出:{a: 1, b: 2} delete obj2.a; console.log(obj2); //输出:{b: 2} console.log(obj); //输出:{a: 1, b: 2}
9、promise
- 基本语法
let a=12; new Promise(function(resolve,reject){ //resolve,成功的时候调用 //reject,失败的时候调用 if(a==10){ resolve(true) }else{ reject(false); }//ajax请求 }).then(res=>{ console.log(res); }).catch(err=>{ //reject发生错误,别名 console.log(err); }); //输出:false
-
快速转成Promise对象
Promise.resolve('aa'); //将现有东西,转成一个promise对象,resolve成功状态 //等价于 new Promise(resolve=>{resolve('aa')}); Promise.reject('aaa'); //将现有东西,转成一个promise对象,resolve成功状态 //等价于 new Promise((resolve,reject)=>{reject('aaa')});
-
Promise.all(): Promise.all([p1,p2,p3]):把promise打包,扔到一个数组里面,打包完还是promise对象,必须确保所有promise对象都是resolve成功状态
let p1=Promise.resolve('aa'); let p2=Promise.resolve('bb'); let p3=Promise.resolve('cc'); Promise.all([p1,p2,p3]).then(res=>{ console.log(res); //输出:["aa", "bb", "cc"] let [res1,res2,res3]=res; //解构 console.log(res1,res2,res3); //输出:aa bb cc }).catch(err=>{ console.log(err); }); //输出:["aa", "bb", "cc"] //输出:aa bb cc
-
Promise.race():Promise.race([p1,p2,p3]):
let p1=Promise.resolve('aa'); let p2=Promise.reject('bb'); let p3=Promise.resolve('cc'); Promise.race([p1,p2,p3]).then(res=>{ console.log(res,1); }).catch(err=>{ console.log(err,2); }); //输出:aa 1
10、模块化
- 注意:需要放到服务器环境
- 如何定义模块 export
- 如何使用 import
- import可以是相对路径
- import模块只会导入一次,无论引入多少次
- import './xx.js';这么用,相当于引入文件
- import 有提升效果
- 导出去的模块内容,如果有定时器更改,外面也会改动,与Common规范不同(Common规范会缓存)
- import 类似node里面require,可以动态引入,默认import语法不能写到if之类的里面
/*1*/ export const a=12; //对应 import './xx.js'; /* * 2 export时可以取别名 */ const a=12; const b=12; const c=12; export{ a as aaa, b as banana, c as cup } //对应 import {aaa,banana,cup} from './xx.js'; console.log(aaa,banana,cup); /* * 3 import时也可以取别名 */ import {aaa as a,banana as b,cup as c} from './xx.js'; console.log(a,b,c); /* * 4 可以简写 * */ import * as modTwo from './xx.js'; console.log(modTwo.aaa); /* * 5 只有default时,import时不用加{} */ export default a=12; //对应 import a from './xx.js'; console.log(a); /* * 6 试题 */ import mod,{show, sum,a,b} from './xx.js'; let p1=new mod.Person('xx'); console.log(p1.showName()); show(); sum(); console.log(a,b); //对应 const a=12; const b=5; const sum=()=>{ return a+b; } const show=()=>{ console.log(1,2); } class Person{ constructor(name,age){ this.name=name; this.age=age; } showName(){ return this.name; } } export { a, b, sum, show } export default { Person } /* * 7 导出去的模块内容,如果有定时器更改,外面也会改动,与Common规范不同(Common规范会缓存) */ //导出 let a=12; let a=b; setTimeout(()=>{ a=11111; },2000); export { a, b } //引入 import {a,b} from './xx.js'; console.log(a); //输出:12 setTimeout(()=>{ console.log(a); },3000); //输出:11111 /* * 8 import 类似node里面require,可以动态引入,默认import语法不能写到if之类的里面 * 优点:1.按需加载2.可以写if中3.路径可以动态 */ import('./xx.js').then(res=>{ console.log(a,b); })
11、类和继承
- ES6之前,js没有类,程序员用function来模拟类
function Person(name,age){ this.name=name; this.age=age; } Person.prototype.showName=function(){ console.log(`我的名字是${this.name}`); } Person.prototype.showAge=function(){ console.log(`我的年龄是${this.age}`); } let p1=new Person('xx',18); p1.showName(); p1.showAge(); //输出:我的名字是xx //输出:我的年龄是18 /****************另一种写法*******************************/ function Person2(name,age){ this.name=name; this.age=age; } Object.assign(Person2.prototype,{ showName(){ console.log(`我的名字是${this.name}`); }, showAge(){ console.log(`我的年龄是${this.age}`); } }) let p2=new Person2('xx',18); p2.showName(); p2.showAge(); //输出:我的名字是xx //输出:我的年龄是18
- ES6 class形式
class Person{ constructor(name,age){//构造函数,调用new,自动执行 // console.log(`${name},${age}`); //xx,18 this.name=name; this.age=age; } showName(){ console.log(`${this.name}`); } showAge(){ console.log(`${this.age}`); } } let p1=new Person('xx',18); p1.showName(); p1.showAge(); //输出:xx //输出:18 /************************************************第二种:表达式形式,不推荐*/ let Person=class{ constructor(name,age){//构造函数,调用new,自动执行 // console.log(`${name},${age}`); //xx,18 this.name=name; this.age=age; } showName(){ console.log(`${this.name}`); } showAge(){ console.log(`${this.age}`); } } let p1=new Person('xx',18); p1.showName(); p1.showAge(); //输出:xx //输出:18
- 属性名可以使用变量
let a='yy'; let b='xx'; class Person{ //constructor构造函数,调用new,自动执行 constructor(){} [a+b](){ console.log(1+2); } } let p1=new Person('xx',18); p1[a+b](); //输出:3
- ES6里面函数没有预解析,不能提升;ES5函数模拟类,有提升
let p1=new Person('xx',18); p1[a+b](); let a='yy'; let b='xx'; class Person{ //constructor构造函数,调用new,自动执行 constructor(){} [a+b](){ console.log(1+2); } } //输出报错:Uncaught ReferenceError: Person is not defined
- this问题:ES6里面的this比之前轻松多了
class Person{ constructor(){ this.name='yy'; } showName(){ console.log(`this=`+this); console.log(`${this.name}`); } } let p1=new Person(); let {showName}=p1; console.log(showName()); //输出:this=undefined //输出报错:Uncaught TypeError: Cannot read property 'name' of undefined /***************************************************矫正this*************************/ class Person{ constructor(){ this.name='yy'; //矫正this bind:只矫正this,call、apply:调用方法 this.showName=this.showName.bind(this); } showName(){ console.log(`this=`+this); console.log(`${this.name}`); } } let p1=new Person(); let {showName}=p1; console.log(showName()); //输出:this=[object Object] //输出:yy
- class里面的取值函数(getter)、存值函数(setter)
class Person{ constructor(){ } //返回值 get aaa(){ return(`getAaa`); } //设置值 set aaa(val){ console.log(`设置aaa为:${val}`,1); } } let p1=new Person(); p1.aaa='123';//设置值 console.log(p1.aaa);//取值 //输出:设置aaa为:123 1 //输出:getAaa
- 静态方法static:类自身上的方法,调用方式类名.静态方法名
class Person{ constructor(){} static aaa(){ console.log(`我是静态类,可以用自身+“.”来调用`); } } Person.aaa(); //输出:我是静态类,可以用自身+“.”来调用
- 继承
/****************************************之前继承****/ //父类 function Person(name){ this.name=name; } Person.prototype.showName=function(){ console.log(`${this.name}`); } //子类 function Student(name,skill){ Person.call(this,name);//继承属性 this.skill=skill; } Student.prototype=new Person();//继承方法 // * Student的constructor有问题,需要矫正 //调用 let stu1=new Student('ss','迟到'); stu1.showName(); //输出:ss
/****************************************ES6继承简化版****/ //父类 class Person1{ constructor(name){ this.name=name; } showName(){ console.log(`${this.name}`); } } //子类 class Student1 extends Person1{} //调用 let stu2=new Student1('yy'); stu2.showName(); //输出:yy /****************************************ES6继承完整版****/ //父类 class Person1{ constructor(name,age){ this.name=name; this.age=age; } showName(){ console.log(`${this.name}`); } showAge(){ console.log(`${this.age}`); } } //子类 class Student1 extends Person1{ constructor(name,age,skill){ //子类也需要有子类的属性和方法 super(name,age);//如果没有super,子类的constructor会覆盖父级的constructor,报错:Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor this.skill=skill; } showSkill(){ console.log(`今天又${this.skill}`); } } //调用 let stu2=new Student1('yy',18,'迟到'); stu2.showName(); stu2.showAge(); stu2.showSkill(); //输出:yy //输出:18 //输出:今天又迟到
- 子类继承父类某方法,并在某方法中写自己的内容
//父类 class Person1{ constructor(name){ this.name=name; } showName(){ console.log(`父类showName`); } } //子类 class Student1 extends Person1{ constructor(name){ //子类也需要有子类的属性和方法 super(name); } showName(){ super.showName();//父类方法执行 //TODO 做子类的事情 console.log(`子类showName`,`${this.name}`); } } //调用 let stu2=new Student1('yy'); stu2.showName(); 输出:父类showName 输出:子类showName yy
12、Symbol & generator
symbol:
symbol使用情况一般,node底层可能会用到
let sym=Symbol('aaa');
console.log(sym);
//输出:Symbol(aaa)
/*
* 注意:
* 1、Symbol不能new
*/
let sym=new Symbol('aaa');
console.log(sym);
//输出报错:Uncaught TypeError: Symbol is not a constructor
/*
* 注意:
* 2、Symbol返回的是一个唯一的值。做一个Key,定义一些唯一或者私有的东西
* 3、symbol是一个单独数据类型,typeof检测出来就是symbol类型,基本类型
*/
let sym=Symbol('aaa');
console.log(typeof sym);
//输出:symbol
/*
* 注意:
* 4、symbol作为key,for...in循环,值出不来
*/
let symbol=Symbol('aaa');
let json={
a:1,
b:2,
[symbol]:'aaa'
}
for(let key in json){
console.log(key);
}
//输出:a
//输出:b
generator函数(生成器):
解决异步深度嵌套,现在多使用async
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,定义不同的内部状态(yield
在英语里的意思就是“产出”)。
/*
*generator函数语法
*/
//定义
function * gen(){
yield 'ww';
yield 'to';
return 'xx';
}
/*
* 调用1:
* 手动调用,太麻烦
*/
let t=gen();
console.log(t.next());
//输出:{value: "ww", done: false}
console.log(t.next());
//输出:{value: "to", done: false}
console.log(t.next());
//输出:{value: "xx", done: true}
/*
* 调用2:
* 还可以使用for...of自动遍历gennerator,
* 但是return的东西,for...of不会遍历出来
*/
let t=gen();
for(let val of t){
console.log(val);
}
//输出:ww
//输出:to
/*
* 调用3:
* 可以使用解构,return的东西,不会解构出来
*/
let [a,b]=gen();
console.log(a,b);
//输出:ww to
/*
* 调用4:
* 可以使用拓展运算符(...),return的东西,不会拓展出来
*/
let [c,...d]=gen();
console.log(c,d);
//输出:ww ["to"]
yield
关键字使生成器函数执行暂停,yield
关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的 return
关键字。javascript中的yield就是类似lisp中的continuation(田春把他翻译成了续延) On Lisp中文版里也有提到,LISP语言是1958年发明的。
yield
关键字实际返回一个 IteratorResult
对象,它有两个属性,value
和 done
。value
属性是对 yield
表达式求值的结果,而 done
是 false
,表示生成器函数尚未完全完成。
一旦遇到 yield
表达式,生成器的代码将被暂停运行,直到生成器的 next()
方法被调用。每次调用生成器的 next()
方法时,生成器都会恢复执行,直到达到以下某个值:
yield
,导致生成器再次暂停并返回生成器的新值。下一次调用next()
时,在yield
之后紧接着的语句继续执行。- throw 用于从生成器中抛出异常。这让生成器完全停止执行,并在调用者中继续执行,正如通常情况下抛出异常一样。
- 到达生成器函数的结尾。在这种情况下,生成器的执行结束,并且
IteratorResult
给调用者返回value
的值是 undefined 并且done
为true
。 - 到达 return 语句。在这种情况下,生成器的执行结束,并将
IteratorResult
返回给调用者,其value
的值是由return
语句指定的,并且done
为true
。
yield
表达式本身没有返回值,或者说总是返回undefined
。next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
generator面试题
function* f(): Generator<number, boolean, boolean> {
for (let i = 0; true; i++) {
console.log('i-----:', i)
let reset: boolean = yield i
if (reset) {
i = -1
console.log('--i--:', i)
}
}
}
let g = f()
console.log(g.next())
console.log(g.next())
console.log(g.next(true))
上面代码先定义了一个可以无限运行的 Generator 函数f
,如果next
方法没有参数,每次运行到yield
表达式,变量reset
的值总是undefined
。当next
方法带一个参数true
时,变量reset
就被重置为这个参数(即true
),因此i
会等于-1
,下一轮循环就会从-1
开始递增。
这个功能有很重要的语法意义。Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next
方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
知识点:
- 异步:不连续操作,上一个操作没有执行完,下一个操作照样开始
- 同步:连续执行,上一个操作没有执行完,不会执行下有一个操作
- 异步解决方案:1):回调函数;2):事件监听;3):发布/订阅;4)Promise对象
13、async、await
- asyn:ES2017规定的
/* * 用node做例子,分别用promise,generator,async异步读取文件 */ //data文件夹有文件a.txt,b.txt,c.txt读取这三个文件 const fs=require('fs'); //将fs进行一个简单的promise封装 const readFile=function(fileName){ return new Promise((resolve,reject)=>{ fs.readFile(fileName,(err,data)=>{ if(err) reject(err); resolve(data); }); }); } /* * promise */ readFile('data/a.txt').then(res=>{ console.log(res); return readFile('data/b.txt') }).then(res=>{ console.log(res); return readFile('data/c.txt') }); /* * generator */ function * gen(){ yield readFile('data/a.txt'); yield readFile('data/b.txt'); yield readFile('data/c.txt'); } let g1=gen(); g1.next().value.then(res=>{ console.log(res); //输出a.txt return g1.next().value; //请求b.txt }).then(res=>{ console.log(res);//输出b.txt return g1.next().value;//请求c.txt }).then(res=>{ console.log(res);//输出c.txt }); /* * async */ async function fn(){ //await:表示后面结果需要等待 let f1= await readFile('data/a.txt'); console.log(f1); let f2= await readFile('data/b.txt'); console.log(f2); let f3= await readFile('data/c.txt'); console.log(f3); } fn();
- async特点:
- await只能放到async方法里面
- 相比generator语义化更强
- await后面可以是promise对象,也可以是数字、字符串、布尔值
- async函数返回的是一个promise对象
async function fn(){ return 'welcome'; } console.log(fn()); //输出:Promise {<resolved>: "welcome"} fn().then(res=>{ console.log(res); }); //输出:welcome async function fnErr(){ throw new Error('出错了'); } fnErr().then(res=>{ console.log(res); },err=>{ console.log(err); }); //输出:Error: 出错了 fnErr().then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }); //输出:Error: 出错了
- 只要await语句后面promise状态变成reject,整个函数会中断执行
/* * 注释出错语句 */ async function fn(){ let a=await Promise.resolve('成功1'); console.log(a); // await Promise.reject('出错了'); let b=await Promise.resolve('成功2'); console.log(b); } fn().then(res=>{ // console.log(res); }).catch(err=>{ console.log(err); }); //输出:成功1 //输出:成功2 /* * 不注释出错语句 */ async function fn(){ let a=await Promise.resolve('成功1'); console.log(a); await Promise.reject('出错了'); let b=await Promise.resolve('成功2'); console.log(b); } fn().then(res=>{ // console.log(res); }).catch(err=>{ console.log(err); }); //输出:成功1 //输出:出错了
- 解决只要await语句后面promise状态变成reject,整个函数会中断执行
/* * 方法一: */ async function fn(){ try{ await Promise.reject('出错了'); }catch(e){ let a=await Promise.resolve('成功1'); console.log(a); let b=await Promise.resolve('成功2'); console.log(b); } } fn().then(res=>{ // console.log(res); }).catch(err=>{ console.log(err); }); //输出:成功1 //输出:成功2 /* * 方法二: */ async function fn(){ await Promise.reject('出错了').catch(err=>{ console.log(err); }); let a=await Promise.resolve('成功1'); console.log(a); let b=await Promise.resolve('成功2'); console.log(b); } fn().then(res=>{ // console.log(res); }).catch(err=>{ console.log(err); }); // 输出:出错了 // 输出:成功1 // 输出:成功2 /* * 方法三:个人建议将await都放在try..catch中 */ async function fn(){ try{ let f1= await readFile('data/a.txt'); let f2= await readFile('data/b.txt'); let f3= await readFile('data/c.txt'); }catch(e){} }
14、Set和WeakSet
- SetES6新增的数据结构,类似数组,但是里面不能有重复值
/* * set:一种新的数据结构 */ let s=new Set(['a','b']); console.log(s); //输出:Set(2) {"a", "b"} /* * set:类似数组,但是里面不能有重复值 */ let s1=new Set(['a','b','a','b',1,'1']); console.log(s1); //输出:Set(4) {"a", "b", 1, "1"} /* * set:方法一:add */ s1.add('c'); console.log(s1); //Set(5) {"a", "b", 1, "1", "c"} /* * set:方法二:delete */ s1.delete('c'); console.log(s1); //输出:Set(4) {"a", "b", 1, "1"} /* * set:方法三:has,判断s1有没有该值 */ console.log(s1.has('a')); //输出:true console.log(s1.has('c')); //输出:false /* * set:属性一:size */ console.log(s1.size); //输出:4 /* * set:方法四:clear() */ s1.clear(); console.log(s1); //输出:Set(0) {}
- set循环及用途
/* * set:循环 */ let s2=new Set(['a','b','c','d']); for(let item of s2){ //默认是values console.log(item); } //输出1:a,输出2: b,输出3: c,输出4: d for(let item of s2.keys()){ //keys与values结果一致 console.log(item); } //输出1:a,输出2: b,输出3: c,输出4: d for(let item of s2.values()){ // console.log(item); } //输出1:a,输出2: b,输出3: c,输出4: d for(let item of s2.entries()){ console.log(item); } //输出1: (2) ["a", "a"],输出2: (2) ["b", "b"], //输出3: (2) ["c", "c"],输出4: (2) ["d", "d"] for(let [k,v] of s2.entries()){ console.log(k,v); } //输出1: a a,输出2: b b, //输出3: c c,输出4: d d s2.forEach((k,v)=>{ console.log(v,k); }); //输出1: a a,输出2: b b, //输出3: c c,输出4: d d /* * set:可链式调用 */ let s3=new Set().add('a').add('b').add('c'); /* * set:用途1:可数组去重 */ let arr=[1,2,3,1,2,3]; let newArr=[...new Set(arr)]; console.log(newArr); //输出:(3) [1, 2, 3] /* * set:用途2:使用数组map方法改变值 */ let s4=new Set([1,2,3]); s4=new Set([...s4].map(val=>val*2)); console.log(s4); //输出:Set(3) {2, 4, 6} /* * set:用途3:使用数组filter方法过滤值 */ let s5=new Set([1,2,3]); s5=new Set([...s5].filter(val=>val%2!=0)); console.log(s5); //输出:Set(2) {1, 3} /* * set:里面放置数组,也可以存放对象,但是最好放数组 */ let s6=new Set(); let json={a:1,b:2}; let json2={a:1,b:2}; s6.add(json).add(json2); console.log(s6); //输出:Set(2) {{…}, {…}} s6.forEach(val=>{ console.log(val); }) //输出:{a: 1, b: 2} //输出:{a: 1, b: 2}
- WeakSet:新增的数据结构,在初始状态,直接添加对象会报错,需要用add,用处不大,也没有size、clear()。最好用Set
let s=new WeakSet({a:1,b:2}); console.log(s); //输出报错:Uncaught TypeError: #<Object> is not iterable let s1=new WeakSet(); let json={ a:1, b:2 } s1.add(json); console.log(s1); //输出:WeakSet {{…}}
15、Map和WeakMap
- map:类似于json,但是json的key只能是字符串,map的key可以是任意类型。
/* * map:设置值 */ let m=new Map(); let j={a:'b'} m.set('a','aaaaa'); m.set(j,1); m.set('c',j); console.log(m); //输出:Map(3) {"a" => "aaaaa", {…} => 1, "c" => {…}} /* * map:获取值 */ console.log(m.get(j)); //输出:1 /* * map:循环,也可以循环keys(),values(),entries() */ for(let [k,v] of m){ //默认entries console.log(k,v); } //输出:a aaaaa,输出: {a: "b"} 1,输出: c {a: "b"} m.forEach((v,k)=>{ console.log(v,k); }); /* * map:删除一项 */ m.delete('a'); console.log(m); //输出:Map(2) {{…} => 1, "c" => {…}} /* * map:判断有没有某一项 */ console.log(m.has(j)); //输出:true /* * map:clear() */ m.clear(); console.log(m); //输出:Map(0) {}
- WeakMap:key只能是对象
let wm1=new WeakMap(); wm1.set({'a':'b'},'aaaa'); console.log(wm1); //输出:WeakMap {{…} => "aaaa"} let wm=new WeakMap(); wm.set('a','aaaa'); console.log(wm); //输出报错:Uncaught TypeError: Invalid value used as weak map key
总结:
- Set里面是数组,不重复,没有key,没有get()
- Map对json的扩展,key是任意类型的值
16、数字新增和Math新增的
- 进制变化
/*二进制*/ let a=0b010101; console.log(a); //输出:21 /*八进制*/ let b=0o666; console.log(b); //输出:438
- 数值变化
/*ES6将部分数字操作方法都挂到Number上*/ let a=12; console.log(isNaN(a)); //输出:false console.log(Number.isNaN(a));//输出:false /*isFinite():判断是否是数字*/ console.log(Number.isFinite(a));//输出:true /*isInteger():判断数字是否是整数*/ console.log(Number.isInteger(a));//输出:true /* * 安全整数: * min:-(2^53-1),负的2的53次方减1 * max:2^53-1,2的53次方减1 */ console.log(Number.isSafeInteger(2**53));//输出:false console.log(Number.isSafeInteger(2**53-1));//输出:true console.log(Number.isSafeInteger(-(2**53)));//输出:false console.log(Number.isSafeInteger(-(2**53-1)));//输出:true console.log(Number.MAX_SAFE_INTEGER);//输出:9007199254740991 console.log(Number.MIN_SAFE_INTEGER);//输出:-9007199254740991
- Math的变化
/*trunc():截取,只保留整数位*/ console.log(Math.trunc(4.56)); //输出:4 /*sign():判断一个数是整数(返回1)还是负数(返回-1)0返回0,-0返回-0*/ console.log(Math.sign(-4.56)); //输出:-1 console.log(Math.sign(4.56)); //输出:1 console.log(Math.sign(-0)); //输出:-0 console.log(Math.sign(0)); //输出:0 /*cbrt():计算一个数的开立方根*/ console.log(Math.cbrt(27)); //输出:3
17、ES2018(ES9)新增
- 命名捕获:?<名字>
let str='2018-03-20'; let reg=/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; var data=str.match(reg) let {year,month,day}=str.match(reg).groups; console.log(year,month,day); //输出:2018 03 20
- 反向引用命名捕获:\k<名字>
let str='a-a'; let str2='xx-xx'; let str3='welcome-welcome'; let str4='welcome-welcome-welcome'; let reg=/^(?<xx>welcome)-\k<xx>$/; console.log(reg.test(str),reg.test(str2),reg.test(str3),reg.test(str4)); //输出:false false true false let reg2=/^(?<xx>welcome)-\k<xx>-\1$/; console.log(reg.test(str4)); //输出:false
- 替换:$<名字>
let str='2018-03-20'; let reg=/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; str=str.replace(reg,'$<year>/$<month>/$<day>'); console.log(str); //输出:2018/03/20 /*************************************************结合回调函数*/ str=str.replace(reg,(...args)=>{ let {year,month,day}=args[args.length-1]; return `${day}/${month}/${year}`; }); console.log(str); //输出:20/03/2018
- dotAll模式(之前'.'在正则里匹配任意东西,不包括/n)
let str='welcome32\nroot'; let reg=/^\w+.\w+$/; console.log(reg.test(str)); //输出:false let reg1=/^\w+.\w+$/m; console.log(reg1.test(str)); //输出:true let reg2=/^\w+.\w+$/s; console.log(reg2.test(str)); //输出:true
- 标签函数
function fn(args){ // console.log(args); //输出:["welcome", raw: Array(1)] console.log(args[0].toUpperCase()); } fn`welcome`; //输出:WELCOME
18、Proxy的使用(非常非常有用,但不常用)
- proxy:代理,扩展(增强)对象一些功能,还有预警、上报、统计等等,是设计模式的一种,代理模式
//假如obj对象用户看不到,实现代理 let obj={name:'a'}; let newObj=new Proxy(obj,{ get(target,property){ console.log(target,property); return target[property] } }); newObj.name; //输出:{name: "a"} "name" console.log(newObj.name); //输出:a /*拦截:实现一个,访问对象属性,不存在的时候默认undefine,如果不存在,返回错误信息*/ let newObj2=new Proxy(obj,{ get(target,property){ if(property in target){ return target[property]; }else{ throw new ReferenceError(`key值${property}不存在`) } } }); console.log(newObj2.age); //输出报错:Uncaught ReferenceError: key值age不存在
- proxy的方法示例
/* * get() */ const DOM=new Proxy({},{ get(target,property){ //property是DOM.xx的xx return function(attr={},...children){ const el=document.createElement(property); //添加属性 for(let key of Object.keys(attr)){ el.setAttribute(key,attr[key]) } //添加子元素 for(let child of children){ if(typeof child=='string'){ child=document.createTextNode(child) } el.appendChild(child) } return el; } } }); let oDiv=DOM.div({id:'div1',class:'odiv'},'我是div','hahaha'); console.log(oDiv); //放到页面上 document.body.appendChild(oDiv); /* * set():设置,拦截 */ const obj=new Proxy({},{ set(target,property,value){ if(property=='age'){ if(!Number.isInteger(value)){ throw new TypeError(`年龄必须为整数`); } if(value>150){ throw new RangeError(`年龄超出限制`); } } target[property]=value; } }); obj.a=3; obj.name='qq'; obj.age=18; console.log(obj); //输出:Proxy {a: 3, name: "qq", age: 18} /* * deleteProperty:删除,拦截 * has:检测有没有某个属性 */ let json={a:1,b:2} const newJson=new Proxy(json,{ deleteProperty(target,property){ delete target[property]; }, has(target,property){ //TODO return property in target; } }); delete newJson.a; console.log(newJson,'b' in newJson); //输出:Proxy {b: 2} true
19、Reflect的使用
- Reflect:反射,Reflect.apply('调用的函数','this指向','参数数组')与fn.apply()类似
/* * apply:拦截方法 */ function sum(a,b){ return a+b; } const newSum=new Proxy(sum,{ apply(target,context,args){ //console.log(target,context,args); /* * Reflect:反射 * Reflect.apply与fn.apply('调用的函数','this指向','参数数组')类似 */ return Reflect.apply(...arguments)**2; } }); console.log(newSum(2,3)); //输出:25
- call()、apply()、Reflect.apply的用法
/*
* Reflect:不太成熟,未来新东西放到Reflect上
* Reflect:通过Reflect对象拿到语言内部的东西
*/
function show(...args){
console.log(this,args);
}
show(1,2);
//输出:window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …} (2) [1, 2]
show.call('ab',1,2);
//输出:String {"ab"} (2) [1, 2]
show.apply('ab',[1,2]);
//输出:String {"ab"} (2) [1, 2]
Reflect.apply(show,'ab',[1,2]);
//输出:String {"ab"} (2) [1, 2]
20、补充
- Array.prototype.at(index)比用array[index]方法表示简洁
at()
方法接收一个整数值并返回该索引的项目,允许正数和负数。负整数从数组中的最后一个项目开始倒数。mdn
console.log([5, 12, 8, 130, 44].at(-1))//输出:44
console.log(['5', 12, 8, 130, 44].at(0))//输出:'5'
//找不到指定的索引,则返回undefined
console.log(['5', 12, 8, 130, 44].at(5))//输出:undefined