工作生活

ES6 常用函数 记一次分享内容

2019-07-04  本文已影响0人  _一九九一_

ES6 新特性

简介:

  1. ECMAScript 6.0(以下简称 ES6)是 JavaScript 新一代标准,加入了很多新的功能和语法
  2. ECMAScript 和 JavaScript 的关系是:前者是后者的规格,后者是前者的一种实现

var let const

var

1. ES5 中作用域有:全局作用域、函数作用域。
// 全局作用域
var a = 123;
function test() {
  console.log(a);     
}
test();  // 123
//函数作用域
function test() {
  var a = 123;
}
test(); 
console.log(a);   // ReferenceError
// 不用var  全局定义
var z = 'n';
function test() {
  z = 'z';        
}
test(); 
console.log(z);     // z


var z = 'n';
function test() {
  var z = 'z';
}
test(); 
console.log(z);    // n
2. var声明的变量会挂载在window上。容易引起变量污染。
// i只是用来控制循环, 但泄露为全局变量 
for (var i = 0; i < 3; i++) { }    
console.log(window.i);  // 3
3. 变量提升
var a = 123;
function test() {
  console.log(a);         // a在声明前打印了  
  if(false) {           
    var a = 'abc';      // 变量提升,导致内层的 a 变量覆盖了外层的 a 变量
  }
}
test();  // undefined

let

ES6 中新增了块级作用域概念。

1. 声明的变量只在块级作用域内有效
 {  
   let i = 1;  
   { let i = 2; }      
 }  

if( ) { }

for( ) { }

2. 不存在变量提升(或者说存在变量提升,但不赋值),而是“绑定”在暂时性死区
var test = 1;
function func(){
  console.log(test);   
  let test = 2;     
};
func(); 
3. 不能重复声明
{
   let a = 1;
   let a = 2;
}
// 报错

{
  let a = 10;
  var a = 1;
}
//报错

const

1. const一旦声明变量,就必须立即初始化
const foo;  // 报错
2. 只在声明所在的块级作用域内有效。
{
  const MAX = 5;
}
console.log(MAX)   //报错
3. 同样存在暂时性死区,只能在声明的位置后面使用。
{
  console.log(MAX); // 报错
  const MAX = 5;
}
4. 不能重复声明。
var message = 'Hello';
const message = 'Goodbye!';

let age = 25;
const age = 30;
5. const声明一个简单数据类型。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415

PI = 3;  // 报错
6. const 定义的复合类型的数据可以进行修改:
const foo = { prop:1 };
foo.prop = 2;   // 只改了数据结构,成功
foo = {};       // 改变了指针指向,报错
const a = [];
a.push('Hello');    // 不改动指针指向,只改了数据结构
a = ['lily'];      // 改变了指针指向,报错

编程风格

  1. 建议不再使用var命令,let命令取代。两者语义相同,而且let没有副作用(全局污染,变量提升)
  2. 在let和const之间,建议优先使用const,一是const比较符合函数式编程思想,我不改变值,只新建值。二是JavaScript 编译器会对const进行优化,提高运行效率
  3. const声明常量还有两个好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是万一修改了会报错
  4. 所有的函数都应该设置为常量。

二、解构赋值

1. 数组的解构
let [a, b, c] = [1, 2, 3];  


// 不完全解构:
let [x, y] = [1, 2, 3]; 


// 嵌套数组进行解构
let [foo, [[bar], baz]] = [1, [[2], 3]]; 


// 只取第三个
let [ , , third] = ['foo', 'bar', 'baz']; 


// 中间的不取
let [x, , y] = [1, 2, 3];


// 取第一个  取剩余的 
let [head, ...other] = [1, 2, 3, 4];
head 
other  
 

let [...other, last] = [1, 2, 3, 4];  //  报错:Rest element must be last element
2. 对象的解构
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };

const { log, info, table, warn } = console; 
 
3. 解构不成功系列:
let [foo] = [];   
// foo: undefined 

 
// 如果等号的右边 是不可遍历的结构    
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {}; 
// 都将报错



// 不同名
let { abc } = { foo: 'aaa', bar: 'bbb' };
// abc: undefined
4. 用途:
// 交换变量的值  
let x = 1;
let y = 2; 
[x, y] = [y, x];


// 函数 return 数组 对象  进行解构
function getArr() {
  return [1, 2, 3];
}
let [a, b, c] = getArr();


function getObj() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();
 

// 传参:参数有次序的
function f([x, y, z]) { console.log(x,y,z) }
f([1, 2, 3]);

// 无序 也会对应着解构
function f({x, y, z}) { console.log(x,y,z) }
f({z: 3, y: 2, x: 1});
 

// 提取接口返回的 JSON 数据
const jsonData = {
  id: 42,
  status: 'OK',
  data: [867, 5309]
};
const { data } = jsonData;


import React, { Component } from 'react';
class SomeComponent extends Component { ... }
 

编程风格

函数的参数如果是对象的成员,优先使用解构赋值。

const user = {
    firstName:'firstName',
    lastName:'lastName'
}

// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;
}

// good
function getFullName(obj) {
  const { firstName, lastName } = obj;
}

// best
function getFullName({ firstName, lastName }) { }

getFullName(user);

三、模板字符串

传统的 JavaScript 语言,输出模板通常是这样写的。

const basket = {
  count:123,
  onSale:20
}
 let str = 'There are ' + basket.count +  
  'items in your basket, ' +
  + basket.onSale +
  ' are on sale!';

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题:

1. 反引号(`)标识 , 变量名写在 ${ } 之中。
const basket = {
  count:123,
  onSale:20
}
let str = `
  There are  ${basket.count}  items
   in your basket,  ${basket.onSale} 
  are on sale!
`
2. 进行运算
let obj = {x: 1, y: 2};

`${obj.x + obj.y}`
3. 调用函数
function fn() {
  return 'and';
}

`foo ${fn()} bar`

编程风格

静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。

四、函数的扩展

1. 默认值

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 对 0 false不友好

function log(y) {
  y = y || '我是默认值';
  console.log(y);
} 
log('传值了');
log(0);       
log(false);

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

// 例子1:
function log(y = '我是默认值') {
  console.log(y);
} 
log(0)
log(false)

// 例子2:
function Point(x = 0, y = 0) {
  this.x = x;
  this.y = y;
} 
const p = new Point();
p // { x: 0, y: 0 }
2. 函数的length属性
// 默认值在不同位置的例子:

(function (a) {}).length  // 没有默认值

(function (a = 5) {}).length 

(function (a, b, c = 5) {}).length 

(function(...args) {}).length //  rest 参数   

(function (a = 0, b, c) {}).length 

(function (a, b = 1, c) {}).length 
3. rest 参数
// arguments 能通过下标访问 转成数组
function sortNumbers() {
  console.log(arguments)
  return Array.prototype.slice.call(arguments);
}
sortNumbers(1,2,3)

// rest参数 真正的数组
const sortNumbers = (...numbers) => {
  console.log(numbers)
}
sortNumbers(1,2,3)

4. 箭头函数
let f = () => 5;
// 等同于
let f = function () { return 5 };



let sum = (num1, num2) => num1 + num2;
// 等同于
let sum = function(num1, num2) {
  return num1 + num2;
};


 
[1,2,3].map(function (x) {
  return x * x;
});
// 等同于 
[1,2,3].map(x => x * x);

 
  
const person = { 
  first:'first', 
  last:'last',
};
function full(person) {
  return person.first + ' and ' + person.last;
}
full(person)
// 等同于 
const full = ({ first, last }) => first + ' and ' + last; 
full(person)
// 定义对象方法的时候 不能用箭头函数
const cat = {
  lives: 9,
  jumps: () => {
    console.log(this);    // window
    this.lives--;     
  }
} 
cat.jumps();       // 箭头函数只能调用外层的this  把函数上下文绑定到了 window 上

// 解决方案:
const dog = {
  lives: 9,
  jumps() {
    console.log(this === dog);    // 写成普通函数 this就指向dog
    this.lives--;     
  }
} 
dog.jumps();




// 不可以当作构造函数。 因为构造函数需要有自己的this,箭头函数没有自己的this,用的是外层的this
let X = () => {};
x = new X(); 


// 定义原型方法的时候 不能用箭头函数
function Cat(name) {
    this.name = name;
} 
Cat.prototype.sayCatName = () => {
    console.log(this); //  window   箭头函数调用外层的this
    return this.name;
}; 
const cat = new Cat('Mew');
cat.sayCatName();   // ''


// 定义事件回调函数  不能用箭头函数
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
    console.log(this === window); // true
    this.innerHTML = 'Clicked button';
}); 

编程风格

// bad
[1, 2, 3].map(function (x) {
  return x * x;
}); 

// best
[1, 2, 3].map(x => x * x);
// bad
const self = this;
const boundMethod = function(...params) {
  return method.apply(self, params);
} 

// best
const boundMethod = (...params) => method.apply(this, params);
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good
function concatenateAll(...args) {
  return args.join('');
}
// bad
function handleThings(opts) {
  opts = opts || {};
}

// good
function handleThings(opts = {}) {
  // ...
}

五、数组的扩展

1.扩展运算符
console.log(...[1, 2, 3])


console.log(1, ...[2, 3, 4], 5)


[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]


// 合并数组
function push(array, ...items) {
  array.push(...items);
  return array;
}
push([1,2,3]4,5,6)


// 函数参数展开
const arr = [0, 1, 2];
function f(x, y, z) { 
  console.log(x,y,z)
}
f(...arr);


// 合并数组
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e']; 
// ES5 
arr1.concat(arr2, arr3);  
// ES6 
[...arr1, ...arr2, ...arr3] 


//拆分字符串
[...'hello']
// [ 'h', 'e', 'l', 'l', 'o' ]


// 遍历map类型的keys
let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]); 
[...map.keys()]
[...map.values()]


// Generator 返回数组
const go = function*(){
  yield 1;
  yield 2;
  yield 3;
}; 
[...go()]    // [1, 2, 3] 
2. Array.from()
// 伪数组的转换
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3,
}; 
console.log(arrayLike instanceof Array)  // false;
let arr1 = [].slice.call(arrayLike);    // ES5  转换成数组
let arr2 = Array.from(arrayLike);    // ES6  转换成数组


// arguments对象
function foo() {
  let args = Array.from(arguments); 
  console.log(arguments instanceof Array)  
  console.log(args instanceof Array)
}
foo(1,2,3); 
 

// 字符串
Array.from('string'); 
let arr = [2,3,4];

Array.from(arr, x => x * x);
// 等同于
Array.from(arr).map(x => x * x);
3. 数组实例的 includes()
// ES5 判读是否存在与数组 
[1,2,3,NaN].indexOf(1) !== -1 ? console.log('存在') : console.log('不存在');
[1,2,3,NaN].indexOf(4) !== -1 ? console.log('存在') : console.log('不存在');
 //  误判  缺点:1. 不够语义化 2. 它内部使用严格相等运算符(===)进行判断 
[1,2,3,NaN].indexOf(NaN) !== -1 ? console.log('存在') : console.log('不存在');   
  

// ES6:  
[1, 2, 3].includes(1)     // true
[1, 2, 3].includes(4)     // false
[1, 2, 3, NaN].includes(NaN) // true

六.对象的扩展

1. Object.assign()
const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}    
typeof Object.assign(2)   // 'object'     
Object.assign(undefined) // 报错  
Object.assign(null)   // 报错  undefined和null无法转成对象
const obj1 = { 
  a: { b: 1 },
  c:2
};
const obj2 = Object.assign({}, obj1);   // 与 {} 拼接会产生一个新的对象

obj1.c = 3;   
obj2     // c不会变  一级深拷贝

obj1.a.b = 2;   
obj2      // b会跟着变 并没有开辟新地址
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
// ES5写法  prototype 添加方法
SomeClass.prototype.someMethod = function (arg1, arg2) {
  ···
};
SomeClass.prototype.anotherMethod = function () {
  ···
};
// ES6 prototype 添加方法
Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    ···
  },
  anotherMethod() {
    ···
  }
});
2. Object.keys(),Object.values(),Object.entries()
let obj1 = { foo: 'bar', baz: 42 };
Object.keys(obj1)

let obj2 = { foo: 'bar', baz: 42 };
Object.values(obj2)

let obj3 = { foo: 'bar', baz: 42 };
Object.entries(obj3)
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };

// 拿obj的keys
for (let key of keys(obj)) {
  console.log(key);  
}

// 拿obj的values
for (let value of values(obj)) {
  console.log(value);  
}

for (let [key, value] of entries(obj)) {
  console.log(key, value); 
}
3. Object.fromEntries()
Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: 'bar', baz: 42 }

七. Promise

1. 简介
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    return resolve(value);
  } else {
    return reject(error);
  }
}); 
2. then()
promise.then(function(value) {
  // success  resolved时调用
}, function(error) {
  // failure  rejected时调用 
});

拿ajax举例:

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open('GET', url);
    client.onreadystatechange = handler;
    client.responseType = 'json';
    client.setRequestHeader('Accept', 'application/json');
    client.send();

  });

  return promise;
};

// 一个异步
getJSON('/posts.json').then(
   json => console.log(json),
   err => console.error('出错了', err)
);

// 嵌套异步
getJSON('/post/1.json').then(
  post => getJSON(post.commentURL)      // 返回的还是一个Promise对象(即有异步操作)
).then(
  comments => console.log('resolved: ', comments),   // 等待前一个Promise对象的状态发生变化,才会被调
  err => console.log('rejected: ', err)
); 
3. catch()
// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good     可以捕获then执行中的错误,也更接近同步的写法(try/catch)
promise 
  .then(function(data) { 
    // success      
  })
  .catch(function(err) {
    // error
  });
4. Promise.all()
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + '.json');
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});

八. Iterator(遍历器)

let m = new Map();
m.set('numberA', 67);
m.set('numberB', 88);
m.get('numberA');   

let s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}
// Array
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
  console.log(v); // red green blue
}


// Set
const engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
  console.log(e);
}

let generator = function* () {
  yield 1;
  yield 2;
  yield 5;
};

var iterator = generator();

for(let v of iterator) {
  console.log(v);  
}

九. Generator

1. 简介
2.语法
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }


let obj = {
  * myGeneratorMethod() {
    ···
  }
};
// 等价
let obj = {
  myGeneratorMethod: function* () {
    // ···
  }
};
function* demo() {
  console.log('Hello' + yield);        // SyntaxError
  console.log('Hello' + yield 123);    // SyntaxError

  console.log('Hello' + (yield));      // OK
  console.log('Hello' + (yield 123));  // OK

  foo(yield 'a', yield 'b');    // OK
  let input = yield;           // OK
}
3. 方法属性
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
hw.next()
//'   { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }
3. next()
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
} 

let b = foo(5);
b.next() // { value:6, done:false }    x=5, 执行第一行,输出yield的值    x+1的值6 
b.next(12) // { value:8, done:false }  y=24,执行第二行,输出yield的值 将上一次yield表达式的值设为12   24/3=8
b.next(13) // { value:42, done:true }  z=13  执行第三行, return (x + y + z);
4. throw()、return()

function* gen(x,y){
  yield 1;
  yield 2;
  yield 3;
}
let g = gen();
g.next();    //{value: 1, done: false}
g.return(5); //{value: 5, done: true}
g.next();    //{value: undefined, done: true} 

 
function* foo(x,y){
  yield 1;
  yield 2;
  yield 3;
}
let f = foo();
f.next();    //{value: 1, done: false}
f.throw(new Error('出错了'));    // Error
f.next();    //{value: undefined, done: true} 


5. 异步对比
// 传统回调函数
ajax('url_1', () => {
    // callback 函数体
    ajax('url_2', () => {
        // callback 函数体
        ajax('url_3', () => {
            // callback 函数体
        })
    })
})

// 无法取消 Promise ,错误需要通过回调函数来捕获
ajax('url_1')
  .then(res => {
      // 操作逻辑
      return ajax('url_2')
  }).then(res => {
      // 操作逻辑
      return ajax('url_3')
  }).then(res => {
      // 操作逻辑
  })

// Generator
function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

// async/await
async function test() {  
  let result1 = await fetch('XXX1')
  let result2 = await fetch('XXX2')
  let result3 = await fetch('XXX3')
}

十. class

1. 构造函数 与 class
//ES5 生成实例对象的传统方法
function Point(x, y) {
  this.x = x;
  this.y = y;
} 
Point.prototype.toString = function () {
  return `${this.x},${this.y}`
}; 
let p = new Point(1, 2);

// ES6  class
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  } 
  toString() {
    return  `${this.x},${this.y}`
  }
}

//其实就是调用原型上的方法。
// 等同于   
Point.prototype = { 
  constructor() {},
  toString() {}
};
2. new类的实例
//定义类
class Point { 
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.toString = this.toString.bind(this);
  }
 
  toString() {  // 普通函数需绑定this
    return `${this.x},${this.y}`
  } 

  //toString = () => {  // 箭头函数不需要绑定this 引用外层this
  //  return `${this.x},${this.y}`
  //} 
}

let point = new Point(2, 3); 
let { toString } = point;
toString();  // (2, 3)

// 检测自身属性中是否具有指定的属性
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true

point.hasOwnProperty('toString') // false
point.__proto__.constructor === Point;  // true
point.__proto__.hasOwnProperty('toString') // true   方法在point的原型Point上
3. 继承
function Father(name, age){
    this.name = name;
    this.age = age;
} 

function Son(name, age){
    Father.call(this, name, age);
}

Son.prototype = Object.create(Father.prototype);  // 通过Object.crete()去关联两者的prototype
Son.prototype.constructor = Son;                  // 将 Son 的constructor指向自己
Son.prototype.show = function () {
    console.log(this.name,this.age);
};
var s = new Son('子类', 110);  
s.show();
// class构造一个父类
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  p() {
    return 2;
  }
}

// extends,super关键字  继承所有的属性,方法
class ColorPoint extends Point {   // 先生成父类实例
  constructor(x, y) {         // 调用子类的构造函数修饰父类实例
    super(x, y);              // super当函数用   
    console.log(x,y);         
    console.log(super.p());   //  super当对象用
  }
}
 
let colorPoint = new ColorPoint('xx','yy');

// 如何判断一个类是否继承了另一个类 
Object.getPrototypeOf(ColorPoint) === Point;



结束!

上一篇下一篇

猜你喜欢

热点阅读