从ES6到ES11--第二章

285 阅读8分钟

proxy

proxy作用

对应es5的Object.defineProperty

let obj = {}
let newVal = ''
Object.defineProperty(obj,'name',{
  get(){
    return newVal;
  },
  set(val){
    console.log('set');
    newVal = val;
  }
})

obj.name = 'es';
console.log(obj.name)

基本使用

let obj = {};
let p = new Proxy(obj,{})
p.name = 'imooc'
console.log(obj.name)
console.log(p.name)
for(let key in obj){
  console.log(key)
}
for(let key in p){
  console.log(key)
}

get

let arr = [7,8,9]
arr = new Proxy(arr,{
  get(target,prop){
    console.log('target',target)
    console.log('prop',prop)
    return prop in target ? target[prop] : 'error'
  }
})
console.log(arr[1])
console.log(arr[10])
let dict = {
  'hello':'你好',
  'world':'世界'
}
dict = new Proxy(dict,{
  get(target,prop){
    return prop in target ? target[prop] : 'error'
  }
})
console.log(dict['hello'])
console.log(dict['world11'])

set

let arr = []
arr = new Proxy(arr,{
  set(target,prop,val){
    if(typeof val === 'number'){
      target[prop] = val;
      return true;
    }else{
      console.log('false')
      return false
    }
  }
})

arr.push(5)
arr.push('6')
console.log(arr[0],arr[1],arr.length)

has

let range = {
  start:1,
  end:5
}

range = new Proxy(range,{
  has(target,prop){
    return prop >= target.start && prop <= range.end
  }
})
console.log(2 in range)
console.log(9 in range)

ownKeys

let obj = {
  name:'imooc',
  [Symbol('es')]:'es6'
}
console.log(Object.getOwnPropertyNames(obj))
console.log(Object.getOwnPropertySymbols(obj))
console.log(Object.keys(obj))
for(let key in obj){
  console.log(key)
}

let userinfo = {
  username:'xiecheng',
  age:34,
  _password:'***'
}
userinfo = new Proxy(userinfo,{
  ownKeys(target){
    return Object.keys(target).filter(key => !key.startsWith('_'))
  }
})

for(let key in userinfo){
  console.log(key)
}
console.log(Object.keys(userinfo))

proxy对象

let user = {
  username:'xiecheng',
  age:34,
  _password:'***'
}

user = new Proxy(user,{
  get(target,prop){
    if(prop.startsWith('_')){
      throw new Error('不可访问')
    }else{
      return target[prop]
    }
  },
  set(target,prop,val){
    if(prop.startsWith('_')){
      throw new Error('不可访问')
    }else{
       target[prop] = val
       return
    }
  },
  deleteProperty(target,prop){
    if(prop.startsWith('_')){
      throw new Error('不可删除')
    }else{
       delete target[prop]
       return true
    }
  }
})
// console.log(user.age)
// console.log(user._password)

user.age = 18
console.log(user.age)

try{
  user._password = 'aa'
}catch(err){
  console.log(err)
}

proxy方法

let sum = (...args) => {
  let sum =0;
  args.forEach(item => {
    sum += item;
  })
  return sum;
}

sum = new Proxy(sum,{
  apply(target,ctx,args){
    return target(...args)*2;
  }
})

console.log(sum(1,2))
console.log(sum.call(null,1,2))
console.log(sum.apply(null,[1,2]))

proxy类

let User = class{
  constructor(name){
    this.name = name;
  }
}

User = new Proxy(User,{
  construct(target,args,newTarget){
    console.log('con')
    return new target(...args)
  }
})
console.log(new User('imooc'))

扩展运算符和rest参数

扩展运算符

把数组或者类数组展开成用逗号隔开的值

rest参数

把逗号隔开的值组合成一个数组

扩展运算符

基本操作

function foo(a,b,c){
  console.log(a,b,c)
}
let arr = [1,2,3]
console.log(...arr) //1 2 3

合并数组

let arr1 = [1,2,3]
let arr2 = [4,5,6]
// Array.prototype.push.apply(arr1,arr2)
arr1.push(...arr2)
console.log(arr1) //[ 1, 2, 3, 4, 5, 6 ]

字符串合并成一个数组

let str = 'abca'
let arr = [...str]
console.log(arr) //[ 'a', 'b', 'c', 'a' ]

深拷贝

        var arr1 = [1, 2, 3]
        var arr2 = arr1
        arr1[0] = 4;
        console.log('arr1', arr1); //[4, 2, 3]
        console.log('arr2', arr2); //[4, 2, 3]
        var arr3 = [...arr1]
        // 等同于 var arr3 = arr1.slice()
        
        arr3[0] = 4
        console.log('arr1', arr1); //[1, 2, 3]
        console.log('arr3', arr3); //[4, 2, 3]

rest参数

通过arguments获取所有参数

function foo(x,y,z){
  let sum = 0;
  // Array.prototype.forEach.call(arguments,function(item){
  //   sum += item;
  // })
  Array.from(arguments).forEach(function(item){
    sum += item;
  })
  return sum;
}

console.log(foo(1,2))  //3
console.log(foo(1,2,3)) //6

通过rest参数

function foo(...args){
  console.log(args) //[ 1, 2 ]      //[ 1, 2, 3 ]
  let sum =0;
  args.forEach(function(item){
    sum += item;
  })
  return sum
}
console.log(foo(1,2))  //3
console.log(foo(1,2,3))  //6

拆分方法参数

function foo(x,...args){
  console.log(x)
  console.log(args)
}
foo(1,2,3,4)  //[ 2, 3, 4 ]

解构赋值

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

对象的拓展

属性简洁表示法

简写前

let name = 'aa'
let age = 14;
let obj = {
  name:name,
  age:age
}
console.log('obj',obj)

简写后

let name = 'aa'
let age = 14;
let obj = {
  name,
  age
}

属性名表达式

let s = 'school'
let obj = {
  [s]:'imooc'
}
console.log('obj',obj) //{ school: 'imooc' }

对象中的方法的正确写法

错误

let obj = {
  name:'aa',
  study:() =>{
    console.log(this.name+'在学习') //undefined在学习
  }
}
obj.study()

正确

let obj = {
  name:'aa',
  study(){
    console.log(this.name+'在学习') //aa在学习
  }
}
obj.study()

Object.is

它用来比较两个值是否严格相等,与严格相等运算符(===)的行为基本一致

对比2个对象是否相等(对比的是存储的内存地址(指针))

let obj1 = { //new Object()
  name :'aa',
  age : 14
}

let obj2 = {//new Object()
  name :'aa',
  age : 14
}
// 对比的是存储的内存地址(指针)
console.log(obj1==obj2)  //false
console.log(Object.is(obj1,obj2)) //false
let obj1 = { //new Object()
  name :'aa',
  age : 14
}
let obj2 = obj1;
console.log(obj1===obj2)  //true
console.log(Object.is(obj1,obj2))  //true

扩展运算符与Object.assign()

扩展运算符

let x = {
  a:3,
  b:4
}
let y = {...x}

console.log(y)  //{ a: 3, b: 4 }

Object.assign后面的对象会把前面的覆盖

let x = {
  a:3,
  b:4
}
let y = {
  a:1,
  c:5,
  d:6
}

Object.assign(y,x)
console.log(y) //{ a: 3, c: 5, d: 6, b: 4 }

对象遍历

for...in

let obj = {
 name : 'aa',
 age : 14
}

for(let key in obj){
  console.log(key,obj[key])
}

Object.keys

let obj = {
 name : 'aa',
 age : 14
}
console.log(Object.keys(obj))  //[ 'name', 'age' ]
Object.keys(obj).forEach(key => {
  console.log(key,obj[key])
})

Object.values

let obj = {
 name : 'aa',
 age : 14
}
console.log(Object.values(obj))  //[ 'aa', 14 ]

let

let声明的全局变量不是全局对象window的属性

var a = 5;
console.log(window.a); //5

let b=5;
console.log(window.b); //undefined

用let定义变量不允许重复声明

var a = 5;
var a = 6;
console.log(a); //6
let b = 5;
let b = 6;
console.log(b); //Identifier 'b' has already been declared

let声明的变量不存在变量提升

function foo(){
  console.log(a); //undefined
  var a = 5;
}
foo()
function foo(){
  var a;
  console.log(a); //undefined
  a = 5;
}
foo()
function foo(){
  console.log(a); //Cannot access 'a' before initialization
  let a = 5;
}
foo()

let声明的变量具有暂时性死区

var a = 5;
if(true){
  a = 6;
  let a;
}
// Cannot access 'a' before initialization
var a = 5;
if(true){
  let a= 6;
  console.log(a); //6
}
console.log(a); //5
var a = 5;
if(true){
  a= 6;
  console.log(a); //6
}
console.log(a);//6

let 声明的变量拥有块级作用域

es5只有全局作用域和函数作用域

{
    let a = 5
}
console.log(a) // undefined
for(var i =0;i<3;i++){
  console.log('内'+i);
}
console.log('外'+i);
//内0
//内1
//内2
//外3
for(let i =0;i<3;i++){
  console.log('内'+i);
}
console.log('外'+i);
//内0
//内1
//内2
//Uncaught ReferenceError: i is not defined
console.log('1'+a);
if(false){
  console.log('2'+a);
  var a = 5;
}
console.log('3'+a);

// 1undefined
// 3undefined

一道面试题

想要打印0,1,2

for (var i = 0; i < 3; i++) {
  setTimeout(function() {
      console.log(i)
  })
}
//3
//3
//3

改成闭包

for (var i = 0; i < 3; i++) {
  (function(j){
    setTimeout(function() {
      console.log(j)
    })
  })(i)
}
//0
//1
//2

改成let

for (let i = 0; i < 3; i++) {
  setTimeout(function() {
      console.log(i)
  })
}
//0
//1
//2

转成es5等同于

function _loop(i) {
  setTimeout(function() {
      console.log(i)
  })
}

for (var i = 0; i < 3; i++) {
  _loop(i)
}
//0
//1
//2

const

ES5 中定义一个常量

Object.defineProperty(window,'pi',{
  value:3.14,
  writable:false
})
console.log(pi); //3.14
pi = 5;
console.log(pi); //3.14

const 声明的变量必须进行初始化,并且不可被修改

const a =5;
a = 6;  //Assignment to constant variable.
const a;
a = 5; // Missing initializer in const declaration
if(true){
  console.log(a); // Cannot access 'a' before initialization
  const a = 5; 
}

const定义引用类型的数据,可以修改

const obj = {
  name:'aa',
  age:12
}
console.log(obj);  //{name: "aa", age: 12}
obj.school = 'bb'
console.log(obj); //{name: "aa", age: 12, school: "bb"}
const arr = [1,2,3]
arr.push(4)
console.log(arr);  // [1, 2, 3, 4]

Object.freeze冻结数据

Object.freeze可以冻结数据,使其不可以被修改,但是只能递归冻结

const obj = {
  name:'aa',
  age:12
}
Object.freeze(obj)
console.log(obj);  //{name: "aa", age: 12}
obj.school = 'bb'
console.log(obj); //{name: "aa", age: 12}

只能冻结当前层

const obj = {
  name:'aa',
  age:12,
  skill:{
    name:'code',
    year:11
  }
}
Object.freeze(obj)
console.log(obj);  //{name: "aa", age: 12}
obj.school = 'bb'
obj.skill.year = 12
console.log(obj); //year: 12

指定冻结第几层

const obj = {
  name:'aa',
  age:12,
  skill:{
    name:'code',
    year:11
  }
}
Object.freeze(obj)
Object.freeze(obj.skill)
console.log(obj);  //{name: "aa", age: 12}
obj.school = 'bb'
obj.skill.year = 12
console.log(obj); //year: 11

解构赋值

数组解构赋值

基本

let arr = [1,2,3,4]
const [a,b,c,d] = arr;
console.log(a,b,c,d); //1 2 3 4

跳过赋值元素

let [name, , title] = ['John', 'Jim', 'Sun', 'Moon']

console.log( title ) // Sun

rest 参数

let [name1, name2, ...rest] = ["a", "b", "c", "d"]

console.log(name1) // a
console.log(name2) // b


console.log(rest[0]) // c
console.log(rest[1]) // d
console.log(rest.length) // 2

默认值

let [name = "a", surname = "b"] = ["aa"]

console.log(name)    // aa (from array)
console.log(surname) // b (default used)

Set

let [a, b, c] = new Set([1, 2, 3])
console.log(a);  //1
console.log(b);  //2
console.log(c);  //3

对象

基本操作

let obj = {
  age:10,
  name:'aa',
  school:'imooc'
}

const {age,name,school} = obj
console.log(age,name,school); //10 "aa" "imooc"

默认值

let options = {
  title: "Menu"
}

let {width = 100, height = 200, title} = options

console.log(width)  // 100
console.log(height) // 200
console.log(title)  // Menu

rest 参数

let options = {
  title: "Menu",
  height: 200,
  width: 100
}

let {title, ...rest} = options

console.log(title)  // Menu
console.log(rest.height)  // 200
console.log(rest.width)   // 100

字符串

let str = '1234'
const [a,b,c,d] = str;
console.log(a,b,c,d)  //1 2 3 4

数组的各种遍历

for

支持break

let arr = [1, 2, 3]
for (let i = 0; i < arr.length; i++) {
  if(arr[i] === 2){
    break;
  }
  console.log(arr[i])  //1
}

支持continue

for (let i = 0; i < arr.length; i++) {
  if(arr[i] === 2){
    continue
  }
  console.log(arr[i]) //1 3
}

forEach不支持break,不支持continue

arr.forEach(item => {
    if(arr[i] === 2){
    // break;  //报错
    continue  //报错
  }
  console.log('item',item);
})

map

let result = arr.map(item => {
  return item +=1;
})
console.log(arr);  //result
console.log(result);  //[2, 3, 4]

filter

let result = arr.filter(item => {
  console.log(item);
  return item ==2;
})
console.log(arr);  // [1, 2, 3]
console.log(result);  //[2]

some

let result = arr.some(item => {
  return item ==2;
})
console.log(arr);  // [1, 2, 3]
console.log(result);  //true

every

let result = arr.every(item => {
  return item ==2;
})
console.log(arr);  // [1, 2, 3]
console.log(result);  //false

reduce

数组求和

let sum = arr.reduce((prev,cur,index,arr) => {
  console.log('prev',prev);
  console.log('cur',cur);
  console.log('index',index);
  console.log('arr',arr);
  return prev+cur
},0)
console.log(sum);  

数组求最大值

let max = arr.reduce((prev,cur) => {
  console.log('prev',prev);
  console.log('cur',cur);
  return Math.max(prev,cur)
})
console.log(max);

// prev 1
// cur 2
// prev 2
// cur 3

数组去重

let arr = [1,2,3,2,4]
let res = arr.reduce((prev,cur) => {
    console.log('prev',prev);
    console.log('cur',cur);
  if(prev.indexOf(cur) == -1 ){
    prev.push(cur)
  }
  return prev
},[])
console.log(res);

// prev []
// cur 1
// prev [1]
// cur 2
// prev (2) [1, 2]
// cur 3
// prev (3) [1, 2, 3]
// cur 2
// prev (3) [1, 2, 3]
// cur 4

for in

for in 是为对象设计的,如果用于数组会调用数组原型上的方法

Array.prototype.foo = function(){
  console.log('foo');
}
for (var index in arr) {
  console.log(arr[index]);
}

// ƒ () {
  console.log('foo');
}

for of 是为数组设计的

for(let val of [1,2,3]){
  console.log(val);
}

find

找到数组中符合条件的第一个item

arr = [1,2,3,2]
let result = arr.find(item => {
  return item ==2
})
console.log(arr);
console.log(result);  //2  item

findIndex

找到数组中符合条件的第一个item的index

arr = [1,2,3,2]
let result = arr.findIndex(item => {
  return item ==2
})
console.log(arr);
console.log(result);  //1  index

数组拓展

伪数组

let divs = document.getElementsByTagName('div')
console.log(divs);  //HTMLCollection []

let divs2 = document.getElementsByClassName('div')
console.log(divs2);  //HTMLCollection []

let divs3 = document.querySelectorAll('.xx')
console.log(divs3);  //NodeList []

console.log(divs3 instanceof Array);  //false 是否是数组实例
divs3.push(123) //报错

Array.from把类数组转化为数组

let arrayLike = {
  0:'a',
  1:'b',
  2:'c',
  length:3
}
let arr = Array.from(arrayLike)
console.log(arr instanceof Array);  //true
arr.push('d') 
console.log(arr);  //["a", "b", "c", "d"]

Array.of

创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型

new Array存在的问题,根据参数传递数量不同,而创建不一样的数组

let arr1 = new Array(1,2)
console.log(arr1);  //[1, 2]

let arr2 = new Array(3)
console.log(arr2);  //[empty × 3]

Array.of

let arr1s = Array.of(1,2)
console.log(arr1s);  //[1, 2]

let arr2s = Array.of(3)
console.log(arr2s);  //[3]


let arr3 = Array.of(1,true,'immoc',{'1':'a'})
console.log(arr3);  //[1, true, "immoc", {…}]

Array.prototype.fill()

fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。

let arr1 = new Array(3).fill(7)
console.log(arr1);  //[7, 7, 7]
let arr2 = [1,2,3,4,5]
arr2.fill('aa',1,3)
console.log(arr2);  //[1, "aa", "aa", 4, 5]
let arr2 = [1,2,3,4,5]
arr2.fill(0)
console.log(arr2);  //[0, 0, 0, 0, 0]

indexOfincludes

indexOf存在的问题 不能判断NaN

let arr = [1,2,3,NaN,undefined]
console.log(arr.indexOf(1)); //0
console.log(arr.indexOf(NaN)); //-1
console.log(arr.indexOf(undefined)); //4

includes完美解决

let arr = [1,2,3,NaN,undefined]
console.log(arr.includes(1)); //true
console.log(arr.includes(NaN)); //true
console.log(arr.includes(undefined)); //true

函数的参数

参数的默认值

es5中默认值存在的问题

function foo(x, y) {
  y = y || 'world'
  console.log(x, y)
}
foo('hello', 'imooc')  //hello imooc
foo('hello', 0)  //hello world

es6默认值解决问题

function foo(x, y = 'world') {
  console.log(x, y)
}
foo('hello', 0) //hello 0

默认值要放在最后一个参数

function foo(x, y = 5,z) {
  // console.log(x, y,z)
  console.log('x',x);  //1
  console.log('y',y);  //2
  console.log('z',z);  //undefined
}
foo(1,2)
function foo(x,z,y = 5) {
  // console.log(x, y,z)
  console.log('x',x);  //1
  console.log('y',y);  //5
  console.log('z',z);  //2
}
foo(1,2)

解构

function foo({x,y =5}){
  console.log(x,y);
}
foo({})  //undefined 5
foo({
  x:1,
  y:2
})  //1 2
function ajax(url,{
  body='',
  method='GET'
} = {}){
  console.log('method',method);
}
ajax('www')   //method GET
ajax('www',{
  method:"POST"
})  //method POST

length

函数指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数

function foo(x,y=2,z=3){
  console.log(x,y,z);
}
console.log(foo.length);  //1

作用域

foo函数里能找到值,直接用

let x = 1;
function foo(x,y =x){
  console.log(y);
}
foo(2)

foo函数里不能找到值,向上一层找,上一层没有函数,就到window上找

let x = 1;
function foo(y =x){
  let x = 2;
  console.log(y);
}
foo()

// Uncaught ReferenceError: x is not defined

function foo(y =x){
  let x = 2;
  console.log(y);
}
foo()

函数的name

function foo(){}
console.log(foo.name);  //foo

console.log((new Function).name);  //anonymous

前头函数

声明方式

函数声明

console.log(sum(4,5));  //9
function sum(x,y){
  return x+y
}
console.log(sum(4,5));  //9

函数表达式

console.log(sum(4,5));  //Uncaught TypeError: sum is not a function
let sum = function(x,y){
  return x+y
}
console.log(sum(4,5));  //9

前头函数简写

只有一行,并且是return

console.log(sum(4,5));  //9
let sum = (x,y) => {
  return x+y
}
let sum = (x,y) => x+y
console.log(sum(4,5));  //9

this(他)

默认情况

  <button id='btn'>btn</button>
  let oBtn = document.querySelector('#btn')
oBtn.addEventListener('click',function(){
  console.log(this);  //btn
})

异步方法里调用

oBtn.addEventListener('click',function(){
  console.log(this);  //btn
  setTimeout(function(){
    console.log(this);  //window
  },1000)
}

bind

利用bind,把this传进来

oBtn.addEventListener('click',function(){
  console.log(this);
  setTimeout(function(){
    console.log(this);  //btn
  }.bind(this),1000)
})

箭头函数

箭头函数并没有this,是从上一层函数拿的,也就是继承

oBtn.addEventListener('click',function(){
  console.log(this);  //btn
  setTimeout(() => {
    console.log(this); //btn
  },1000)
})

构造函数

非箭头函数

function People(name,age){
  console.log(this);  //People {}
  this.name = name;
  this.age = age;
}
let p1 = new People('aa',10)
console.log(p1);  //People {name: "aa", age: 10}

箭头函数

let People = (name,age) => {
  console.log(this); //{}
  this.name = name;
  this.age = age;
}

let p1 = new People('aa',10)
console.log(p1);  //People {}

arguments

let foo = () => {
  console.log(arguments);
}
foo(1,2,3)

let foo = (...args) => {
  console.log(args);  //1 2 3
}
foo(1,2,3)

深拷贝

把一个对象赋值给另一个对象,修改原对象,不影响新对象

let checkType = data => {
  return Object.prototype.toString.call(data).slice(8,-1)
}

let deepClone = target => {
  let targetType = checkType(target)
  let result;
  if(targetType === 'Object'){
    result = {}
  }else if(targetType === 'Array'){
    result = []
  }else{
    return target;
  }
  for(let i in target){
    let value = target[i]
    let valueType = checkType(value)
    if(valueType === 'Object' || valueType === 'Array'){
      result[i] = deepClone(value)
    }else{
      result[i] = value
    }
  }
  return result;
}
let obj1 = {
  name:'aa',
  age:22
}
let obj2 = deepClone(obj1)
obj2.name = 'bb'
console.log(obj1);
console.log(obj2);