es6

作者: 田成力 | 来源:发表于2019-10-09 20:41 被阅读0次

let && const

let a=10;
{
  console.log(a);
  let a=100;
}
//let 声明的变量会绑定到当前的作用域上
//Uncaught ReferenceError: Cannot access 'a' before initialization
let fn=function a(){

}

console.log(fn);
console.log(a)//a is not defined

var fn=function a(){

}

console.log(fn);
console.log(a)//a is not defined

//var 和 let 都一样都获取不到函数名 a

解构赋值&剩余运算符&展开

解构赋值

//数组解构赋值:
let [name,age]=['zhangsan',18];
//只获取第二项
let [,age]=['zhangsan',18];

//对象的解构赋值
let {name,age}={name:'zhangsan',age:18};
//变量重命名
let {name:hisName,age:hisAge}={name:'zhangsan',age:18}
//默认赋值
let {name,age,address='默认地址'}={name:'zhangsan',age:18}
console.log(hisName,hisAge)

对象的解构赋值



剩余运算符

//需求 从['zhangsan',18,'成都']获取zhangsan,并将 18,成都 在组成新的数组

let [name,...args]=['zhangsan',18,'成都'];
console.log(name,args);

//需求 从['zhangsan',18,'成都']去掉第一项后面的组成数组
//ES5:
var arr=['zhangsan',18,'成都'];
arr.splice(0,1);
console.log(arr);

//ES6:
let arr=['zhangsan',18,'成都'];
[,...args]=arr;
console.log(args);


//对象的剩余运算符
let zhangsan={name:'zhangsan',age:18,address:'成都'};
let {name,...args}=zhangsan;
console.log(args);//{age:18,address:'成都'}

//该方法可用于copy对象:
let zhangsan={name:'zhangsan',age:18,address:'成都'};
let lisi=zhangsan
console.log(zhangsan===lisi);

let {...args}=zhangsan;
console.log(args===zhangsan);

展开


//数组的展开:
let arr=[1,2,3];
console.log(...arr);

//运用展开运算符 合并数组:
let a1=[1,2,3];
let a2=[4,5,6];
let a3=[...a1,...a2];

//扩展 合并去重:
let a1=[1,2,3];
let a2=[3,2,5,6];
let a3=[...a1,...a2];
let a4=[...new Set(a3)];

//扩展 交集
let s1=new Set(a1);
let s2=new Set(a2);

let a3=a1.filter((item)=>s2.has(item))
console.log(a3);

//扩展 并集
let a3=[...a1,...a2];
let a4=[...new Set(a3)];

//扩展 差集
let s1=new Set(a1);
let s2=new Set(a2);

let a3=a1.filter((item)=>!s2.has(item))
console.log(a3);


//对象的展开(浅拷贝,只拷贝了一层) 功能同 Object.assign()
let zhangsan_name={name:"zhangsan"};
let zhangsan_age={age:20};
let zhangsan_address={address:"成都"};
let zhangsan={...zhangsan_name,...zhangsan_age,...zhangsan_address};
console.log(zhangsan);
//{name: "zhangsan", age: 20, address: "成都"}

//注意点:同名的key会被后面的key代替掉

//扩展:如何实现一个深拷贝呢?
//可以用 JSON.parse(JSON.stringify(newObj))实现 但有缺憾,不能很好的识别函数,正则表达式


//例子:
//浅拷贝例子:
let zhangsan_name = { "name": "zhangsan", "age": 18, son: { "name": "lisi", "age": 1 } }
let zhangsan_address = { "address": "成都" };
let zhangsan = { ...zhangsan_name, ...zhangsan_address };
console.log("zhangsan==>", zhangsan);
zhangsan.son.name = "wangwu";
console.log("zhangsan==>", zhangsan);
console.log("zhangsan_name==>", zhangsan_name);//跟着一起改变了


//深拷贝
let deepClone = (value,hash=new WeakMap) => {
  //判断是否是null值或是undefined
  if (value == null) {
    return value;
  }

  //判断是否是普通类型或是函数类型,如果是普通类型则返回
  if (typeof value !== 'object') {
    //如果不是object,则是普通类型
    return value;
  }

  //如果是object类型,则判断类型,构建不同的类型
  if (value instanceof Date) {
    return new Date(value)
  }
  if (value instanceof RegExp) {
    return new RegExp(value)
  }

  //其他对象类型,或是数组类型
  //如果是对象,则会获取一个新的{}
  //如果是数组,则会获取一个新的[]
  let newValue = new value.constructor;
  if(hash.has(value)){
    return hash.get(value)
  }
  hash.set(value,newValue);
  for (const key in value) {
    if (value.hasOwnProperty(key)) {
      newValue[key] = deepClone(value[key],hash);
    }
  }
  return newValue;
}

let zhangsan_name2 = { "name": "zhangsan", "age": 18, son: { "name": "lisi", "age": 1 } }
let zhangsan_address2 = { "address": "成都" };
let zhangsan2 = { ...deepClone(zhangsan_name2), ...deepClone(zhangsan_address2) }
console.log("zhangsan2===>", zhangsan2);
zhangsan2.son.name = "wangwu";
console.log("zhangsan2 new ===>", zhangsan2);
console.log("zhangsan_name2===>", zhangsan_name2);


Object.defineProperty & Proxy

//Object.defineProperty的getter 和 setter简写方式:
let obj={
  _a:'',
  get a(){
    console.log("获取a的值");
    return this._a
  }
  set a(value){
    console.log("设置a的值");
    this._a=value;
  }
}

console.log(obj.a);//获取a的值


// Object.definfProperty的基本写法
let obj = {};
Object.defineProperty(obj, 'a', {
  writable: true,//是否可以被更改
  enumerable: true,//是否可以被循环出来 for-in
  configurable:true,//是否可以被删除
  value: 100
})
obj.a = 20;
delete obj.a;
console.log(obj);
for (const key in obj) {
  if (obj.hasOwnProperty(key)) {
    const item = obj[key];
    console.log("item==>", item);
  }
}

//使用getter和setter
let obj = {};
let _a;
Object.defineProperty(obj, 'a', {
  // writable: true,//是否可以被更改
  enumerable: true,//是否可以被循环出来 for-in
  configurable: true,//是否可以被删除
  get() {
    return _a;
  },
  set(value) {
    console.log("in a set");
    _a = value;
  }

})
obj.a = 20;
delete obj.a;
console.log(obj);
for (const key in obj) {
  if (obj.hasOwnProperty(key)) {
    const item = obj[key];
    console.log("item==>", item);
  }
}


//使用 Object.defineProperty实现监控一个对象

let obj = {
  name: "zhangsan",
  age: 18,
  son: {
    name: 'lisi',
    age: 2
  }
}

//监控对象(问题,不能监控数组的长度的变化,因为是单个属性进行监控,新增的属性不能有效的监控到)
let observer = (obj) => {
  if (typeof obj !== 'object') {
    //普通类型
    return;
  }

  //如果是对象类型
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      let value = obj[key];
      //可能这个value值也是一个对象
      //对value值再进行一次监控(如果是普通值则不会被监控,如果是对象则会再次被监控)
      observer(value);
      Object.defineProperty(obj, key, {
        get() {
          //取值
          console.log("this is get ");
          return value;
        },
        set(val) {
          //设值
          console.log("this is set");
          value = val;
        }
      })
    }
  }
}

observer(obj);
console.log(obj.name);
obj.name = 'lisi'
console.log(obj.name);
console.log(obj.age);
console.log(obj.son.name);


//Proxy 代理:
//拦截

//使用Proxy实现监控对象属性

//监控对象,set方法只能监控一级
let _zhangsan = {
  name: 'zhangsan',
  age: 18,
  son: {
    name: 'lisi',
    age: 1
  }
}

zhangsan = new Proxy(_zhangsan, {
  get(target, key) {
    console.log("进入get");
    return target[key];
  },
  set(target, key, value) {
    console.log("进入set");
    target[key] = value;
  }
})

zhangsan.name = 'lisi';
zhangsan.age = 20;
console.log(zhangsan.name);
console.log(zhangsan.age);
zhangsan.son.name = 'wangwu';
console.log(zhangsan.son.name);


//监控数组
let _arr = [1, 2, 3];

arr = new Proxy(_arr, {
  get(target, key) {
    console.log("进入get");
    return target[key];
  },
  set(target, key, value) {
    console.log("进入set");
    target[key] = value;
  }
});

arr.push(1);

//实现Proxy监控多级对象

let _zhangsan = {
  name: 'zhangsan',
  age: 18,
  son: {
    name: 'lisi',
    age: 1
  }
}

let observer = (obj) => {
  let cache = new WeakMap;
  let handler = {
    get(target, key) {
      console.log("进入get");
      if (typeof target[key] === 'object' && target[key] != null) {
        if (cache.has(target[key])) {
          return cache.get(target[key]);
        }
        let newProxy = new Proxy(target[key], handler);
        cache.set(target[key], newProxy);
        return newProxy;
      }
      return target[key];
    },
    set(target, key, value) {
      console.log("进入set");
      target[key] = value;
    }
  }
  return new Proxy(obj, handler)
}

let zhangsan=observer(_zhangsan);
zhangsan.son.name='lisi';


代码是如何运行的

  1. 代码读取script脚本中的代码从上到下依次读取依次执行(执行JS的主进程)
  2. 当执行栈遇到异步代码(click的函数,setTimeout,ajax等)会去开辟另外的线程去执行这些操作
  3. 当settimeout的时间到了,或是ajax的回调完成了,就会把这些回调放入任务队里中(微任务/宏任务)
  4. 当主任务(JS执行的主栈)任务全部做完后,JS会从任务队列中(优先是宏任务)拿第一个任务在主任务中执行
  5. 循环罔替
//分析下一下代码:
setTimeout(() => {
  console.log("1");
  setTimeout(() => {
    console.log("3");
  }, 0);
}, 0);

setTimeout(() => {
  console.log("2");
  setTimeout(() => {
    console.log("4");
  }, 0);
}, 0);


//1. JS执行主栈拿到以上一段代码
//2. 执行时发现第一个异步任务settimeout不去执行
//3. 执行时又发现了第二个异步任务 settimeout 不去执行
//4. 扫描时发现第一个settimeout执行的时间到了,将它放入宏任务队列中
//5. 扫描时发现第二个settimeout执行的时间也到了,也将它放入宏任务队列中
//6. JS执行主栈从宏任务中取出第一个宏队列中的任务(第一个settimeout)开始执行此刻打印了 1
//7. JS执行主栈又发现了一个异步任务 settimeout 不去执行
//8. 此刻JS执行主栈又空了,再从宏队列中获取第一个任务并开始执行 此刻打印了 2
//9. 打印3和4和打印1 2 类似

基本知识点

先执行宏任务,再执行微任务(因为最先执行的是script脚本中的代码,而script脚本也是一个宏任务)
UI的渲染和JS的执行是互斥的
每次JS执行主栈完成后都将把微任务进行清空,然后再从宏任务中取出一个任务进行执行

微任务

微任务:promise.then MutationObserver process.nextTick
每次当主栈中的任务执行完成后 微任务列表都将被清空

  setTimeout(() => {
    console.log("time0");
    Promise.resolve().then(() => {
      console.log("then0");
    })
  });//放入宏任务

  setTimeout(() => {
    console.log("time1");
  });//放入宏任务

  Promise.resolve().then(() => {
    console.log("then1");
  })//放入微任务

  Promise.resolve().then(() => {
    console.log("then2");
  })//放入微任务


  //执行jS主栈 将settimeout放入宏任务 promise.then放入微任务
  //js主栈执行完成,清空微任务列表 then1 then2 被执行
  //从宏任务中拿出第一个settimeout并执行 打印time0 并将then0 放入微任务
  //js主栈执行完成,清空微任务列表 then0被执行
  //从宏任务中拿出第二个settimeout并执行打印time1

宏任务

微任务:script ajax 事件(click) requestFrameAnimation setTimeout setInterval setImmediate(IE的方法) MessageChannel I/O UI rendering
每次只取一个队列中的任务出来执行

ES6的模块化 && Node的模块化

export let a=10 导出变量

//导出两个变量 a 和 b
export let a=10;
export let b=10;

// 等于于
const a=10;
const b=20;
export {
  a,b
}
//导出的变量(接口)而不是值
//错误:
export {
  a:10,
  b:20
}

//导出时候修改别名
exoirt {
  a as c,b
}
//将a名称修改成c

//实例:

//a.js文件
let a=10;
let b=20;
setInterval(() => {
  a++
}, 1000);
export {
  a,b
}

//b.js
import {a,b} from './a';
setInterval(()=>{
  console.log(a)
},1000)

//b文件中的打印信息会改变吗
// 答应是会的 因为 export导出的是变量,而不是值,所以变量的值改变了再去取的值,会改变
//导入的 可以把它看成是一个接口,每次取的值其实是调用一个接口

import只能放到最外的作用域中
并且import语法会被解析到页面的最顶部
导入的变量不能更改它的值

export default 默认导出

export default的区别 export 导出的是一个变量(接口) export default 是导出的一个值
例如: 可以使用 export default 'hello' 但不能有 export 123;
export default xx的原理(不同,因为export default导出的是值,而export导出的是变量):

let a=10;
let b=20;
let c=30;
export default c
export {
  a,b,
}
//等价于:

let a=10;
let b=20;
let c=30;

export {
  a,b,c as default
}

// 引用方式
import xxx from './a';

使用import导入全部的属性

import * as obj from './a'
//这个时候 obj中就有了全部导入的属性了

如何接收默认导出的对象

//文件a.js
export let a=10;
export let b=20;
export default 30;

//文件b.js
import c,{a,b} from './a.js';

合并导出

export * from './a';
export * from './b';

副作用引用(文件会被执行,但引用的地方没用使用它)

import './a.css'
import './a.js'

动态导入

什么叫动态导入,当我代码执行到一个地方时,再去加载一个js文件
原理是 通过webpack自动拆分成一个新的文件,再用jsonp的方式引用

let button=document.getElementById("btn")
btn.onclick=function(){
  //动态引入file.js文件,并且执行
  import('./file.js').then(data=>{
    console.log("data");
  })
}

instanceof 的原理:

我们在使用 instanceof进行实例判断时 其实 实例会去找它的proto属性的值,如果能找到,则返回true

class My {
}

let a = new My();
a.__proto__ = Array.prototype;
console.log(a instanceof Array);//true


Reflect && Symbol && ES6模块 && class类

Reflect 反射

```javascript

//1.get && set 设置/获取属性的值
//用Reflect设置和获取obj的值
let obj = {};
Reflect.set(obj, 'name', 'zhangsan');
console.log(obj.name);
console.log(Reflect.get(obj,'name'));


//2.has 判断对象中是否有属性
//使用Reflect判断key是否在对象中存在
let zhangsan={name:"zhangsan"}
//使用in
console.log("name" in zhangsan);

console.log(Reflect.has(zhangsan,'name'));


//3.defineProperty 定义对象属性
//使用Reflect设置属性
let zhangsan = { name: 'zhangsan' };
  Object.defineProperty(zhangsan, 'name', {
    value: 'zhangsan'
  });

  //用法和Object.defineProperty相同
  Reflect.defineProperty(zhangsan,'age',{
    value:18
  });

  console.log(zhangsan);


//为什么要用Reflect.defineProperty呢?
//例子:
let zhangsan = { name: "zhangsan" };
Object.freeze(zhangsan)
Object.defineProperty(zhangsan, 'a', {
  value: 100
});

//用Object.freeze方法将zhangsan对象进行冻结,然后在使用Object.defineProperty的方式对zhangsan的属性进行赋值
//此时因为zhangsan已经被冻结了,再定义属性时会报错

//而如果使用Reflect则不会
let zhangsan = { name: "zhangsan" };
Object.freeze(zhangsan)
let flag=Reflect.defineProperty(zhangsan,'age',{
  value:18
})
console.log(flag);
console.log(zhangsan);

//该方法会有一个返回值,标识是否设置成功


//4.getOwnPropertyDescriptor 获取属性描述符 value writeable enumerable configurable
let zhangsan = { name: "zhangsan" };
console.log(Object.getOwnPropertyDescriptor(zhangsan,'name'));

//5.ownKeys 获取对象全部的key(包括Symbol的属性)

let zhangsan = { name: "zhangsan",[Symbol()]:18 };
console.log(Object.keys(zhangsan));//获取对象的全部key,但不能获取到symbol的属性
console.log(Object.getOwnPropertyNames(zhangsan));//获取对象的全部key,但不能获取到symbol的属性
console.log(Object.getOwnPropertySymbols(zhangsan));//获取对象的全部Symbol的key值
console.log(Reflect.ownKeys(zhangsan));

//6.apply
//使用普通的aplly方法:
let fn=function(a,b){
  console.log(this,a,b);
}

fn.apply(1,[2,3]);

//当fn中有一个同名方法:apply呢
fn.apply=function(){
  console.log("this is apply")
}

//如何调用原型链上的apply呢?
//直接调用prototype上的apply方法
Function.prototype.apply.call(fn);

//使用Reflect如何实现呢?
Reflect.apply(fn,1,[2,3]);
//上面的两个方法是完全等价的


//7.deleteProperty删除属性

//使用delete 属性的方法:
let zhangsan = { name: "zhangsan" };
Object.freeze(zhangsan)
delete zhangsan.name;
console.log(zhangsan);

//没有报错,也没有删除掉

//使用Reflect.deleteProperty
let zhangsan = { name: "zhangsan" };
Object.freeze(zhangsan)
let flag = Reflect.deleteProperty(zhangsan,'name');
console.log(flag);
//删除失败了会有返回值告诉我们 删除失败了

//8.preventExtensions 不允许对象添加字段
let zhangsan = { name: "zhangsan" };
Object.preventExtensions(zhangsan);
zhangsan.age=20;
console.log(zhangsan);

//功能相同,多了一个返回值
let zhangsan = { name: "zhangsan" };
let flag=Reflect.preventExtensions(zhangsan);
zhangsan.age=20;
console.log(flag);
console.log(zhangsan);

//9.isExtensible判断对象是否可以被扩展的
let zhangsan = { name: "zhangsan" };
let flag=Reflect.preventExtensions(zhangsan);
console.log(Object.isExtensible(zhangsan));

let zhangsan = { name: "zhangsan" };
let flag=Reflect.preventExtensions(zhangsan);
console.log(Reflect.isExtensible(zhangsan));

```

Symbol 他的值是独一无二

  1. 作为常量使用
const s1=Symbol('zhangsan');
const s2=Symbol('zhangsan');
console.log(s1==s2);
  1. 属性私有化
  2. Symbol.for
//Symbol('zhangsan') 是每次创建一个Symbol 而使用Symbol.for是先去找有相同key的Symbol没,没有才创建,如果有直接返回
const s1=Symbol.for('zhangsan');
const s2=Symbol.for('zhangsan');
console.log(s1===s2)//true
  1. Symbol.keyFor() 获取Symbol的key值
const s1=Symbol.for('zhangsan');
Symbol.keyFor(s1)//zhangsan
  1. 将Symbol当成对象的属性
const s1=Symbol('name');
let zhangsan={
  [s1]:'zhangsan'//将s1的值当成zhangsan对象的key
}
console.log(zhangsan[s1]);//这里不能使用"."来获取值
  1. 元编程,
    6.1. 通过Symbol.hasInstance改变原js的能力
  let o = {
    name: 'zhangsan'
  }

  let obj = {
  }
  // console.log(o instanceof obj);
  //Right-hand side of 'instanceof' is not callable


  //使用Symbol.hasInstance()方法改变js的instanceof方法
  //Symbol.hasInstance 当调用:instanceof obj时,会去调用使用Symbol.hasInstance()
  obj = {
    [Symbol.hasInstance]() {
      return 'name' in o
    }
  }

  console.log(o instanceof obj);

6.2. 通过Symbol.toStringTag改变对象的toString行为

  //原来的写法:
  let zhangsan={
    toString(){
      return "zhangsan"
    }
  }

  console.log(zhangsan+"")

  //使用Symbol的写法:
  let zhangsan={
  get [Symbol.toStringTag](){
    return 'zhangsan'
    }
  }

  console.log(zhangsan+"")//[object zhangsan]

  1. 控制衍生对象的类的构造函数
    class MyArray extends Array {
      constructor(...args) {
        super(...args)
      }
      static get [Symbol.species](){
        return Array;//控制衍生对象的所属类型
      }
    }

    let myArr = new MyArray(1,2,3);
    console.log(myArr);
    console.log(myArr instanceof MyArray);
    console.log(myArr instanceof Array);

    //newArr就是衍生对象
    let newArr=myArr.map(item=>item*2);
    console.log(newArr);
    console.log(newArr instanceof MyArray);

相关文章

网友评论

      本文标题:es6

      本文链接:https://www.haomeiwen.com/subject/sjcnpctx.html