参考链接:https://www.jianshu.com/p/87008f4f8513,
https://www.cnblogs.com/20gg-com/p/6687443.html?utm_source=itdadao&utm_medium=referral
参考内容:妙味课堂
let、const
let 定义的变量不会被变量提升,const 定义的常量不能被修改,let 和 const 都是块级作用域
ES6前,是var命名变量,有“预编译”功能(变量会先赋值为undefined),js 是没有块级作用域 {} 的概念的。(有函数作用域、全局作用域、eval作用域)
ES6后,let 和 const 的出现,js 也有了块级作用域的概念,前端的知识是日新月异的~
变量提升:在ES6以前,var关键字声明变量。无论声明在何处,都会被视为声明在函数的最顶部;不在函数内即在全局作用域的最顶部。这样就会引起一些误解。例如:
<script>
console.log(a); // undefined
var a = 'hello1';
// 上面的代码相当于,即预编译
var a;
console.log(a); //hello1
a = 'hello1';
// 而 let 就不会被变量提升;且不能重复对同一变量声明赋值,可以写成 a='hello',但不能let声明
console.log(a); // hello1
//报错,SyntaxError: Identifier 'a' has already been declared
a = 'hello2';
let a = 'hello2';
</script>
<script>
console.log(a); //undefined
var a = 1;
a = 1000;
var a = 300;
console.log(a); //300
{
var a = 1;
}
console.log(a) //1
</script>
<script>
{
let b = 100;
console.log(b); //100
}
console.log(b) //b is not defined
</script>
<script>
console.log(b);
//ReferenceError: Cannot access 'b' before initialization
let b = 2;
let b = 200;
// Identifier 'b' has already been declared
console.log(b);
</script>
const 定义的常量不能被修改
<script>
var name = "bai";
name = "ming1";
console.log(name); // ming1
const name = "bai";
name = "ming2"; // Assignment to constant variable.
console.log(name);
</script>
<script>
//var 可以重复对同一变量修改赋值, let则不可以
var x = 10;
x = 30;
console.log(x);
//const可以重复对同一变量修改赋值
const c = 3.14;
c = 10;
console.log( c )
// Assignment to constant variable.
//不可以直接对对象赋值,但可以对它声明属性并赋值
const obj = {};
// obj = 1
obj.name = "kimoo";
console.log( obj ) //{name: "kimoo"}
</script>
import、export
import导入模块、export导出模块
<script>
// 全部导入
import people from './example'
// 将整个模块当作单一对象进行导入,该模块的所有导出都会作为对象的属性存在
import * as example from "./example.js"
console.log(example.name)
console.log(example.getName())
// 导入部分,引入非 default 时,使用花括号
import {name, age} from './example'
// 导出默认, 有且只有一个默认
export default App
// 部分导出
export class App extend Component {};
</script>
class、extends、super
ES5中最令人头疼的的几个部分:原型、构造函数,继承,有了ES6我们不再烦恼!
ES6引入了Class(类)这个概念。
<script>
class Animal {
constructor() {
this.type = 'animal';
}
says(say) {
console.log(this.type + ' says ' + say);
}
}
let animal = new Animal();
animal.says('hello'); //animal says hello
class Cat extends Animal {
constructor() {
super();
this.type = 'cat';
}
}
let cat = new Cat();
cat.says('hello'); //cat says hello
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log( "我叫"+this.name+"今年,"+this.age )
}
eat(){
console.log( "eat...." )
}
}
var p1 = new Person( "kimoo",27 );
console.log( Person.prototype ); //{constructor: ƒ, say: ƒ, eat: ƒ}
// Person("kimoo",27)//出错
</script>
<script>
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
//加上static将变成静态方法或静态属性,必须用 父类. 来调用,如(Person.sex,Person.isHuman(1))
static isHuman( obj ){
return obj instanceof Person
}
static sex = 'man';
say(){
console.log( "我叫"+this.name+"今年,"+this.age )
}
eat(){
console.log( "eat...." )
}
}
//添加静态属性
Person.staticProps = "miaov";
console.log( Person.sex ); //man
console.log( Person.staticProps ) //miaov
var p1 = new Person( "kimoo",27 );
// p1.say();
// p1.isHuman();// 静态方法,报错
console.log( Person.isHuman( 1 ) ); //false
console.log( Person.isHuman( p1 ) ); //true
// var str = "妙";
// console.log( str.charCodeAt(0) ) //22937
// console.log( String.fromCharCode( 22937 ) )
</script>
<script>
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
static isHuman( obj ){
// console.log( "isHuman" )
return obj instanceof Person
}
say(){
console.log( "我叫"+this.name+"今年,"+this.age );
}
eat(){
console.log( "eat...." );
}
}
//继承必须加super()继承父级属性,
class Coder extends Person{
constructor(name,age,money){
super(name,age);
this.money = money;
}
say(){
console.log( "我叫"+this.name+"今年,"+this.age+",一月赚"+this.money );
}
}
var c1 = new Coder("zm",30,30000);
console.log( c1 ); //Coder
c1.say(); //我叫zm今年,30,一月赚30000
console.log( Coder.isHuman( c1 ) ); //true
//继承不会改写原先父级的方法
var p1 = new Person("momo",20);
console.log( p1 ); //Person
p1.say(); //我叫momo今年,20
</script>
上面代码首先用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实力对象可以共享的。Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Cat类,该类通过extends关键字,继承了Animal类的所有属性和方法。super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法,写在第一行),然后再用子类的构造函数修改this。
<script>
// ES5
var Shape = function(id, x, y) {
this.id = id,
this.move(x, y);
};
Shape.prototype.move = function(x, y) {
this.x = x;
this.y = y;
};
var Rectangle = function id(ix, x, y, width, height) {
Shape.call(this, id, x, y);
this.width = width;
this.height = height;
};
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var Circle = function(id, x, y, radius) {
Shape.call(this, id, x, y);
this.radius = radius;
};
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
// ES6
class Shape {
constructor(id, x, y) {
this.id = id;
this.move(x, y);
}
move(x, y) {
this.x = x;
this.y = y;
}
}
class Rectangle extends Shape {
constructor(id, x, y, width, height) {
super(id, x, y) this.width = width this.height = height;
}
}
class Circle extends Shape {
constructor(id, x, y, radius) {
super(id, x, y) this.radius = radius;
}
}
</script>
解构赋值
<script>
var obj = {
name: "kim",
age: 28,
c:undefined,
a: 1,
b: 2
};
var {a,b,...r} = obj;
console.log(r)
/*Object
age: 28
c: undefined
name: "kim"*/
//r 可以任意写,表示从除了a,b以外其他的属性值
//普通取值
var name = obj.name;
var age = obj.age;
console.log( name,age ); //kim 28
//对对象里的属性取值,但以下方式取出来的属性名仍然是对象里的属性名
var {name, age} = obj;
console.log( name,age ); //kim 28
var name = "";
({name} = obj);
console.log( name ); //kim
//以下方式可以赋值改名字
var { name: str } = obj;
console.log( str,name ); //kim kim
console.log( typeof str ); //string
//以下方式给默认值,即对象中没有或undefined,则使用默认值
var {c=100} = obj;
console.log(c); //100
//赋值并给默认值
var {gender:g="male"} = obj;
console.log(g); //male
//数组也与上面json一样
var arr = "miaov".split("");
var f = arr[0];
var s = arr[1];
console.log(f,s); //m i
//不取值可以写逗号
var [f,s,,fouth] = arr;
console.log(f,s,fouth); //m i o
arr[0] = undefined;
console.log(arr); // [undefined, "i", "a", "o", "v"]
//数组同样可以用默认值
var [ a="123456",b,c ] = arr;
console.log(a,b,c); //123456 i a
var [a="879",b,c,...r] = arr;
console.log( a,b,c,r ) //879 i a ["o", "v"]
</script>
函数
<script>
/* function add( a,b ){
||前面出现0与undefined时,会跳到后面
// var _b = b || 100;
var _b = b === undefined ? 100 : b;
return a+_b;
}*/
function add( a,b=100 ){
return a+b;
}
console.log( add( 10 ) ) // 110
console.log( add( 10,5 ) ) // 15
console.log( add( 10,0 ) ) // 10
console.log( add( 10,undefined ) ) // 110
//----------------------------------------------
function fn( a,b, ...r ){
// console.log( a,arguments );
console.log( r ); // [3, 4, 5]
}
fn(1,2,3,4,5)
/*function add(a,b){
return a+b
}*/
//箭头函数省略function,若是return输出,则可以省略return和{}
// var add = (a,b)=>{return a+b}
//若是return输出,则可以省略return和{}
// var add = (a,b)=>a+b
// 若想输出对象,则需加()
var add = (a,b)=>({miaov: 3})
console.log( add ); //(a,b)=>({miaov: 3})
//若没有加(),则会变成表达式标签
var add = (a,b)=>{
miaov: for( var i = 0;i<10;i++ ){
console.log( i )
if( i==3 ){
break miaov;
}
}
}
//若只有一个参数时,可以把(a)写成a,省略()。但如果没有传参数时,必须写()
var add = a=> a*100
console.log( add(2) )
//.filter => 过滤
var arr = [10,2,31,48,15,6];
// var res = arr.filter( function(item){
// return item > 20
// } );
var res = arr.filter( item=>item > 20 )
console.log( res )
//----------------------------------------------
//箭头函数可以用...
var fn = (...r)=>{
console.log( this ) //Window
// console.log( arguments );
//arguments is not defined
console.log( r ); //[MouseEvent]
}
//箭头函数,this在编写时已确定指向,不会因为谁调用而指向谁
/* var fn = function(){
console.log( this )
}*/
/* var fn = () =>{
console.log( this );
}
fn(1,2,3)
fn.bind({a:1}); .bind()依然无法修改this指向*/
document.onclick = fn;
//箭头函数不能new,没有arguments
// var obj = new fn();
</script>
arrow functions (箭头函数)
函数的快捷写法。不需要 function 关键字来创建函数,省略 return 关键字,继承当前上下文的 this 关键字
<script>
// ES5
var arr1 = [1, 2, 3];
var newArr1 = arr1.map(function(x) {
return x + 1;
});
// ES6
let arr2 = [1, 2, 3];
let newArr2 = arr2.map((x) => {
x + 1
});
</script>
箭头函数小细节:当你的函数有且仅有一个参数的时候,是可以省略掉括号的;当你函数中有且仅有一个表达式的时候可以省略{}
<script>
let arr2 = [1, 2, 3];
let newArr2 = arr2.map(x => x + 1);
</script>
JavaScript语言的this对象一直是一个令人头痛的问题,如setTimeout中的this指向的是全局对象。
<script>
class Animal {
constructor() {
this.type = 'animal';
}
says(say) {
setTimeout(function() {
console.log(this.type + ' says ' + say);
}, 1000);
}
}
var animal = new Animal();
animal.says('hi'); //undefined says hi
</script>
解决办法:
<script>
class Animal {
constructor() {
this.type = 'animal';
}
// 传统方法1: 将this传给self,再用self来指代this
says(say) {
var self = this;
setTimeout(function() {
console.log(self.type + ' says ' + say);
}, 1000);
}
// 传统方法2: 用bind(this),修改this指向,即
says(say) {
setTimeout(function() {
console.log(this.type + ' says ' + say);
}.bind(this), 1000);
}
// ES6: 箭头函数
// 当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象
says(say) {
setTimeout(() => {
console.log(this.type + ' says ' + say);
}, 1000);
}
}
var animal = new Animal();
animal.says('hi');
</script>
字符串
<script>
var str = "miaovketang";
console.log( str.indexOf("ke") !== -1 ); //true
//includes() => 检查字符串中是否包括
console.log( str.includes("zzz") ); //false
//startsWith() => 以()开头
console.log( str.startsWith("miao") ); //true
console.log( str.startsWith("iao") ); //false
//endsWith => 以()结尾
console.log( str.endsWith("tang") ); //true
console.log( str.endsWith("tan") ); //false
//repeat() => 重复打印()遍
var str = "miaovketang |";
console.log( str.repeat(3) ); //miaovketang |miaovketang |miaovketang |
console.log( str ); //miaovketang |
//``超级运算符,里面可传参${},可以回车,空格不会报错;还可以三目运算,调用函数
var classStr = "yellow"
var html = `
<ul>
<li class="red"></li>
<li class='green'></li>
<li class='${classStr}'></li>
</ul>`
console.log( html )
var n = 2;
var m = 3;
console.log( "n+m="+(n+m) )
console.log( `n+m=${n+m}` )
var isRaining = false
console.log( `今天天气是 ${ isRaining?"雨天":"晴天" }` )
function fn(){
return "miaov"
}
console.log( `fn执行结果是 : ${ fn() }` )
</script>
template string (模板字符串)
解决了 ES5 在字符串功能上的痛点。
第一个用途:字符串拼接。将表达式嵌入字符串中进行拼接,用 ` 和${}`来界定。
<script>
// es5
var name1 = "bai";
console.log('hello' + name1); //hellobai
// es6
const name2 = "ming";
console.log(`hello${name2}`); //helloming
</script>
第二个用途:在ES5时我们通过反斜杠来做多行字符串拼接。ES6反引号 `` 直接搞定。
<script>
// es5
var msg = "Hi \
man!";
// es6
const template = `<div>
<span>hello world</span>
</div>`;
console.log(msg); //Hi man!
console.log(template);
/*<div>
<span>hello world</span>
</div>*/
</script>
另外:includes repeat
<script>
// includes:判断是否包含然后直接返回布尔值
let str = 'hahah';
console.log(str.includes('y')); // false
// repeat: 获取字符串重复n次
let s = 'he';
console.log(s.repeat(3)); // 'hehehe'
</script>
destructuring (解构)
简化数组和对象中信息的提取。
ES6前,我们一个一个获取对象信息;
ES6后,解构能让我们从对象或者数组里取出数据存为变量
<script>
// ES5
var people1 = {
name: 'bai',
age: 20,
color: ['red', 'blue']
};
var myName = people1.name;
var myAge = people1.age;
var myColor = people1.color[0];
console.log(myName + '----' + myAge + '----' + myColor);
//bai----20----red
// ES6
let people2 = {
name: 'ming',
age: 20,
color: ['red', 'blue']
}
//解构
let { name, age } = people2;
let [first, second] = people2.color;
console.log(`${name}----${age}----${first}`);
//ming----20----red
</script>
default 函数默认参数
<script>
// ES5 给函数定义参数默认值
function foo(num) {
num = num || 200;
return num;
}
// ES6
function foo(num = 200) {
return num;
}
</script>
rest arguments (rest参数)
解决了 es5 复杂的 arguments 问题
<script>
//继承也可以用...
function foo(x, y, ...rest) {
console.log((x + y) * rest.length)
return ((x + y) * rest.length);
}
foo(1, 2, 'hello', true, 7); // 9
</script>
Spread Operator (展开运算符)
第一个用途:组装数组
<script>
let color = ['red', 'yellow'];
let colorful = [...color, 'green', 'blue'];
console.log(colorful); // ["red", "yellow", "green", "blue"]
</script>
第二个用途:获取数组除了某几项的其他项
<script>
let num = [1, 3, 5, 7, 9];
//解构
let [first, second, ...rest] = num;
console.log(rest); // [5, 7, 9]
</script>
对象
对象初始化简写
<script>
// ES5
function people(name, age) {
return {
name: name,
age: age
};
}
// ES6
function people(name, age) {
return {
//当键名与键值相同时,可省略
name,
age
};
}
</script>
对象字面量简写(省略冒号与 function 关键字)
<script>
// ES5
var people1 = {
name: 'bai',
getName: function () {
console.log(this.name);
}
};
// ES6
let people2 = {
name: 'bai',
getName () {
console.log(this.name);
}
};
</script>
另外:Object.assign()
ES6 对象提供了Object.assign()这个方法来实现浅复制。Object.assign()可以把任意多个源对象自身可枚举的属性拷贝给目标对象,然后返回目标对象。第一参数即为目标对象。在实际项目中,我们为了不改变源对象。一般会把目标对象传为{}
<script>
objA = {
a:1
}
objB = {
b:2
}
const obj = Object.assign({}, objA, objB)
console.log(obj) //{a: 1, b: 2}
// 给对象添加属性
this.seller = Object.assign({}, this.seller, response.data)
</script>
Promise
用同步的方式去写异步代码
<script>
// 发起异步请求
fetch('/api/todos')
.then(res => res.json())
.then(data => ({
data
}))
.catch(err => ({
err
}));
//Promise 封装异步请求demo
export default function getMethods (url){
return new Promise(function(resolve, reject){
axios.get(url).then(res => {
resolve(res)
}).catch(err =>{
reject(err)
})
})
}
getMethods('/api/xxx').then(res => {
console.log(res)
}, err => {
console.log(err)
})
</script>
<script>
// 异步编程解决方案
var p = new Promise( ( resolve,reject )=>{
console.log( "实例化..." ); //实例化...
return resolve("成功!");
reject();
console.log( "222" );
} );
console.log( p ) //Promise {<resolved>: "成功!"}
/* __proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "成功!"*/
p.then( ( data )=>{
console.log( "then: ",data ) //then: 成功!
} )
//主任务,隐式任务,队列
console.log( 1 );
setTimeout( ()=>{
console.log( 3 );
} )
setTimeout( ()=>{
console.log( 4 );
} )
var p = new Promise( (res,rej)=>{
console.log( 5 )
res()
} )
p.then( ()=>{
console.log( 6 )
} )
console.log( 2 );
//打印顺序:1,5,2 6 3,4
</script>
<script>
var p = new Promise( (res,rej)=>{
res("成功")
// rej();
} )
p.then( (data)=>{
console.log( data + "这里" ) //res表成功,会传递数据 =>成功这里
},()=>{
console.log( "失败" + "走这里" ) //rej表失败,不会传递数据 =>失败走这里
} )
// .catch是then的第二个参数,表失败
p.catch( ()=>{
console.log( "失败" + "来吧") //rej表失败,不会传递数据 =>失败走这里
} )
var p2 = p.then( (data)=>{
console.log( data + "在哪呢") //es表成功,会传递数据 =>成功在哪呢
} )
console.log( p2 === p ) //false
</script>
<script>
var p = new Promise( (res,rej)=>{
res("成功")
} )
var p2 = p.then( ()=>{ return new Promise( (res,rej)=>{rej(111)} ) } );
//Uncaught (in promise) 111
//返回状态为rejected,值为222,但报错,应用.catch方法
var p2 = p.catch( ()=>{ return new Promise( (res,rej)=>{rej(222)} ) } );
//返回状态为resolved,值为"成功",不是222
var p2 = p.then( ()=>{ return new Promise( (res,rej)=>{res(333)} ) } )
//返回状态为resolved,值为333
console.log( p2 )
p2.then( data=>{
console.log( "p2 then: ",data )
} )
</script>
<script>
var p = new Promise( (res,rej)=>{
res("成功")
} )
var res = p.then( ()=>{
console.log( 1 )
} )
.then( ()=>{
console.log( 2 )
//a
//不注释a此处出错,不会执行下个then,即不会打印出3
} )
.then( ()=>{
console.log( 3 )
b
} )
//捕获异常,不会报错
.catch( (err)=>{
console.log( err ) //ReferenceError: b is not defined
} )
//.finally结束语句
.finally( ()=>{
console.log( "执行完了..." ) //finally是无论程序对错,一定会走这里
} )
console.log( 333 )
/* 输出结果为:
333
1
2
3
ReferenceError: b is not defined
执行完了...*/
</script>
<script>
var p = new Promise( (res,rej)=>{res()} )
p
.then( ()=>{ console.log(1) } )
.then( ()=>{ return new Promise( (res,rej)=>{res(200)} ) } )
.then( (data)=>{ return Promise.resolve( data) }) //调用.resolve可省略new Promise( (res,rej)=>{res(...)} ),要么res,要么rej
.then( ()=>{ return Promise.reject( "失败!" ) }) //调用.reject可省略new Promise( (res,rej)=>{rej(...)} )
.then( (data)=>{ console.log(data+"跑这里了") } )
.catch( (err)=>{ console.log( err+"这里这里" ) } )
/* 打印结果:
1 200跑这里
打开Promise.reject("失败:")会覆盖.resolve这一状态,结果变成了
1 失败!这里这里*/
</script>
<script>
var p1 = new Promise( (res,rej)=>{
setTimeout( ()=>{
res("a")
},1000 )
} )
var p2 = new Promise( (res,rej)=>{
setTimeout( ()=>{
res("b")
},1500 )
} )
var p3 = new Promise( (res,rej)=>{
setTimeout( ()=>{
res("c")
},500 )
} )
// var p = Promise.all( [p1,p2,p3] ); //all()一起执行完再打印 ["a", "b", "c"]
var p = Promise.race( [p1,p2,p3] ); //.race()时间最快的 c
console.log( p ) //Promise {<pending>}
p.then( data=>console.log( data ) )
</script>
//promise运动实例
<script>
/* function move( obj,attr,target,duration,callback ){
var b = parseInt(getComputedStyle(obj)[attr]);
var c = target - b;
var d = duration;
var temp = new Date().getTime();
var timer = setInterval( function(){
var t = new Date().getTime()-temp;
if( t >= d ){
clearInterval( timer );
t = d;
}
var v = c/d*t+b ;
obj.style[attr] = v + "px";
if( t === d ){
typeof callback === "function" && callback();
}
},20 )
}
move( box,"width",200,500,()=>{
move( box,"height",300,500,()=>{
move( box,"left",300,500,()=>{
move( box,"top",200,500)
})
})
} )*/
var box = document.getElementById( 'box' );
function movePromise( obj,attr,target,duration ){
return new Promise( (res,rej)=>{
var b = parseInt(getComputedStyle(obj)[attr]);
var c = target - b;
var d = duration;
var temp = new Date().getTime();
var timer = setInterval( function(){
var t = new Date().getTime()-temp;
var v = c/d*t+b ;
obj.style[attr] = v + "px";
if( t >= d ){
clearInterval( timer );
res();
}
},20 )
} )
}
movePromise( box,"width",200,500 )
.then( ()=>movePromise( box,"height",300,1000 ) )
.then( ()=>movePromise( box,"left",300,1000 ) )
.then( ()=>movePromise( box,"top",200,1000 ) )
.then( ()=>console.log( "done" ) )
</script>
Generators
生成器( generator)是能返回一个迭代器的函数。
生成器函数也是一种函数,最直观的表现就是比普通的function多了个星号*,在其函数体内可以使用yield关键字,有意思的是函数会在每个yield后暂停。这里生活中有一个比较形象的例子。咱们到银行办理业务时候都得向大厅的机器取一张排队号。你拿到你的排队号,机器并不会自动为你再出下一张票。也就是说取票机“暂停”住了,直到下一个人再次唤起才会继续吐票。
迭代器:当你调用一个generator时,它将返回一个迭代器对象。这个迭代器对象拥有一个叫做next的方法来帮助你重启generator函数并得到下一个值。next方法不仅返回值,它返回的对象具有两个属性:done和value。value是你获得的值,done用来表明你的generator是否已经停止提供值。继续用刚刚取票的例子,每张排队号就是这里的value,打印票的纸是否用完就这是这里的done。
<script>
// 生成器
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
</script>
迭代器对异步编程作用很大,异步调用对于我们来说是很困难的事,我们的函数并不会等待异步调用完再执行,你可能会想到用回调函数,(当然还有其他方案比如Promise比如Async/await)。生成器可以让我们的代码进行等待。就不用嵌套的回调函数。使用generator可以确保当异步调用在我们的generator函数运行一下行代码之前完成时暂停函数的执行。
那么问题来了,咱们也不能手动一直调用next()方法,你需要一个能够调用生成器并启动迭代器的方法。就像这样子的:
<script>
function run(taskDef) {
// taskDef 即一个生成器函数
// 创建迭代器,让它在别处可用
let task = taskDef();
// 启动任务
let result = task.next();
// 递归使用函数来保持对 next() 的调用
function step() {
// 如果还有更多要做的
if (!result.done) {
result = task.next();
step();
}
}
// 开始处理过程
step();
}
</script>
js 中 Map/Set 集合
Map(一)
Map
是一组键值对的结构,具有极快的查找速度。
举个例子,假设要根据同学的名字查找对应的成绩,如果用Array
实现,需要两个Array
:
var names = ['Michael', 'Bob', 'Tracy'];
var scores = [95, 75, 85];
给定一个名字,要查找对应的成绩,就先要在names中找到对应的位置,再从scores取出对应的成绩,Array越长,耗时越长。
如果用Map实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,无论这个表有多大,查找速度都不会变慢。用JavaScript写一个Map如下:
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
初始化Map
需要一个二维数组,或者直接初始化一个空Map
。Map
具有以下方法:
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88
Set(一)
Set
和Map
类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set
中,没有重复的key。
要创建一个Set
,需要提供一个Array
作为输入,或者直接创建一个空Set
:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
重复元素在Set
中自动被过滤:
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
注意数字3
和字符串'3'
是不同的元素。
通过add(key)
方法可以添加元素到Set
中,可以重复添加,但不会有效果:
<script>
var s = new Set([1, 2, 3, 3, '3']);
s.add(4)
console.log( s )
/* 0: 1
1: 2
2: 3
3: "3"
4: 4
length: 5*/
s.add(4)
console.log( s )
//Set(5) {1, 2, 3, "3", 4}
</script>
通过delete(key)
方法可以删除元素
Map(二)
<script>
var obj = {
name: "miaov"
};
var momo = {
name: "m"
};
console.log(momo.toString()) //[object Object]
obj[momo] = 1;
console.log( obj ) //Object
/* [object Object]: 1
name: "miaov"*/
</script>
<script>
var m = new Map([ ["name","miaov"],["aaa",200] ]);
console.log( m.size ); //2
console.log( m.get( "name" ) ) //miaov
m.set( "age",100 );
var momo = { name: "m" };
m.set( momo,1 );
console.log( m ); //Map(4) {"name" => "miaov", "aaa" => 200, "age" => 100, {…} => 1}
var res = m.delete( momo );
var res = m.delete( "z" );
console.log( m ); //Map(3) {"name" => "miaov", "aaa" => 200, "age" => 100}
console.log( m.has( momo ) ); //false
console.log( m.has( "z" ) ); //false
m.forEach( function( item,key,map ){
console.log(item,key,map)
} )
/*miaov name Map(3) {"name" => "miaov", "aaa" => 200, "age" => 100}
200 "aaa" Map(3) {"name" => "miaov", "aaa" => 200, "age" => 100}
100 "age" Map(3) {"name" => "miaov", "aaa" => 200, "age" => 100}*/
var res = m.keys();
console.log( res.next() ); //{value: "name", done: false}
console.log( res.next() ); //{value: "aaa", done: false}
console.log( res.next() ); //{value: "age", done: false}
console.log( res.next() ); //{value: undefined, done: true}
var res = m.values();
console.log( res.next() ); //{value: "miaov", done: false}
console.log( res.next() ); //{value: 200, done: false}
console.log( res.next() ); //{value: 100, done: false}
console.log( res.next() ); //{value: undefined, done: true}
var res = m.entries();
console.log( res.next() ); //{value: Array(2), done: false}
console.log( res.next() ); //{value: Array(2), done: false}
console.log( res.next() ); //{value: Array(2), done: false}
console.log( res.next() ); //{value: undefined, done: true}
//两个不同的对象{}
m.set( {},2 );
m.set( {},3 );
console.log( m.get( {} ) ); //undefined
//后者会覆盖前者
var obj = {};
m.set( obj,2 );
m.set( obj,3 );
console.log( m.get( obj ) ); //3
</script>
set(二)
<script>
var s = new Set(["a","b","c"]);
/* console.log(s.size); //=> 对象的长度
s.add( 1 ).add( 2 ).add( 1 ); // => 不会添加相同的元素
//尽管 NaN === NaN 为false,还是不能重复添加NaN
s.add( NaN ).add( NaN );
console.log(s)
//删除元素
var res = s.delete( "b" );
//var res = s.delete( "z" ); //false
console.log( res ); //true
console.log( s ); //Set(2) {"a", "c"}
//判断是否存在某元素
// console.log(s.has("a"))
// console.log(s.has("x"))
//清除对象的元素
s.clear();
console.log( s ); //Set(0) {}*/
//forEach遍历对象里的key,value,整个对象
/* s.forEach( function(item,index,set){
console.log( item,index,set )
a a Set(3) {"a", "b", "c"}
b b Set(3) {"a", "b", "c"}
c c Set(3) {"a", "b", "c"}
} )*/
//此处keys与values相同
/* var keys = s.keys();
console.log( keys );
console.log( keys.next() ) //{value: "a", done: false}
console.log( keys.next() ) //{value: "b", done: false}
console.log( keys.next() ) //{value: "c", done: false}
console.log( keys.next() ) //{value: undefined, done: true}*/
/*var values = s.values();
console.log( values.next() ) //{value: "a", done: false}
console.log( values.next() ) //{value: "b", done: false}
console.log( values.next() ) //{value: "c", done: false}
console.log( values.next() ) //{value: undefined, done: true}*/
//整一个对象
/* var entries = s.entries();
console.log( entries.next() ) //{value: Array(2), done: false}
console.log( entries.next() ) //{value: Array(2), done: false}
console.log( entries.next() ) //{value: Array(2), done: false}
console.log( entries.next() ) //{value: undefined, done: true}
console.log( s ); //Set(3) {"a", "b", "c"}*/
var arr = [1,2,3,2,3,5,4,"1",NaN,NaN];
var s = new Set(arr);
console.log( s );
/* 0: 1
1: 2
2: 3
3: 5
4: 4
5: "1"
6: NaN
length: 7*/
//用以上方法去重
</script>
Synbol
<script>
//symbol => 加标记,独一无二,不会被覆盖
var s = Symbol( "a" );
console.log( typeof s ) //"symbol"
console.log( s ) // Symbol(a)
var s2 = Symbol( "b" );
console.log( s2 ) // Symbol(b)
console.log( s === s2 ); //false
var s3 = new Symbol(); //出错
//Symbol is not a constructor
//下面方法会覆盖原来的值
var obj = {
a: 1
}
obj.b = 2;
obj.a = 3;
console.log( obj ); //{a: 3, b: 2}
//Symbol("a") => 括号里的"a"只是标记
var a = Symbol("a");
obj[ a ] = "sa";
console.log( obj );
//{a: 3, b: 2, Symbol(a): "sa"}
var b = Symbol("a");
obj[ b ] = "sb";
console.log( obj )
//{a: 3, b: 2, Symbol(a): "sa", Symbol(a): "sb"}
for( var attr in obj ){
console.log( obj[attr] )
//3 2
}
//获取symbol值
console.log( Object.getOwnPropertySymbols( obj ) )
// [Symbol(a), Symbol(a)]
var s = Symbol();
// console.log( s + 1 );
//Cannot convert a Symbol value to a number
// console.log( s + "a" );
// Cannot convert a Symbol value to a string
// !! => 转布尔值
console.log( !!s ); //true
console.log( Boolean( s ) ); //true
</script>
遍历接口
<script>
/* var els = document.getElementsByTagName("*");
遍历所有 for( var attr of ele )
for( var attr of els ){
console.log( attr )
}*/
var arr = [10,20,30];
/*console.log( arr[Symbol.iterator] );
ƒ values() { [native code] }*/
var k = arr.keys();
console.log( k.next() ); //{value: 0, done: false}
console.log( k.next() ); //{value: 1, done: false}
console.log( k.next() ); //{value: 2, done: false}
console.log( k.next() ); //{value: undefined, done: true}
//模拟.keys()及.next()
/*var k = i( arr );
console.log( k.next() ); //{value: 0, done: false}
console.log( k.next() ); //{value: 1, done: false}
console.log( k.next() ); //{value: 2, done: false}
console.log( k.next() ); //{value: undefined, done: true}
function i( obj ){
var index = -1;
return {
next: function(){
index ++
return index < obj.length
? {
value: index,
done: false
}
:{
value: undefined,
done: true
}
}
}
}*/
</script>
对象
<script>
var a = 1;
var obj = {
a: a,
fn: function(){
console.log( "fn" )
}
}
//-------------------------------------------------
var obj = {
a, //此处为a:a省略,原因是属性值与外部变量相同时,可简写
fn(){
console.log( "fn执行了" )
},
b: 100
};
console.log( obj ) //{a: 1, fn: ƒ, b: 100}
obj.fn(); //fn执行了
//-------------------------------------------------
var attrname = "width";
var attrname = "height";
var attrObj = {};
var attrFn = "fn";
var obj = {
//解决键名可变问题
[attrname]: 100,
//对象不可以作为键名
[attrObj]: "hello",
//[attrFn]相当于fn
[attrFn](){
console.log( "attrFn执行了" )
}
}
console.log( obj ) //{height: 100, [object Object]: "hello", fn: ƒ}
obj.fn() //attrFn执行了
//-------------------------------------------------
console.log( 1 == "1" )//true
console.log( NaN === NaN )//false
console.log( NaN == NaN )//false
console.log( Object.is( 1,1 ) )//true
console.log( Object.is( 1,"1" ) )//false
console.log( Object.is( NaN,NaN ) )//true,解决了NaN不能判断的情况
console.log( Object.is( {},{} ) )//false,两个{}各自创建了新的空间
var obj = {};
console.log( Object.is( obj,obj ) )//true,同一对象obj
//--------------------------------------------------
//新的值传入改变默认值,旧方法
function move(obj){
var defaultObj = {
ease: "linear",
duration: 1000
}
/* var para = {
ease: obj.ease || defaultObj.ease,
duration: obj.duration || defaultObj.duration
}*/
//新的值传入改变默认值,新方法,Object.assign(),括号里的值越后面,等级越高,可以替换掉前面相同的属性值
var para = {}
Object.assign( para,defaultObj,obj )
console.log( para )
}
move({
duration: 2000
})
//-----------------------------------------------------
// for( var attr of Object.keys(obj) ) => 遍历对象键名。values => 键值,entries => 键值+键名
var obj = {
a: 1,
b: 2
}
console.log( Object.keys(obj) ) // ["a", "b"]
console.log( Object.values(obj) ) // [1, 2]
console.log( Object.entries(obj) ) // [Array(2), Array(2)]
// for( var attr of Object.values(obj) ){
// [key,val] => 解构赋值
for( var [key,val] of Object.entries(obj) ){
console.log( key,val )
/*a 1
b 2*/
}
//-------------------------------------------------------
var obj = {
a: 1,
b: 2
}
var res = {
// 类似继承 ...
...obj,
c: 3
}
console.log( res ) //{a: 1, b: 2, c: 3}
var arr = [1,2,3,4];
console.log( arr ); // [1, 2, 3, 4]
// console.log( 1,2,3,4 );
// ...可将数组值变成参数
console.log( ...arr ); //1 2 3 4
var arr = [1,23,4,5];
//=> 求数组的最大值
console.log( Math.max( 1,23,4,5 ) ); //23
console.log( Math.max( ...arr ) ); //23
var arr = [1,1,12,2,3,2,1,2];
//去重 => 先转化为Set对象,再用...将其转化为参数,然后在[]变为数组
var res = [...new Set(arr)];
console.log( res ); //[1, 12, 2, 3]
</script>
弱引用
<script>
var m = new Map();
var wm = new WeakMap();
var zm = {
name: "zmouse",
age: 30
};
var reci = {
name: "reci",
age: 20
}
//对象作为键值
m.set( zm,"miaov" );
m.set( reci,"miaov2" );
m.set( 1,"miao3" );
for( var v of m ){
console.log(v)
//[{…}, "miaov"] [{…}, "miaov2"] [1, "miao3"]
}
wm.set( zm,"miaov1" );
wm.set( reci,"miaov3" );
console.log( wm ); //WeakMap {{…} => "miaov3", {…} => "miaov1"}
//Map()为强引用,null清不了里面存进去的对象,m(a,b)中a可以不为对象。
//WeakMap()为弱引用不能使用for-of方法,可以用null清除对象,且wm(a,b)中a必须为对象。
/* for( var v of wm ){
console.log(v)
//wm is not iterable
}*/
</script>
数组
<script>
// new Array(10) => 创建长度为10的数组; Array.of(10) => 是添加元素为10
/* var arr = new Array(10,20,30) //(3) [10, 20, 30]
var arr = new Array(10) //[empty × 10]
var arr = Array.of(10,20,30) //(3) [10, 20, 30]
var arr = Array.of(10) //[10]
console.log( arr );
//document.all <=> document.getElementsByTagName("*")
var els = document.all
var els = document.getElementsByTagName("*")
console.log( els )
//HTMLCollection(6) [html, head, meta, title, body, script]
//Array.from => 转化为数组,Array.isArray() => 检查()是否为数组
var arrEls = Array.from( els );
console.log( Array.isArray( els ) ) //false
console.log( Array.isArray( arrEls ) ) //true */
var arr = [10,20,60,30,40,50];
var res = arr.find( function( item ){
console.log(item)
return item > 50
//.find => 当item>50时,跳出循环,返回对应值
} )
console.log( res )
//10 20 60 60
var res = arr.findIndex( function( item ){
console.log(item)
return item > 50
//.findIndex => 当item>50时,跳出循环,返回对应下标
} )
console.log( res )
//10 20 60 2
</script>