【记】ECMAScript 6新特性及适用场景

ES6 是对javascript语言的一次重大升级,目前es6中很多新语法和特性已经被浏览器兼容,还有一些新语法可以借助插件实现浏览器兼容。

总之现在就开始用es6编写javascript绝对是趋势...这里记录一些es6中常用的新特性和其引用场景。

1.新的数据类型Symbol,新的数据结构(对象)Set,Map

1.1 Symbol数据类型

Symbol和string,bool一样,是一种新增的基础类型数据,代表一个唯一的不可重复的数值

let s1 = Symbol(1);                   //内部参数为描述
console.log(s1,typeof s1);            //Symbol(1) "symbol"
console.log(Symbol(2) == Symbol(2));  //false  (唯一性)
console.log(String(Symbol(1)));       //"Symbol(1)" (可以转为字符串类型)
console.log(!Symbol(1));              //false (可转为bool类型)
//不能转为数字类型,不能使用运算符进行运算
1.2 Set对象

Set类似于数组,但是成员的值都是唯一的,没有重复的值。可以应用Set做数组去重

const s = new Set([1,2,3]);
s.size = 3;
s.add("a").add("b"); console.log(s);    //Set {1, 2, 3, "a", "b"}
s.delete("a"); console.log(s.has("a")); //false
const arr2 = [1,2,'a','b',2,'a'];
console.log([...new Set(arr2)]); //[1,2,'a','b'] 数组去重
1.3 Map对象

Map类似json对象,但是不能像json那样设置,获取,删除属性和值

var map = new Map();  //创建Map数据
map.set(name,value);  //设置键和值
map.get(name);        //获取值
map.delete(name);     //删除值

Map数据只能通过for of遍历

2.字符串,数组,对象,函数的扩展

2.1字符串的扩展
//repeat()重复操作
let str1 = 'a';
let str2 = str1.repeat(3);         // "aaa"
//includes() 判断是否包含段字符
let str = 'abcf';
console.log(str.includes('cf'));   //true
console.log(str.includes('cd'));   //true
//startsWith() 判断是否以某个字符开头
console.log(str.startsWith('ab')); //true
console.log(str.startsWith('b'));  //flase
//endsWidth() 判断是否以某个字符结尾
console.log(str.endsWith('cf'));   //true
console.log(str.endsWith('c'));    //flase
2.2数组的扩展
//Array.from()  将类数组转为数组
var aAs = document.querySelectorAll("a");
var aArr = Array.from(aAs);  //aArr为数组,存的是dom对象
//Array.of();  创建数组
var arr1 = Array.of(1,'a',2,3,3);
//find(val,eq,arr) 找出第一个符合条件的数组成员
//findIndex(val,eq,arr) 找出第一个符合条件的数组成员的索引
//三个参数,依次为当前的值、当前的位置和原数组
var arr2 = ['a',2,'c',11,4];
let res1 = arr2.find(function(val,eq,arr){
    return val < 4;
});
let res2 = arr2.findIndex(function(val,eq,arr){
    return val > 2 && val <10;
});
console.log(res1); //2
console.log(res2); //4
//fill() 数组填充,覆盖
var oarr = [1,2,3];
//oarr.fill("a"); console.log(oarr); // ["a", "a", "a"]
oarr.fill("a",1,2); console.log(oarr); // [1, "a", 3] 
//map()映射方法.n个值,n个结果
var arr =  [61,20,70];
var arr2 = arr.map(function(item){
  return item>=60?'及格':'不及格';
});
//arr2  //['及格','不及格','及格']
//reduce()方法.n个值,1个结果
var arrr1 = [68,53,12];
var res = arrr1.reduce(function(tmp,item,index){  //求和
    return tmp+item;   //返回前两个数的和,然后继续执行,直到返回一个值.index为执行索引
});
//res  //133
//filter() 过滤方法
var res2 = arrr1.filter(function(item){  //过滤掉奇数
    if(item%2==1){
       return false;  //奇数不要
    }else{
       return true;
    }
});
//forEach() 遍历方法
arrr1.forEach(function(item,index){
    console.log(`第${index}个是${item}`);  //第0个是68
});
2.3对象的扩展
//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
//Object.assign() 对象的合并
var obj1 = {a:1}
var obj2 = {a:2,b:3}
var obj3 = {c:'abc'}
Object.assign(obj1,obj2,obj3); //obj1为源对象
console.log(obj1);  // Object {a: 2, b: 3, c: "abc"}
//stringify()  //json转字符串
//pares()  //字符串转json
2.4函数的扩展
//函数的rest参数
function fn(...args){  //...可以展开数组和json,此处...args等于[]
    args.push(4);
    console.log(args); 
}
fn(1,2,3);  //[1,2,3,4]
var json1 = {a:"a1",b:"b1"}
var json2 = {...json1,c:"c1"} //{a: "a1", b: "b1", c: "c1"}
//箭头函数
//原则:1.函数有且只有一个参数,()可以不写. 2.函数有且只有一个语句,且是return语句,{}可以不写.
const fn = (a,b) => a+b;        //箭头后跟变量,指函数返回值
const fn2 = function(a,b){        //fn2与fn效果一样
    return a+b;
}
const fn = a =>{ const b=a+1;return b }    //箭头函数后跟{},不再是返回值,而是函数体
fn(1);   //2
const fn2 = (a,b) =>({a,b});    //要返回函数体时,用()包裹,返回的是一个对象
fn2(1,2); //Object {a: 1, b: 2}
h => h(App) //等价于 h => { return h(App) } //也等价于function (h) { return h(App) }
//箭头函数体内没有自己的this,在使用时,内部的this就是定义时所在环境的对象.
document.onclick = ()=>{
   console.log("ok");  
   console.log(this===window);   //true,此处this不再指向触发方法的对象
}

3.解构赋值,字符串模板,扩展运算符

3.1解构赋值

原则:等号两边的结构必须一样,需要一个等式同时完成.

简单的场景: var [a,b,c] = [1,2,3]; a为1,b为2..

在得到json数据时也可以直接解构赋值:

var data = {"title":"a","msg":"aa"}; 
var {title,msg,name="默认名"} = data;  
//title为"a",msg为"aa",name为"默认名"

函数传参时可以传入默认值:

function fn({name="lili"}={}){ 
     console.log(name); //输出lili 
}
function fn(name="lili"){ 
     console.log(name); //输出lili
}
3.2字符串模板字符串连接
let flag = true;
let str = "上海";
let html = `<ul>
              <li>${'首页'}</li>
              <li>${str}</li>
              <li>${flag?'北京':'上海'}</li>
           </ul>`;
console.log(html); // <ul><li>首页</li><li>上海</li><li>北京</li></ul>
3.3 扩展运算符

扩展运算符...可用作数组复制,常用的数组复制方式有:Array.from()方法;连接空数组

var arr1 = [1,2];
//var arr2 = Array.from(arr1);
//var arr2 = arr1.concat([]);
var arr2 = [...arr1];
arr2.push(3); console.log(arr2); //[1,2,3]   //arr1  [1,2]

4.for-of循环和iterator

4.1 for-of主要用来循环Map数据,还可以循环数组,不能循环json
var arr = ['a','b','c'];
for(var i in arr){
    console.log(i);  // 0,1,2
}
for(var i of arr){
    console.log(i);  // a,b,c
}
var map = new Map();
map.set("a","11");
map.set("b","22");
for(var name of map){
    console.log(name);  //同时输出键和值 a,11 ; b,22
}
for(var [a,b] of map.entries()){  //此处map.entries()就是map的默认值
    console.log(a,b);  // a 11; b 22
}
for(var key of map.keys()){  //仅遍历键
    console.log(key);  // a;b
}
for(var val of map.values()){  //仅遍历值
    console.log(val);  // 11;22
}
4.2 iterator方法
var arr = [1,2,3];
var itArr = arr[Symbol.iterator](); //原生方法运行后得到该数据的对象指针
//数组,set,map都有对应各自类型的对象指针,json不具备
console.log(itArr.next()); //{value: 1, done: false}
console.log(itArr.next()); //{value: 2, done: false}
console.log(itArr.next()); //{value: 3, done: false}
console.log(itArr.next()); //{value: undefined, done: true}
//具备iterator方法的数据结构都可以进行解构赋值和扩展运算符

5.es6中的面向对象与继承

5.1面向对象

es5中面向对象是通过new构造函数实现的,es6中有了class

class Person{ //类
    constructor(name="小明"){
       this.name = name
    }
    showName(){
       console.log(this.name)
    }
}
var p1 = new Person("lili");
p1.showName();
5.2对象继承

在es5中,对象的继承分为属性继承和方法继承.属性通过call/apply原型冒充来继承,方法通过原型对象来继承.es6中可以通过extends来继承了

class Person{ //类
    constructor(name="小明"){
       this.name = name;
       this.head = 1;
    }
    static showClass(){  //静态方法
       console.log("人类");
    }
    showName(){
       console.log(this.name)
    }
}
class Worker extends Person{    
}
var p1 = new Person();
var w1  = new Worker("工人小王");
p1.showName===w1.showName; //true, 子类和父类的实例的方法都是一个引用地址
Person.showClass();        //人类, 通过extend继承
Worker.showClass()         //人类, 子类继承了父类的属性,方法,静态方法

若子类中还需定义其他属性时,需要先继承父类的构造器中的属性:

class Worker extends Person{   
       constructor(name){
           //this.name ="xxx";  //找不到this,子类必须先调用super,才能用this
           super(name);         //继承父类构造器中的属性,类似es5中的call/apply
           this.name ="xxx";    //子类可以覆盖或新增新的属性
       }   
}

6.es6中的模块化

6.1 export/import实现模块化

早期实现模块化都有sea,require。目前es6中支持export/import的方式实现模块化。目前浏览器对模块化的兼容还不完善,可以用构建工具开发并引入babel等方式实现。

模块文件的创建和引入

//=================export=======================
//a.js 一个js文件中,多个export语句导出多个模块
export const a ="dd"
export const b ="ee"
//b.js 引入a.js
//import {a} from 'a.js'      //引入模块需要写在{}中
//import {a,b} from 'a.js'    //引入时变量名需要对应
import * as objs from 'a.js'  //将a.js内所有导出的模块集成为一个对象objs
//=================export default================
//c.js 导出一个模块,一个文件中只能有一个export default语句
export default 'hello world'
//d.js 引入c.js
import a from 'c.js'   //引入export default导出的模块时,变量名不用对应
//-----------------------------------------------------------
//e.js 用export default语句导出多个模块
const str = 'hello world';
const txt = '你好';
export default {str,txt}
//f.js 引入e.js
import a from './e.js'
console.log(a.str,b.txt) //hello world 你好
6.2 CommonJs模块化 (node中采用的是CommonJs规范)
//a.js 导出模块
const str = 'hello world';
module.exports = str;
//b.js 引入模块
const b = require('./a.js'); 
console.log(b);  //hello world

7.es6中的Promise

Promise是一个对象,可以通过new来创建实例。有两个原型方法和几个静态方法。

7.1 then,catch方法

const p = new Promise(function(resolve,reject){
     const img = new Image();
     img.src = "https://resource.hbiger.com/image/blog/archive/flex/5.jpg";
     img.onload = function(){
         resolve(this);
     }
     img.onerror = function(err){
             reject(err);
     }
});
//异步响应,等图片加载动作完成后执行
p.then(function(img){
    document.body.appendChild(img)
},function(err){
})
//或者用catch方法替换上面第二个参数
p.then(function(img){
    document.body.appendChild(img)
}).catch(function(err){
})

7.2 all,race等静态方法

var p2 = Promise.resolve(3);  //创建一个成功的响应
var p3 = Promise.reject(5);   //创建一个失败的响应
//all静态方法,当页面渲染需要等待多个异步数据都完成后才能进行时,使用all
Promise.all([p1,p2,p3]).then(function(datas){
      console.log(datas);     //当p1,p2,p3三个响应都成功时才执行then
},function(err){ ///... })    //p1,p2,p3中有一个不成功,便执行err,返回错误信息
//race静态方法,当页面同时发出多个异步请求,使用响应最快的请求的返回数据时,可以用到race
var t1 = new Promise(function(resolve,reject){
      setTimeout(resolve,500,'one'); //延迟执行resolve('one');
});
var t2 = new Promise(function(resolve,reject){
      setTimeout(resolve,300,'two'); 
});
Promise.race([t1,t2]).then(function(value){
    console.log(value);   //two
});

8.async,await

8.1 await会在async异步函数内阻塞当前线程向后执行
//假设promiseFun是一个异步方法
function promiseFun(){
    return new Promise((resolve,reject)=>{
        new Promise((resolve,reject)=>{
        resolve()
    })
    .then(()=>{
       resolve()
        console.log('innerPromise')
   })
})
}
async function fn(){
    let a = await promiseFun();
    console.log('fn')
    console.log(a)
}
fn();
//打印innerPromise 
//打印fn
//打印promiseFun
//返回一个Promise {: undefined}
8.2 await并不是阻塞主线程执行,因为async函数本身是异步的,所以await其实是阻塞的当前异步函数的异步线程
var hold = function () { 
    return new Promise(function (resolve, reject) { 
            resolve(); 
    }) 
}; 
async function count(i){ 
    await hold() 
    console.log(i) 
} 
for(var i = 0 ;i < 3 ; i++){
     count(i); 
}
console.log("run")
//打印run
//打印0
//打印1
//打印2

1.虽然await会阻塞async异步函数,但是并没有阻塞主线程。

2.虽然await阻塞异步函数向后执行,看起来像是同步的,但是它本质还是异步的,我们同样可以并行执行。而同步函数不能并行执行。

9.proxy代理器,对目标对象的操作进行代理

var oa = {a:"aa"}
const proxy = new Proxy(oa, {
  get(target, key) {
    // 这里的 target 就是 Proxy 的第一个参数对象
    console.log('proxy get key', key)
    return target[key]
  },
  set(target, key, value) {
    target[key] = value
    console.log('value', value)
  }
})
proxy.a
//proxy get key a
proxy.a = "aa2"
//value aa2
//oa.a === "aa2" //true

Proxy支持拦截的操作,一共有13种:

1.get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
2.set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
3.has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
4.deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
5.ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、6.Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
7.getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
8.defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
9.preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
10.getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
11.isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
12.setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
13.apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

 欢迎转载:转载时请注明本文出处及文章链接

标签:


添加新评论

captcha
请输入验证码