ECMAScript 6入门笔记

1. 作用域变量

作用域就是变量的作用范围。也就是你声明一个变量以后,这个变量可以在什么场合下使用。以前的JavaScript只有全局作用域,和函数作用域。

1.1 var的问题

1.var 没有块级作用域,定义后在当前包中都都可以访问,如果变量名重复,就会覆盖前面定义的变量,并且很可以被他人修改。

if(true){
    var a = "a"; //期望a是某一个值
}
console.log(a);

<br />2.var 在for循环标记变量共享,一般在循环中会使用的i会被共享,其本质也是由于没有块级作用域造成的<br />

for (var i = 0; i < 3; i++) {
     setTimeout(function () {
         alert(i);
     }, 0);
 }
 
 // 结果就是 弹窗三次 3
 
 for ( i = 0; i < 3; i++) {
     setTimeout(function () {
         alert(i);
     }, 0);
 }
 
 // 结果就是 弹窗三次 0-2

1.2 块级作用域

在用var定义变量的时候,变量通过闭包进行隔离的,现在用了let,不仅仅可以通过闭包隔离,还可以增加了一些块级作用域隔离。块级作用用一组大括号定义一个快,使用let定义的变量字啊大括号的外面是访问不到的。

1.2.1 实现会计作用域

if(ture){
    let name = 'wjh'
}

consloe.log('name'); // ReferenceError: name is not defined

1.2.2 不会污染全局对象

if(ture){
    let name = 'wjh'
}
console.log(window.name); // undefined

1.2.3 for 循环中也可以使用i

// 嵌套循环不会相互影响
for (let i = 0; i < 3; i++) {
   console.log("out", i);
   for (let i = 0; i < 2; i++) {
   console.log("in", i);
  }
}

// 结果 out 0 in 0 in 1 out 1 in 0 in 1 out 2 in 0 in 1

1.2.4 重复定义会报错

if(ture){
    let a = 1;
  let a = 2; //Identifier 'a'
}

1.2.5 不存在变量的预解释

for(let i = 0;i<2;i++){
    console.log('inner',i);
  let i =100;
}

// 结果 i is not defined

1.2.6 闭包的新写法

;(function(){

})();

现在

{

}

2. 常量

使用 const 我们可以声明一个常量,一旦声明之后,就不可以更改。

2.1 常量不能重新赋值

const MY_NAME = 'wjw';
MY_NAME = 'wjw2';//Assignment to constant variable

2.2 变量可以改变

注意const限制的是不能给变量重新赋值,而变量的值本身是可以改变的,下面的操作是可以的

const names = ['wjw1'];
names.push('wjw2');
console.log(names);

2.3 不同的块级作用域可以多次定义

const A = "0";
{
    const A = "A";
    console.log(A)
}
{
    const A = "B";
    console.log(A)
}
console.log(A)

// 结果 A B 0

3. 解构

3.1 解构数组

解构意思就是分解一个东西的结构,可以用一种类似数组的方式定义N个变量,可以将一个数组中的值按照规则赋值过去。<br />

var [name,age]=['wjh',8];
console.log(name,age);

3.2 嵌套赋值

let [x,[y],z]=[1,[2.1]];
console.log(x,y,z);

let [x,[y,z]] = [1,[2.1,2.2]];
console.log(x,y,z);

let [json,arr,num] = [{name:'wjw'},[1,2],3];
console.log(json,arr,num);

// 1 2.1 undefined 1 2.1 2.2 { name: 'wjw' } [ 1, 2 ] 3

3.3 省略赋值

let [,,x]=[1,2,3]
console.log(x);

3.4 解构对象

对象也可以被解构

var obj = {name:'wjw',age:8};
//对象里的name属性的值会交给name这个变量,age的值会交给age这个变量
var {name,age} = obj
//对象里的name属性的值会交给myname这个变量,age的值会交给myage这个变量
let {name: myname, age: myage} = obj;
console.log(name,age,myname,myage);

3.5 默认值

在赋值和传参的时候可以使用默认值

let [a='a',b='b',c=new Error('C必须指定')] = [1, , 3];
console.log(a,b,c);

function ajax(options){
    var method = options.method || "get";
  var data = options.data || {};
}

function ajax(method='get',data){
    console.log(arguments);
}

ajax({
    method:'post',
  data:{'name':'wjh'}
})

4. 字符串

4.1 模板字符串

模板字符串用反应号(数字1左边的那个建)包含,用${}括起来<br /><br />

var name = 'wjw',age = 8;
let desc = `${name} is ${age} old!`;
console.log(desc);

//所有模板字符串的空格和换行,都是被保留的
var str = `<ul>
<li>a</li>
<li>b</li>
</ul>`;
console.log(str);

其中的变量会用变量的值替换掉

function replace(desc){
    return desc.replace(/\$\{([^}]+)\}/g,function(matched,key){
    return eval(key);
  });
}

4.2 带标签的模板字符串

可以在模板字符串的前面添加一个标签,这个标签可以去处理模板字符串 标签其实就是一个函数,函数可以接收两个参数,一个是 strings 就是模板字符串里的每个部分的字符 还有一个参数可以使用rest的形式values,这个参数里面是模板字符串里的值。

var name = 'wjh',age = 8;
function desc(strings,...values){
    console.log(strings,values);
}
desc`${name} is ${age} old!`;

字符串新方法

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
var s = 'wjh';
s.startsWith('w') // true
s.endsWith('h') // true
s.includes('j') // true

第二个参数,表示开始搜索的位置

var s = 'wjh';
console.log(s.startsWith('j',2)); // true
console.log(s.endsWith('j',2)); // true
console.log(s.includes('j',2)); // false

endsWith的行为与其他其他方法有所不同。它针对前n个字符,而其他方法是从第几位开始到字符串结束

4.4 repeat

repeat 方法返回一个新字符串,表示将原字符串重复n次。<br /><br />

 'x'.repeat(3);
 'x'.repeat(0);

5. 函数

5.1 默认参数

可以给定义的函数接收的参数设置默认的值 在执行这个函数的时候,如果不指定函数的参数的值,就会使用参数的这些默认的值。

function ajax(url,method='GET',dataType="json"){
  console.log(url);
  console.log(method);
  console.log(dataType);
}

5.2 展开操作符

把...放在数组前面可以把一个数组进行展开,可以把一个函数而不需要使用apply<br /><br />

//传入参数
let print = function(a,b,c){
    console.log(a,b,c);
}
print([1,2,3]);
print(...[1,2,3]);

// 可以替代apply
var m1 = Math.max.apply(null, [8, 9, 4, 1]);
var m2 = Math.max(...[8, 9, 4, 1]);

// 可以替代concat
var arr1 = [1, 3];
var arr2 = [3, 5];
var arr3 = arr1.concat(arr2);
var arr4 = [...arr1, ...arr2];
console.log(arr3,arr4);

//类数组的转数组
function max(a,b,c) {
    console.log(Math.max(...arguments));
}
max(1, 3, 4);

5.3 剩余操作符

剩余操作符可以把其余参数的值放在一个叫做b的数组里

let rest = function(a,...rest){
    console.log(a,rest);
}
rest(1,2,3);

5.4 解构参数

let destruct = function({name,age}){
    console.log(name,age);
}
destruct({name:'wjh',age:10})

5.6 箭头函数

箭头函数简化了函数的定义方式

[1,2,3].forEach(val=>console.log(val));

输入参数如果多于一个要用()包含,函数体如果有多条语句需要用{}包起来

箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。 正是因为它没有this,从而避免了this指向的问题。

var person = {
    name:'wjh',
    getName:function(){
-        setTimeout(function(){console.log(this);},1000); //在浏览器执行的话this指向window
+        setTimeout(() => console.log(this),1000);//在浏览器执行的话this指向person
    }
}
person.getName();

6. 数组的新方法

// 相同的阵列
var people = [
  {
    name : 'Casper' ,
    like : '锅烧意面' ,
    age : 18
  },
  {
    name : 'Wang' ,
    like : '炒面' ,
    age : 24
  },
  {
    name : 'Bobo' ,
    like : '萝卜泥' ,
    age : 1
  },
  {
    name : '卤蛋' ,
    like : '萝卜泥' ,
    age : 3
  }
];

6.1 Array.prototype.filter()

filter() 会回传一个阵列,其条件是return 后方为true 的物件,很适合用在搜寻符合条件的资料。

var filterEmpty = people.filter( function ( item, index, array ) {
});
console.log(filterEmpty);     //没有条件,会是一个空阵列

var filterAgeThan5 = people.filter( function ( item, index, array ) {
  return item.age > 5 ;        //取得大于五岁的   如果这边符合条件 只要为ture即可
});
console .log(filterAgeThan5);    // Casper, Wang这两个物件

6.2 Array.prototype.find()

find()与filter()很像,但find() 只会回传一次值,且是第一次为true的值。

var findEmpty = people.find( function ( item, index, array ) {
});
console .log(findEmpty);           //没有条件,会是undefined

var findAgeThan5 = people.find( function ( item, index, array ) {
  return item.age > 5 ;            //取得大于五岁的
});

console .log(findAgeThan5);        //虽然答案有两个,但只会回传Casper这一个物件
var findLike = people.find( function ( item, index, array ) {
  return item.like === '萝卜泥' ;   //取得阵列like === '萝卜泥'
});
console .log(findLike);            //虽然答案有两个,但只会回传第一个Bobo物件

6.3 Array.prototype.forEach()

forEach 是这几个阵列函式最单纯的一个,不会额外回传值,只单纯执行每个阵列内的物件或值。

var forEachIt = people.forEach( function ( item, index, array ) {
  console .log(item, index, array); //物件,索引,全部阵列
  return item;                      // forEach没在return的,所以这边写了也没用
});

console .log(forEachIt);             // undefined

people.forEach( function ( item, index, array ) {
  item.age = item.age + 1 ;          // forEach就如同for,不过写法更容易
});
console .log(people);                //全部age + 1

6.4 Array.prototype.map()

使用map() 时他需要回传一个值,他会透过函式内所回传的值组合成一个阵列。
如果不回传则是 undefined
回传数量等于原始阵列的长度
这很适合将原始的变数运算后重新组合一个新的阵列。

var mapEmpty = people.map( function ( item, index, array ) {
});
console .log(mapEmpty);     // [undefined, undefined, undefined, undefined]

var mapAgeThan5 = people.map( function ( item, index, array ) {
  return item.age > 5 ;     //比较大于五岁的
});
console .log(mapAgeThan5); // [true, true, false, false]

var mapAgeThan5_2 = people.map( function ( item, index, array ) {
  // 错误示范
  if (item.age > 5 ) {
    return item;               //回传大于五岁的
  }
  return  false ;                //别以为空的或是false就不会回传
});
console .log(mapAgeThan5_2);    // [{name: 'Casper'...}, {name: 'Wang'...}, false, false]
var mapEat = people.map( function ( item, index, array ) {
  if (item.like !== '萝卜泥' ) {
    return  ` ${item.like}好吃` ;
  } else {
    return  ` ${item.like}不好吃` ;
  }
});
console .log(mapEat);           // ["锅烧意面好吃", "炒面好吃", "萝卜泥不好吃", "萝卜泥不好吃"]

6.5 Array.prototype.every()

every()可以检查所有的阵列是否符合条件,这仅会回传一个值trueor false,可以用来检查阵列中的内容是否符合特定条件。

var ans = array.every( function ( item, index, array ) {
  console .log(item, index, array); //物件,索引,全部阵列
  return item.age > 10  //当全部age大于10才能回传true
});
console .log(ans); // false:只要有部分不符合,则为false

var ans2 = array.every( function ( item, index, array ) {
  return item.age < 25
});
console .log(ans2); // true:全部age都小于25

6.6 Array.prototype.some()

some() 与every() 非常接近,都是回传true or false,差异仅在every() 需完全符合,some() 仅需要部分符合。

var ans = people.some( function ( item, index, array ) {
  return item.age > 10  //当全部age大于10才能回传true
});
console .log(ans);   // true:只要有部分符合,则为true

var ans2 = people.some( function ( item, index, array ) {
  return item.age < 25
});
console .log(ans2); // true:只要有部分符合,则为true  

var ans2 = people.some( function ( item, index, array ) {
  return item.age > 25
});
console .log(ans2); // false:全部都不符合则为false

6.7 Array.prototype.reduce()

reduce() 和其他几个差异就很大了,他可以与前一个回传的值再次作运算,参数包含以下:
accumulator: 前一个参数,如果是第一个阵列的话,值是以另外传入或初始化的值
currentValue: 当前变数
currentIndex: 当前索引
array: 全部阵列

var reduceEmpty = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
});
console .log(reduceEmpty);                  //没有条件,会是undefined

var reducePlus = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
  // 分别为前一个回传值, 目前值, 当前索引值
  console .log(accumulator, currentValue, currentIndex);
  return accumulator + currentValue.age;   //与前一个值相加
}, 0 );                                     //传入初始化值为0
console .log(reducePlus);                   //总和为46

var reducePlus = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
  console .log( 'reduce' , accumulator, currentValue, currentIndex)
  return  Math .max( accumulator, currentValue.age ); //与前一个值比较哪个大
}, 0 );
console .log(reducePlus);                   //最大值为24

7. 对象

7.1 对象字面量

如果你想在对象里添加跟变量名一样的属性,并且属性的值就是变量表示的值就可以直接在对象里加上这些属性

let name = 'wjh';
let age = 8;
let getName = function(){
        console.log(this.name)
}
let person ={
        name,
    age,
    getName
}
person.getName();

7.2 Object.is

对比两个值是否相等

console.log(Object.is(NaN,NaN));

7.3 Object.assign

把多个对象的属性复制到一个对象中,第一个参数是复制的对象,从第二个参数开始往后,都是复制的源对象

var nameObj = {name:'wjh'}
var ageObj = {age:8};
var obj = {};
Object.assign(obj,nameObj,ageObj);
console.log(obj);

//克隆对象
function clone(obj){
    return Object.assgin({},obj);
}

7.4 Object.setPrototypeOf

将一个指定的对象原型设置为另一个对象或者null

var obj1 = {name:'wjh1'};
var obj2 = {name:'wjh2'};
var obj = {};
Object.setPrototypeOf(obj,obj1);
console.log(obj.name);
console.log(Object.getPrototypeOf(obj));
Object.setProtoypeOF(obj,obj2);
console.log(obj.name);
console.log(Object.getPrototypeOf(obj));

7.5 proto

直接对象表达式中设置prototype

var obj1 = {name:'wjh'};
var obj3 = {
    _proto_:obj1
} 
console.log(obj3.name);
console.log(Object.getPrototypeOf(obj3));

7.6 super

通过super可以调用protype上的属性或方法

let person ={
    eat(){
    return 'milk';
  }
}
let student = {
    _proto_:person,
  eat(){
    return super.eat()+'bead'
  }
}
console.log(student.eat());

8. 类

8.1 class

使用 class 这个关键词定义一个类,基于这个创建实例以后就会创建 constructor 方法,此方法可以用来初始化

class Person{
    constructor(name){
    this.name = name;
  }
  
  getName(){
        console.log(this.name)  
  }

}

let person = new Person('wjh');
person.getName();

8.2 get与set

getter 可以用来获取属性,setter 可以去设置属性

class Person {
    constructor(){
    this.hobbies = [];
  }
  set hobby(hobby){
    this.hobbies.push(hobby);
  }
  get hobby(){
    return this.hobbies;
  }
}
let person = new Person();
person.hobby = 'aa';
person.hobby = 'bb';
console.log(person.hobby)

8.3 静态方法-static

在类里面添加静态的方法可以使用static 这个关键词,静态方法就是不需要实例化类就能使用的方法

class Person{
    static add(a,b){
    return a+b;
  }
}
console.log(Person.add(1,x));

8.4 继承extends

一个类可以继承其他的类里的东西

class Person{
    constructor(name){
    this.name = name;
  }
}

class Teacher extends Person{
    constructor(name,age){
    super(name);
    this.age = age;
  }
}

var teacher = Teacher('wjh',8);
console.log(teacher.name,teacher.age)

9. 生成器(Generator)与迭代器(Iterator)

Generator 是一个特殊的函数,执行它会返回一个Iterator对象。通过遍历迭代器,Generator函数运行后悔返回遍历器对象,而不是函数的返回值。

9.1 Iterators模拟

迭代器有一个next方法,每次执行的时候会返回一个对象 对象里面有两个函数,一个是value表示返回的值,还有就是布尔值done,表示是迭代完成

function buy(books){
  let i = 0;
  return{
    next(){
      let done = i ===books.length;
      let value = !done ? books[i++]:undefined;
      return {
        value:value,
        done:done
      }
    }
  }
}

let iterators = buy(['js','html']);
var curr;
do{
    curr = iterators.next();
  console.log(curr);
}while(!curr.done);

9.3 Generators

生成器用于创建迭代器

function* buy(boos){
    for(var i=0;i<boos.length;i++){
        yield books[i];
  }
}
let buying = buy(['js','html]);
var curr;
do {
    curr = buying.next();
  console.log(curr);
}while(!curr.done);

10. 集合

10.1 Set

一个Set是一堆东西的集合,Set 有点像数组,不过跟数组不一样的是,Set里面不能有重复的内容

var books = new Set();
books.add('js');
books.add('js');//添加重复元素的集合元素个数不会变化
books.add('html');
books.forEach(function(book){ // 循环集合
    console.log(book);
})
console.log(book.size);//集合中元数的个数
console.log(books.has('js'));//判断集合是否有此元素
books.delete('js');
console.log(books.size);
console.log(books.has('js'));
books.clear();//清空set
console.log(books.size);

10.2 Map

可以使用Map来组织这个名值对的数据

var books = new Map();
books.set('js',{name:'js'});//向map中添加元素
books.set('html',{name:'html'});
console.log(books.size);//查看集合中的元素
console.log(books.get('js'));//通过key获取值
books.delete('js');//执行key删除元素
console.log(books.has('js'));//判断map中有没有key
book.forEach((value,key)=>{
    console.log(key+'='+value);
})
books.clear();//清空map

11. 模块

可以根据应用的需求吧代码分成不同的模块,每个模块里可以导出它需要让其他模块使用的东西,在其他模块里面可以导入这些模块,导出的东西。

11.1 模块

在浏览器中使用模块需要借助 导出

export var name = 'wjh';
export var age = 8;

导入

//import {name,age} from './school.js';
import * as school from './school.js';
console.log(school.name,school.age);

在页面中引用

<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/bin/BrowserSystem.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<script type="module" src="index.js"></script>

11.2 重命名

导出时重命名

function say(){
    console.log('say');
}
export {say as say2};

导入时重命名

import {say2 as say3} from './school.js'

11.3 默认导出

每个模块都可以有一个默认要导出的东西

export default function say(){
    console.log('say')
}

导入

import say from './school.js'

11.4 深度克隆

var parent = {
  age: 5,
  hobby: [1, 2, 3],
  home: {city: '北京'},
};

var child = extendDeep(parent);
child.age = 6;
child.hobby.push('4');
child.home.city = '广东';
console.log('child ', child); //[1, 2, 3, 4]
console.log('parent ', parent);
function extend(parent) {
  let child;
  if (Object.prototype.toString.call(parent) == '[object Object]') {
    child = {};
    for (let key in parent) {
      child[key] = extend(parent[key])
    }
  } else if (Object.prototype.toString.call(parent) == '[object Array]') {
    child = parent.map(item => extend(item));
  } else {
    return parent;
  }
  return child;
}

function extendDeep(parent, child) {
  child = child || {};
  for (var key in parent) {
    if (typeof parent[key] === "object") {
      child[key] = (Object.prototype.toString.call(parent[key]) === "[object Array]") ? [] : {};
      extendDeep(parent[key], child[key]);
    } else {
      child[key] = parent[key];
    }
  }
  return child;
}

12.JavaScript(ES6) 中条件语句

12.1 使用 Array.includes 来处理多个条件

function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red');
  }
}

优化变成 ->>

function test(fruit) {
  // 条件提取到数组中
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}

12.2 减少嵌套,提前使用 return 语句

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  // 条件 1:fruit 必须有值
  if (fruit) {
    // 条件 2:必须为红色
    if (redFruits.includes(fruit)) {
      console.log('red');
 
      // 条件 3:数量必须大于 10
      if (quantity > 10) {
        console.log('big quantity');
      }
    }
  } else {
    throw new Error('No fruit!');
  }
}
 
// 测试结果
test(null); // 抛出错误:No fruits
test('apple'); // 打印:red
test('apple', 20); // 打印:red,big quantity

优化

/* 在发现无效条件时提前 return */
 
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  // 条件 1:提前抛出错误
  if (!fruit) throw new Error('No fruit!');
 
  // 条件2:必须为红色
  if (redFruits.includes(fruit)) {
    console.log('red');
 
    // 条件 3:数量必须大于 10
    if (quantity > 10) {
      console.log('big quantity');
    }
  }
}

为了减少一个嵌套层级,优化编码风格

/* 在发现无效条件时提前 return */
 
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  if (!fruit) throw new Error('No fruit!'); // 条件 1:提前抛出错误
  if (!redFruits.includes(fruit)) return;  // 条件 2:当 fruit 不是红色的时候,提前 return
 
  console.log('red');
 
  // 条件 3:必须是大量存在
  if (quantity > 10) {
    console.log('big quantity');
  }
}

12.3.使用函数的默认参数 和 解构

function test(fruit, quantity) {
  if (!fruit) return;
  const q = quantity || 1; // 如果没有提供 quantity 参数,则默认为 1
 
  console.log(`We have ${q} ${fruit}!`);
}
 
// 测试结果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

但是q在这边不直观所有优化

function test(fruit, quantity = 1) { // i如果没有提供 quantity 参数,则默认为 1
  if (!fruit) return;
  console.log(`We have ${quantity} ${fruit}!`);
}
 
// 测试结果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

但是这边 也可能是个对象

// 解构 —— 只获得 name 属性
// 参数默认分配空对象 {}
function test({name} = {}) {
  console.log (name || 'unknown');
}
 
//测试结果
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

12.4 选择 Map / Object 字面量,而不是Switch语句

function test(color) {
  // 使用 switch case 语句,根据颜色找出对应的水果
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}
 
//测试结果
test(null); // []
test('yellow'); // ['banana', 'pineapple']

这边建议使用对象,更加清晰

// 使用对象字面量,根据颜色找出对应的水果
  const fruitColor = {
    red: ['apple', 'strawberry'],
    yellow: ['banana', 'pineapple'],
    purple: ['grape', 'plum']
  };
 
function test(color) {
  return fruitColor[color] || [];
}

但是这边是很有可能为网络数据,无法判断red这样的变量,那么就用arry.filter 来过滤

const fruits = [
    { name: 'apple', color: 'red' }, 
    { name: 'strawberry', color: 'red' }, 
    { name: 'banana', color: 'yellow' }, 
    { name: 'pineapple', color: 'yellow' }, 
    { name: 'grape', color: 'purple' }, 
    { name: 'plum', color: 'purple' }
];
 
function test(color) {
  // 使用 Array filter  ,根据颜色找出对应的水果
 
  return fruits.filter(f => f.color == color);
}

12.5 使用 Array.every 和 Array.some 来处理全部/部分满足条件

我们想检查所有水果是否都是红色的

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];
 
function test() {
  let isAllRed = true;
 
  // 条件:所有的水果都必须是红色
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color == 'red');
  }
 
  console.log(isAllRed); // false
}

使用 arry.every来过滤

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];
 
function test() {
  // 条件:简短方式,所有的水果都必须是红色
  const isAllRed = fruits.every(f => f.color == 'red');
 
  console.log(isAllRed); // false
}

如果我们想要检查是否有至少一个水果是红色的,我们可以使用 Array.some 仅用一行代码就实现出来

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
];
 
function test() {
  // 条件:是否存在红色的水果
  const isAnyRed = fruits.some(f => f.color == 'red');
 
  console.log(isAnyRed); // true
}

13 promise

13.1 异步回调

13.1.1 回调地狱

在需要多个操作的时间,会导致多个回调函数嵌套,导致代码不够直观,就常说的回调地狱

13.1.2 并行结果

如果几个异步操作之间并没有前后顺序之分,但需要等多个异步完成操作完成后才能执行后续的任务,无法实现并行节约时间

13.2 Promise

promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果。什么时间会用到过一段时间?答案是异步操作,异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等

13.3 Promise的三种状态

  • Pending Promise对象势力创建时候的初始化状态
  • Fulfilled 可以理解为成功的状态
  • Rejected 可以理解为失败的状态

then方法就是用来指定Promise 对象的状态改变时确定执行的操作,resolve时执行第一个函数(onFulfilled),reject时执行第二函数(onRejected)

13.4 构造一个Promise

13.4.1 使用Promise

let promise = new Promise((resolve,reject)=>{
        setTimeout(()=>{
        if(Math.random()>0.5)
        resolve('This is resolve!')
      else
        reject('This is reject')
    },1000);
});
promise.then(Fulfilled,Rejected)
  • 构造一个Promise实例需要给Promise构造函数传入一个函数
  • 传入的函数需要有两个形参,两个形参都是function类型的参数。
    • 第一个形参运行后会让Promise实例处于resolve状态,所以我们一般给第一个形参命名为resolve,使 Promise对象的状态改变成成功,同时传递一个参数用于后续成功后的操作
    • 第一个形参运行后悔让Promise实例处于reject状态,所以我们一般给第一个形参命名为reject,将Promise对象的状态改变为失败,同事将错误的信息传递到后续错误处理的操作

13.4.2 es5模拟Promise

function Promise(fn){
    this.success(data);
},(error)=>{
    this.error();
}

Promise.prtotype.resolve = function (data){
    this.success(data);
}

Promise.prototype.then = function (success,error){
    this.success = success;
    this.error = error;
}

13.4.3 es5模拟Promise

class Promise{
    constructor(fn){
        fn((data)=>{
        this.success(data);
      },(error)=>{
        this.error();
      })
  }
  
  resolve(data){
    this.success(data);
  }
  
  reject(error){
    this.error(error);
  }
  
  then(success,error){
    this.success = success;
    this.error = error;
    console.log(this);
  }

}

13.5 promise 做为函数的返回值

function ajaxPromise(queryUrl){
    return new Promise((resolve,reject)=>{
    xhr.open('GET',queryUrl,ture);
    xhr.send(null);
    xhr.onreadystatechange = () =>{
            if(xhr.readyState === 4 ){
            if(xhr.status === 200){
            resolve(xhr.responseText);
          }else{
            reject(xhr.responseText);
          }
        }
    }
  })
}


ajaxPromise('http://www.baidu.com')
    .then((value)=>{
    console.log(value);
  })
  .catch((err)=>{
    console.error(err);
 });

13.6 promise的链式调用

  • 每次调用返回的都是一个新的Promise实例
  • 链式调用的参数通过返回值传递

then 可以使用链式调用的写法原因在于,每一次执行该方法时总是会返回一个 Promise 对象

readFile('1.txt').then(function(data){
    console.log(data);
}).then(function (data){
    console.log(data);
  return readFile(data);
}).then(function (data){
    console.log(data);
}).catch(function (err){
    console.log(err);
})

13.7 promise API

13.7.1 Promise.all

  • 参数:接受一个数组,数组内都是Promise实例
  • 返回值: 返回一个 promise 实例,这个promise 实例的状态转移取决于参数的 promise实例的状态变化。当参数处于resolve状态时,返回resolve状态。如果参数中任意一个实例处于reject状态,返回的promise实例变为reject状态。
Promise.all([p1,p2]).then(function(result){
    console.log(result); //[ '2.txt', '2' ]
})

不管两个promise谁先完成,Promise.all 方法会按照数组里面的顺序将结果返回

13.7.2 Promise.race

  • 参数:接受一个数组,数组内都是Promise实例
  • 返回值: 返回一个 promise 实例,这个promise 实例的状态转移取决于参数的 promise实例的状态变化。当参数处于resolve状态时,返回resolve状态。如果参数中任意一个实例处于reject状态,返回的promise实例变为reject状态。
Promise.race([p1,p2]).then(function(result){
    console.log(result); //[ '2.txt', '2' ]
})

13.7.3 Promise.resolve

返回一个Promise 实例,这个实例处于resolve状态。<br />根据传入的参数不同有不同的功能:

  • 值(对象、数组、字符串等):作为resolve传递出去的值
  • Promise 实例 : 原封不动返回

Promise.reject

返回一个Promise实例,这个实例处于reject状态

  • 参数一般就是抛出的错误信息。

13.8 q

Q是一个在Javascrip中实现promise的模块

13.8.1 q的基本用法

var Q = require('q');
var fs = require('fs');
function read(filename){
    var deferred = Q.defer();
  fs.readFile(filename,'utf8',function)(err,data){
    if(err){
        deferred.reject(err);
    }else{
        deferred.resolve(data);
    }
  });
}

read('1.txt1').then(function(data){
    console.log(data);
},funtcion(error){
    console.error(error);                    
})

13.8.2 q的简单实现

module.exports = {
    defer(){
        var _success,_error;
        return {
            resolve(data){
                _success(data);
            },
            reject(err){
                _error(err);
            },
            promise:{
                then(success,error){
                    _success = success;
                    _error = error;
                }
            }
        }
    }
}

13.8.3 q的实现

var defer = function () {
    var pending = [], value;
    return {
        resolve: function (_value) {
            if (pending) {
                value = _value;
                for (var i = 0, ii = pending.length; i < ii; i++) {
                    var callback = pending[i];
                    callback(value);
                }
                pending = undefined;
            }
        },
        promise: {
            then: function (callback) {
                if (pending) {
                    pending.push(callback);
                } else {
                    callback(value);
                }
            }
        }
    };
};

13.9 bluebird

实现 promise 标准的库是功能最全,速度最快的一个库

13.9.1 bluebird经典使用

var Promise = require('./bluebird');

var readFile = Promise.promisify(require("fs").readFile);
readFile("1.txt", "utf8").then(function(contents) {
    console.log(contents);
})

var fs = Promise.promisifyAll(require("fs"));

fs.readFileAsync("1.txt", "utf8").then(function (contents) {
    console.log(contents);
})

13.9.2 bluebird简单实现

module.exports = {
    promisify(fn){
        return function () {
            var args = Array.from(arguments);
            return new Promise(function (resolve, reject) {
                fn.apply(null, args.concat(function (err) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(arguments[1])
                    }
                }));
            })
        }
    },
    promisifyAll(obj){
        for(var attr in obj){
            if(obj.hasOwnProperty(attr) && typeof obj[attr] =='function'){
                obj[attr+'Async'] = this.promisify(obj[attr]);
            }
        }
        return obj;
    }
}

13.10 动画

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>move</title>
    <style>
        .square{
            width:40px;
            height:40px;
            border-radius: 50%;
        }
        .square1{
            background-color: red;
        }
        .square2{
            background-color: yellow;
        }
        .square3{
            background-color: blue;
        }
    </style>
</head>
<body>
<div class="square square1" style="margin-left: 0"></div>
<div class="square square2" style="margin-left: 0"></div>
<div class="square square3" style="margin-left: 0"></div>
</body>
<script>
  var square1 = document.querySelector('.square1');
  var square2 = document.querySelector('.square2');
  var square3 = document.querySelector('.square3');

  /*function move(element,target,resolve){
    let timer = setInterval(function(){
      var marginLeft = parseInt(element.style.marginLeft, 10);
      if(marginLeft == target){
        resolve();
      }else{
        element.style.marginLeft = ++marginLeft+'px';
      }
    },13);
  }*/
  function move(element,target,resolve){
    let current = 0;
    let timer = setInterval(function(){
      element.style.transform=`translateX(${++current}px)`;
      if(current>target){
        clearInterval(timer);
        resolve();
      };
    },13);
  }
  function animate(element,target){
    return new Promise(function(resolve,reject){
      move(element,target,resolve);
    });
  }
  animate(square1,100)
    .then(function(){
      return animate(square2,100);
    })
    .then(function(){
      return animate(square3,100);
    });
</script>
</html>

13.11. co

13.11.1 co初体验

let fs = require('fs');
function getNumber(){
  return new Promise(function (resolve,reject) {
    setTimeout(function(){
      let number = Math.random();
      if(number >.5){
        resolve(number);
      }else{
        reject('数字太小');
      }
    },1000);
  });
}
function *read(){
  let a = yield getNumber();
  console.log(a);
  let b = yield 'b';
  console.log(b);
  let c = yield getNumber();
  console.log(c);
}

function co(gen){
  return new Promise(function(resolve,reject){
    let g = gen();
    function next(lastValue){
      let {done,value} = g.next(lastValue);
      if(done){
         resolve(lastValue);
      }else{
        if(value instanceof Promise){
          value.then(next,function(val){
            reject(val);
          });
        }else{
          next(value);
        }
      }
    }
    next();
  });
}
co(read).then(function(data){
  console.log(data);
},function(reason){
  console.log(reason);
});

13.11.2 co连续读文件

let fs = require('fs');
function readFile(filename){
  return new Promise(function (resolve,reject) {
    fs.readFile(filename,'utf8',function(err,data){
      if(err)
        reject(err);
      else
        resolve(data);
    })
  });
}
function *read(){
  let a = yield readFile('./1.txt');
  console.log(a);
  let b = yield readFile('./2.txt');
  console.log(b);
}

function co(gen){
  let g = gen();
  function next(val){
    let {done,value} = g.next(val);
    if(!done){
      value.then(next);
    }
  }
  next();
}

13.12 Promise/A+完整实现

function Promise(executor) {
  let self = this;
  self.status = "pending";
  self.value = undefined;
  self.onResolvedCallbacks = [];
  self.onRejectedCallbacks = [];
  function resolve(value) {
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    setTimeout(function () { // 异步执行所有的回调函数
      if (self.status == 'pending') {
        self.value = value;
        self.status = 'resolved';
        self.onResolvedCallbacks.forEach(item => item(value));
      }
    });

  }

  function reject(value) {
    setTimeout(function () {
      if (self.status == 'pending') {
        self.value = value;
        self.status = 'rejected';
        self.onRejectedCallbacks.forEach(item => item(value));
      }
    });
  }

  try {
    executor(resolve, reject);
  } catch (e) {
    reject(e);
  }
}
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('循环引用'));
  }
  let then, called;

  if (x != null && ((typeof x == 'object' || typeof x == 'function'))) {
    try {
      then = x.then;
      if (typeof then == 'function') {
        then.call(x, function (y) {
          if (called)return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, function (r) {
          if (called)return;
          called = true;
          reject(r);
        });
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called)return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}
Promise.prototype.then = function (onFulfilled, onRejected) {
  let self = this;
  onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : function (value) {
    return value
  };
  onRejected = typeof onRejected == 'function' ? onRejected : function (value) {
    throw value
  };
  let promise2;
  if (self.status == 'resolved') {
    promise2 = new Promise(function (resolve, reject) {
      setTimeout(function () {
        try {
          let x = onFulfilled(self.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });

    });
  }
  if (self.status == 'rejected') {
    promise2 = new Promise(function (resolve, reject) {
      setTimeout(function () {
        try {
          let x = onRejected(self.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    });
  }
  if (self.status == 'pending') {
    promise2 = new Promise(function (resolve, reject) {
      self.onResolvedCallbacks.push(function (value) {
        try {
          let x = onFulfilled(value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
      self.onRejectedCallbacks.push(function (value) {
        try {
          let x = onRejected(value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    });
  }
  return promise2;
}
Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
}
Promise.all = function (promises) {
  return new Promise(function (resolve, reject) {
    let result = [];
    let count = 0;
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(function (data) {
        result[i] = data;
        if (++count == promises.length) {
          resolve(result);
        }
      }, function (err) {
        reject(err);
      });
    }
  });
}

Promise.deferred = Promise.defer = function () {
  var defer = {};
  defer.promise = new Promise(function (resolve, reject) {
    defer.resolve = resolve;
    defer.reject = reject;
  })
  return defer;
}
/**
 * npm i -g promises-aplus-tests
 * promises-aplus-tests Promise.js
 */
try {
  module.exports = Promise
} catch (e) {
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容