es6-解构赋值,函数.数组.对象扩展

一、变量的解构赋值

1、对象的解构赋值

(1)属性名与变量名不一致
当属性名称与变量名称不一致的,需要显式的指定属性名。这样才能把属性值给赋值到变量中。

let user = {name: '小明', age: 12};
let {name: userName, age: userAge} = user;
console.log(userName); // '小明'
console.log(userAge);  // 12  

(2)属性名与变量名一致
当属性名称与变量名称一致的,就只需要显示的指定变量名。

let user = {name: '小明', age: 12};
let {name, age} = user;
console.log(name);
console.log(age);

(3)嵌套赋值

let user = {name: '小明', age: 12, course: {name: '数学', score: 90}};
let {course: { score }} = user
console.log(score) // 90

(其他)变量赋予默认值

// 属性不存在
let user = {name: '小明', age: 12};
let {address: userAddress = '上海'} = user;
console.log(userAddress); // 由于user中不存在address属性,所以userAddress的值是默认值 `上海`

// 属性存在
let user = {name: '小明', age: 12};
let {name: userName = '小天'} = user;
console.log(userName); // userName => '小明'// 因为属性存在,变量无法获取默认值

2、数组的解构赋值

(1)数组解构语法能让我们快速的迭代数组的元素,并赋值给多个变量。

let myArray = [1, 2, 3]
let [a, b, c]= myArray
//a 1  b 2 c 3
let [a, b, c = 3] = [1, 2];
console.log(c); //Output "3”

(2)嵌套数组,可以从多维数组中提取值并分配给变量
let [a, b, [c, d]] = [1, 2, [3, 4]];

Promise.all([radioGroup(), cascader(),getTable()]).then((res)=>{
  const res1=res[0].data;
  const res2=res[1].data;
  const res3=res[2].data;
})

//----优化---
async getInfo() {
  const [res1, res2,res3] = await Promise.all([radioGroup(), cascader(),getTable()]);
}

二、函数的扩展

1、函数参数问题
函数的参数可以加默认值,直接写在参数定义后面,只有不传实参的时候才会走默认值

getBase64(ticket, scaleRation = 5, isType = 0){}

2、与解构赋值默认值结合使用

function fetch(url, { body = "", method = "GET", headers = {} }){
  console.log(method);
}
fetch("base/base/screenConfig/list", {});

3、箭头函数

function fn(x){
  return x;
}
//可以改为:
x=>x;
// 1.无参数的箭头函数
    let a1 = () => console.log('无参数的箭头函数')
    a1()

 // 2.传参数的箭头函数
    let a2 = (item)  => console.log(`->a2(${item})`);
    a2(25);
    // 当只有一个形参的时候,括号是可以省略的.
    let a2 = item  => console.log(`->a2(${item})`);

//3.多个参数
    let a3 = (...items) => console.log(`->a3(${items})`);
    a3(1,2,3,4);

  //函数体 
  //只有一条语句或者表达式的情况,{}可以省略---->会自动返回语句执行的结果或者是表达式的结果
  let a4=(x,y)=>x+y;
  console.log(a4(1,3))
  // 函数体不止一条语句或者是表达式的情况 需要加上return
  let a5=(x,y)=>{
    console.log(a5(1,3))
    return x+y
  }

4、因为箭头函数没有自己的 this,其内部的 this 指向的是外层作用域的 this,所以不适合定义对象的方法(对象字面量方法、对象原型方法、构造器方法)
然后也不适合定义结合动态上下文的回调函数(事件绑定函数),因为箭头函数在声明的时候会绑定静态上下文。

const button = document.querySelector('button');
button.addEventListener('click', () => {
    this.textContent = 'Loading...';
});
// this 并不是指向预期的button 元素,而是window

普通函数中的this:

1.this总是代表它的直接调用者(js的this是执行上下文), 例如 obj.func ,那么func中的this就是obj

2.在默认情况(非严格模式下,未使用 ‘use strict’),没找到直接调用者,则this指的是 window (约定俗成)

3.在严格模式下,没有直接调用者的函数中的this是 undefined

4.使用call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象

三、数组的扩展

1、Array.from()

Array.from()将类数组(array-like)对象与可遍历的对象转化为数组并返回。

let arr = {
  '0':'a',
  '1':'b',
  '2':'c',
  length:3
};
var arr2=Array.from(arr)
var arr1=[].slice.call(arr);

例子

// 报表重置功能
const arr=this.$refs.search.$el.querySelectorAll('input[type=text]') 
Array.from(arr).forEach(item => item.value = '');  //重置

2、includes()

includes()方法用来判断数组中是否含有某元素,如果存在返回 true,不存在返回 false,

3、find()&findIndex()

find()函数用来在数组中查找目标元素,找到就返回该元素(找到一个即可,不再寻找其它),找不到返回 undefined。

  const oldV = oldData.find((d) => d.policyId == v.policyId);

findindex()方法返回符合表达式结果的第一个元素的索引值,否则返回-1

const selectIdx = [{goodsCode:1},{goodsCode:2}].findIndex(v => v.goodsCode == 2);// selectIdx 1
  selectIdx != -1 && this.selectedData.splice(selectIdx,1);

4、find与filter的区别

let findResult = arr.find(i => i.name === 'Rick')
let filterResult = arr.filter(i => i.name === 'Rick')
console.log(arr); 
/*  输出结果
  [ {
      name: "Rick",
      age: 60
    },
    {
      name: "Rick",
      age: 70
    },
    {
      name: "Morty",
      age: 14
}]
*/
console.log(findResult);   // {name: "Rick", age: 60}
console.log(filterResult);  // [{name: "Rick", age: 60}, {name: "Rick", age: 70}]

四、对象的扩展和新增方法

1、Object.is()
可以用于比较两个值是否严格相等,与严格比较运算符 === 基本一致;
与严格比较运算符===的差异主要有两点:
(1). +0 不等于-0,
(2). NaN 等于自身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

2、Object.assign()
Object.assign 方法用于对象合并,将待合并对象的所有可枚举属性,复制到目标对象中。

let target = { x: 1};
let s1 = { y: 2 };
let s2 = { z: 3 };
Object.assign(target, s1, s2); // {x: 1, y: 2, z: 3}

(1)如果目标对象与源对象用同名属性,或多个源对象具有同名属性,则后面的属性会覆盖前面的属性

let target2 = { x: 1};
let s3 = { x: 3, y: 2 };
let s4 = { y: 4,  z: 3 };
Object.assign(target2, s3, s4); // {x: 3, y: 4, z: 3}

(2)Object.assign()是浅拷贝,如果源对象的某个属性值是对象,那么目标对象拷贝到的是这个 对象的引用。

let source = {person: { name: 'Clearlove'}}
let target = Object.assign({}, source)

source.person.name = 'Meiko'
target.person.name  // 'Meiko'

对于这种嵌套的对象,一旦遇到同名属性,Object.assign()的处理方法是替换,而不是添加。

let source = {person: { name: 'Clearlove' }}
let target = {person: { name: 'Meiko', age: 18 }}
Object.assign(target, source) // {person: { name: 'Clearlove' }}  // source的person替换target的person

浅拷贝与深拷贝与赋值的区别:

赋值   var b=a;
---- 和原数据指向同一对象------ 第一层数据为基础数据类型--会改变
---- 第一层数据包含子对象--会改变
浅拷贝  Object.assign
---- 和原数据指向同一对象------ 第一层数据为基础数据类型--不会改变
---- 第一层数据包含子对象--会改变
let a={b:1,c:{d:2}}
let e=Object.assign({},a);
a.b=20;
a.c.d=3;
//改变源对象(第一层数据包含子对象)拷贝的对象会改变
console.log("🚀 ~ file: deepClone.js ~ line 50 ~ e", e)
深拷贝  1.JSON.stringify() 这种方法虽然可以实现数组或对象深拷贝,但不能处理函数
2.递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝
//改变源对象(第一层数据包含子对象,不包含)拷贝的对象都会改变
实现原理: 
(1)typeof obj !== "object"不为对象的时候就是递归到底了
(2)定义一个空对象接这个递归返回的值;
(3)第一次是不为对象的返回 之后是这个函数的返回 return result;
(4)map可以对象作为key值
Map {
  { a: { b: [Object] }, d: 2 } => {},
  { b: { c: 111 } } => {},
  { c: 111 } => {}
}
/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 * @param {Map} map 用于存储循环引用对象的地址
 */
let objDeep={a:{b:{c:111}},d:2};
 function deepClone(obj = {}, map = new Map()) {
    if (typeof obj !== "object") {//当遍历到 111的时候 返回不再继续递归
      return obj;
    }
    if (map.get(obj)) {
      return map.get(obj);
    }
    let result = {};
    // 初始化返回结果
    if (
      obj instanceof Array ||
      // 加 || 的原因是为了防止 Array 的 prototype 被重写,Array.isArray 也是如此
      Object.prototype.toString(obj) === "[object Array]"
    ) {
      result = [];
    }
    // 防止循环引用
    map.set(obj, result);
    for (const key in obj) {
      // 保证 key 不是原型属性
      if (obj.hasOwnProperty(key)) {
        // 递归调用
        result[key] = deepClone(obj[key], map);
      }
    }
    // 返回结果
    return result;
  }
  let getData=deepClone(objDeep);
 console.log("🚀 ~ file: deepClone.js ~ line 48 ~ getData", getData)
 
3.函数库lodash 该函数库也有提供_.cloneDeep用来做 Deep Copy
---- 和原数据指向同一对象------ 第一层数据为基础数据类型--不会改变
---- 第一层数据包含子对象--不会改变

const obj = { name: 'global obj',age:{ageInner:18} }
const funcObj = Object.assign({}, obj)
funcObj.name = 'func obj';
funcObj.age.ageInner=8;
console.log(obj)//{ name: 'global obj', age: { ageInner: 8 } }

3、Object.keys()
返回一个由给定对象的自身可枚举属性组成的数组。

let person = {name:"张三",age:25,address:"深圳"}
Object.keys(person) // ["name", "age","address"]

对象转 数组

//data0 {133179548792890404: "YPZB",133185397229854127: "Vip钻石卡"}
Object.keys(data0).map((item) => {
  this.policyMapOpt.push({
    value: item,
    label: data0[item],
  });
});
// this.policyMapOpt  [{label: "YPZB",value: "133179548792890404"},{label: "Vip钻石卡",value: "133185397229854127"}]

4、Object.values()
以数组的形式输出对象所有的值,省去我们手动迭代取出对象每个属性的值。(属性值—》合成数组)

const obj = {
    book: "Learning ES2017 (ES8)",
    author: "前端达人",
    publisher: "前端达人",
    useful: true
};
console.log(Object.values(obj));
// [ 'Learning ES2017 (ES8)', '前端达人', '前端达人', true ]

5、Object.entries()
可用于将对象转换为键/值对的数组形式。 即一个二维数组,数组的每个元素是一个包含键和值的数组。

const obj = {
    book: "Learning ES2017 (ES8)",
    author: "前端达人",
    publisher: "前端达人",
    useful: true
};
console.log(Object.entries(obj));
// [ [ 'book', 'Learning ES2017 (ES8)' ],[ 'author', '前端达人' ], [ 'publisher', '前端达人' ],[ 'useful', true ] ]

例子

const obj=this.options.hallInfoMap
const arrMap = [];
Object.entries(obj).forEach(([key, val]) => {
  arrMap.push({ 'label': val,'value': key });
});
return arrMap;

ECMAScript 2015

//01. let 修复了变量声明提升现象 --------------------------------------------

// console.log(foo)
 var foo = 'zce'

// console.log(foo)
let foo = 'zce'
//02. const 恒量声明过后不允许重新赋值 --------------------------------------------
// const name = 'zce'
// 恒量声明过后不允许重新赋值
name = 'jack'

// 恒量要求声明同时赋值
 const name
 name = 'zce' // 报错
// 恒量只是要求内层指向不允许被修改
 const obj = {}
// 对于数据成员的修改是没有问题的
 obj.name = 'zce'
// obj = {}
//03-数组的解构-array-destructuring  --------------------------------------------
const arr = [100, 200, 300]

const foo = arr[0]
const bar = arr[1]
const baz = arr[2]
console.log(foo, bar, baz)

const [foo, bar, baz] = arr
console.log(foo, bar, baz)

const [, , baz] = arr
console.log(baz)

const [foo, ...rest] = arr
console.log(rest)

const [foo, bar, baz = 123, more = 'default value'] = arr
console.log(bar, more)

const path = '/foo/bar/baz'
// const tmp = path.split('/')
// const rootdir = tmp[1]

const [, rootdir] = path.split('/')
console.log(rootdir)

//04-对象的解构-array-destructuring  --------------------------------------------
const obj = { name: 'zce', age: 18 }
const name = 'tom'
const { name: objName = 'jack' } = obj
console.log(objName)
const { log } = console
log('foo')
log('bar')
log('123')

//05- 模板字符串 --------------------------------------------
//反引号包裹 ``
// 允许换行
const str = `hello es2015,

this is a \`string\``
console.log(str)
///hello es2015,     

//this is a `string`

const name = 'tom'
// 可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置
const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}`
console.log(msg)
//hey, tom --- 3 ---- 0.1093110008934588

//06- 带标签的模板字符串--------------------------------------------

// 模板字符串的标签就是一个特殊的函数,
// 使用这个标签就是调用这个函数
// const str = console.log`hello world`
const name = 'tom'
const gender = false

function myTagFunc (strings, name, gender) {
  console.log(strings, name, gender)//[ 'hey, ', ' is a ', '.' ] tom false
  const sex = gender ? 'man' : 'woman'
  return strings[0] + name + strings[1] + sex + strings[2]
}

const result = myTagFunc`hey, ${name} is a ${gender}.`
console.log(result)//hey, tom is a woman.

// 07-字符串的扩展方法--------------------------------------------

const message = 'Error: foo is not defined.'

console.log(
  message.startsWith('Error'),//true
  message.endsWith('.'),//true
  message.includes('foo')//true
)

// 08-函数参数的默认值--------------------------------------------
function foo (enable) {
  // 短路运算很多情况下是不适合判断默认参数的,例如 0 '' false null
  // enable = enable || true    //这个是有问题的
   enable = enable === undefined ? true : enable
  console.log('foo invoked - enable: ')
  console.log(enable)
}

// 默认参数一定是在形参列表的最后
function foo (enable = true) {
  console.log('foo invoked - enable: ')
  console.log(enable)
}
foo(false)

// 09- 剩余参数--------------------------------------------
function foo () {
  console.log(arguments)//[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
}

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

// 10- 展开数组参数--------------------------------------------
const arr = ['foo', 'bar', 'baz']

// console.log(
//   arr[0],
//   arr[1],
//   arr[2],
// )
// console.log.apply(console, arr) 
console.log(...arr)//foo bar baz

// 11- 箭头函数--------------------------------------------

// function inc (number) {
//   return number + 1
// }

// 最简方式
 const inc = n => n + 1
 
 // 完整参数列表,函数体多条语句,返回值仍需 return
const inc = (n, m) => {
  console.log('inc invoked')
  return n + 1
}

const arr = [1, 2, 3, 4, 5, 6, 7]
// arr.filter(function (item) {
//   return item % 2
// })

// 常用场景,回调函数
arr.filter(i => i % 2)

// 12- 箭头函数与 this--------------------------------------------
// 箭头函数不会改变 this 指向

const person = {
  name: 'tom',
  // sayHi: function () {
  //   console.log(`hi, my name is ${this.name}`)
  // }
  sayHi: () => {
    console.log(`hi, my name is ${this.name}`)
  },
  sayHiAsync: function () {
    // const _this = this
    // setTimeout(function () {
    //   console.log(_this.name)
    // }, 1000)

    console.log(this)
    setTimeout(() => {
      // console.log(this.name)
      console.log(this)
    }, 1000)
  }
}

person.sayHiAsync()
// 13-对象字面量--------------------------------------------

const bar = '345'
const obj = {
  foo: 123,
  // bar: bar
  // 属性名与变量名相同,可以省略 : bar
  bar,
  // method1: function () {
  //   console.log('method111')
  // }
  // 方法可以省略 : function
  method1 () {
    console.log('method111')
    // 这种方法就是普通的函数,同样影响 this 指向。
    console.log(this)//obj对象
  },
  // Math.random(): 123 // 不允许
  // 通过 [] 让表达式的结果作为属性名
  [bar]: 123
}

obj[Math.random()] = 123

console.log(obj)
obj.method1()

// 14-Object.assign 方法--------------------------------------------
//Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,
//然后返回目标对象。但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
const source1 = {
  a: 123,
  b: 123
}
const source2 = {
  b: 789,
  d: 789
}
const target = {
  a: 456,
  c: 456
}

const result = Object.assign(target, source1, source2)
console.log(target)//{ a: 123, c: 456, b: 789, d: 789 }
console.log(result === target)//true
// 应用场景

function func (obj) {
  // obj.name = 'func obj'
  // console.log(obj)
  const funcObj = Object.assign({}, obj)
  funcObj.name = 'func obj'
  console.log(funcObj)//{ name: 'func obj' }
}

const obj = { name: 'global obj' }
func(obj)
// console.log(obj)//{ name: 'global obj' }


// 15-Object.is 方法--------------------------------------------
console.log(
  // 0 == false              // => true
  // 0 === false             // => false
  // +0 === -0               // => true
  // NaN === NaN             // => false
  // Object.is(+0, -0)       // => false
  // Object.is(NaN, NaN)     // => true
)
//16.proxy 对象--------------------------------------------
const person = {
  name: {nameInner:'zce'},
  age: 20
}

const personProxy = new Proxy(person, {
  // 监视属性读取
  get (target, property) {
    console.log(target, property)
    return property in target ? target[property] : 'default'
    // return 100
  },
  // 监视属性设置
  set (target, property, value) {
    if (property === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError(`${value} is not an int`)
      }
    }
    target[property] = value
    console.log(target, property, value)
  }
})

personProxy.age = 100//执行set
personProxy.name.nameInner="wangji";
console.log(person.age,"--person--")//执行get
// Proxy 对比 Object.defineProperty() ===============
// 优势1:Proxy 可以监视读写以外的操作 --------------------------
const person = {
  name: 'zce',
  age: 20
}

const personProxy = new Proxy(person, {
  deleteProperty (target, property) {
    console.log('delete', property)
    delete target[property]
  }
})

delete personProxy.age
console.log(person)//{ name: 'zce' }
// 优势2:Proxy 可以很方便的监视数组操作 --------------------------
const list = []
const listProxy = new Proxy(list, {
  set (target, property, value) {
    console.log('set', property, value)
    target[property] = value
    return true // 表示设置成功
  }
})

listProxy.push(100)
listProxy.push(100)
//set 0 100
//set length 1
//set 1 100
//set length 2
// 优势3:Proxy 不需要侵入对象 --------------------------
// defineProperty 需要每个key侵入
const person = {}
Object.defineProperty(person, 'name', {
  get () {
    console.log('name 被访问')
    return person._name
  },
  set (value) {
    console.log('name 被设置')
    person._name = value
  }
})
Object.defineProperty(person, 'age', {
  get () {
    console.log('age 被访问')
    return person._age
  },
  set (value) {
    console.log('age 被设置')
    person._age = value
  }
})
person.name = 'jack'
console.log(person.name)
-------------------------------------
// Proxy 方式更为合理
const person2 = {
  name: 'zce',
  age: 20
}
const personProxy = new Proxy(person2, {
  get (target, property) {
    console.log('get', property)
    return target[property]
  },
  set (target, property, value) {
    console.log('set', property, value)
    target[property] = value
  }
})
personProxy.name = 'jack'
console.log(personProxy.name)
//17.Reflect 对象--------------------------------------------
const obj = {
  foo: '123',
  bar: '456'
}
const proxy = new Proxy(obj, {
  get (target, property) {
    console.log('watch logic~')
    return Reflect.get(target, property)
  }
})
console.log(proxy.foo)
//相比之前的更加统一
const obj = {
  name: 'zce',
  age: 18
}

// console.log('name' in obj)
// console.log(delete obj['age'])
// console.log(Object.keys(obj))

console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))

// 18.class 关键词--------------------------------------------
//构造函数方法虽然好用,但是存在浪费内存的问题。对于每一个实例对象,每一次生成一个实例,都必须 生成重复的内容,多占用内存
//js中,每一个构造函数都又有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。实例化的__proto__指向构造函数的原型对象。Person.prototype==new Person().__proto__ 

// function Person (name) {
//   this.name = name
// }

// Person.prototype.say = function () {
//   console.log(`hi, my name is ${this.name}`)
// }

class Person {
  constructor (name) {///相当于构造函数 
  //Object.constructor  == Function  //true
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}
const p = new Person('tom')
p.say()

//static 方法--------------------------------------------
//static 方法的this只能指向这个 class Person  不能指向他的实例对象new person
class Person {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
  static create (name) {
    return new Person(name)
  }
}

const tom = Person.create('tom')
tom.say()

//19. extends 继承--------------------------------------------
class Person {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}
class Student extends Person {
  constructor (name, number) {
    super(name) // 父类构造函数
    this.number = number
  }
  hello () {
    super.say() // 调用父类成员
    console.log(`my school number is ${this.number}`)
  }
}
const s = new Student('jack', '100')
s.hello()

//20.set--------------------------------------------
// Set 数据结构
const s = new Set()
s.add(1).add(2).add(3).add(4).add(2)

console.log(s)//Set(4) {1, 2, 3, 4}
 s.forEach(i => console.log(i)) // 1 2 3 4
 for (let i of s) {
   console.log(i)// 1 2 3 4
 }
console.log(s.size)//4
console.log(s.has(100))//false
console.log(s.delete(3))//true
console.log(s)// Set(3) {1, 2, 4}
// s.clear()
console.log(s)//Set(0) {size: 0}
// 应用场景:数组去重
const arr = [1, 2, 1, 3, 4, 1]
// const result = Array.from(new Set(arr))
const result = [...new Set(arr)]
console.log(result)

// 弱引用版本 WeakSet
// 差异就是 Set 中会对所使用到的数据产生引用
// 即便这个数据在外面被消耗,但是由于 Set 引用了这个数据,所以依然不会回收
// 而 WeakSet 的特点就是不会产生引用,
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

//21.Map--------------------------------------------
可以对象作为key
const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{ a: 1 }] = 'value'

console.log(Object.keys(obj))//[ '123', 'true', '[object Object]' ]
console.log(obj['[object Object]'])//value

const m = new Map()
const tom = { name: 'tom' }
m.set(tom, 90)
console.log(m)//Map { { name: 'tom' } => 90 }
console.log(m.get(tom))//90
m.has()//90

 m.has(tom) //true
 m.delete(tom) //map {}
m.clear()
console.log(m)//map {}
m.forEach((value, key) => {
  console.log(value, key)//90 { name: 'tom' }
})

//23. Symbol 数据类型--------------------------------------------
// 场景1:扩展对象,属性名冲突问题

// shared.js ====================================
const cache = {}
// a.js =========================================
cache['a_foo'] = Math.random()

// b.js =========================================
cache['b_foo'] = '123'

console.log(cache)//{ a_foo: 0.8687478499267698, b_foo: '123' }
// 两个 Symbol 永远不会相等
console.log(Symbol() === Symbol()) //false
// Symbol 描述文本
console.log(Symbol('foo'))//Symbol(foo)
console.log(Symbol('bar'))//Symbol(bar)
console.log(Symbol('baz'))//Symbol(baz)

// 也可以在计算属性名中使用

const obj = {
  [Symbol()]: 123
}
console.log(obj)
// 案例2:Symbol 模拟实现私有成员
// a.js ======================================
const name = Symbol()
const person = {
  [name]: 'zce',
  say () {
    console.log(this[name])
  }
}
// 只对外暴露 person
// b.js =======================================
// 由于无法创建出一样的 Symbol 值,
// 所以无法直接访问到 person 中的「私有」成员
// person[Symbol()]  // undefined
person.say()
console.log(
  Symbol() === Symbol(),//false
  Symbol('foo') === Symbol('foo')//false
)

// Symbol 全局注册表 ----------------------------------------------------
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2)//true

// 内置 Symbol 常量 ---------------------------------------------------
console.log(Symbol.iterator)//Symbol(Symbol.iterator)
console.log(Symbol.hasInstance)//Symbol(Symbol.hasInstance)
const obj = {
  [Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString())//[object XObject]

// Symbol 属性名获取 ---------------------------------------------------
const obj = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}
for (var key in obj) {
  console.log(key)//foo
}
console.log(Object.keys(obj))//[ 'foo' ]
console.log(JSON.stringify(obj))//{"foo":"normal value"}
console.log(Object.getOwnPropertySymbols(obj))//[ Symbol() ]
//24. for...of 循环---------------------------------------------------
const arr = [100, 200, 300, 400]

for (const item of arr) {
  console.log(item)// 100 200 300 400
}
// for...of 循环可以替代 数组对象的 forEach 方法
arr.forEach(item => {
  console.log(item)
})
for (const item of arr) {
  console.log(item)// 100  200  大于100 的时候就break;中断
  if (item > 100) {
    break
  }
}
// forEach 无法跳出循环,必须使用 some 或者 every 方法
arr.forEach() // 不能跳出循环
arr.some()
arr.every()

// 遍历 Set 与遍历数组相同
const s = new Set(['foo', 'bar'])
for (const item of s) {
  console.log(item)// foo bar
}
// 遍历 Map 可以配合数组结构语法,直接获取键值
const m = new Map()
m.set('foo', '123')
m.set('bar', '345')
for (const [key, value] of m) {
  console.log(key, value)// foo 123  bar 345
}
// 普通对象不能被直接 for...of 遍历
const obj = { foo: 123, bar: 456 }
for (const item of obj) {
  console.log(item)//obj is not iterable 可迭代
}

//25-iterator---------------------------------------------------
// 迭代器(Iterator)

const set = new Set(['foo', 'bar', 'baz'])

const iterator = set[Symbol.iterator]()

// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())

while (true) {
  const current = iterator.next()
  if (current.done) {
    break // 迭代已经结束了,没必要继续了
  }
  console.log(current.value)//foo  bar  baz
}

// 26.实现可迭代接口(Iterable)---------------------------------------------------
// const obj = {
//   [Symbol.iterator]: function () {
//     return {
//       next: function () {
//         return {
//           value: 'zce',
//           done: true
//         }
//       }
//     }
//   }
// }
const obj = {
  store: ['foo', 'bar', 'baz'],
  [Symbol.iterator]: function () {
    let index = 0
    const self = this
    return {
      next: function () {
        const result = {
          value: self.store[index],
          done: index >= self.store.length
        }
        index++
        return result
      }
    }
  }
}
for (const item of obj) {
  console.log('循环体', item)
  //循环体 foo
  //循环体 bar
  //循环体 baz
}
//27-iterator-pattern---------------------------------------------------
// 迭代器设计模式
// 场景:你我协同开发一个任务清单应用
// 我的代码 ===============================
const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],

  // 提供统一遍历访问接口
  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for (const item of all) {
      callback(item)
    }
  },

  // 提供迭代器(ES2015 统一遍历访问接口)
  [Symbol.iterator]: function () {
    const all = [...this.life, ...this.learn, ...this.work]
    let index = 0
    return {
      next: function () {
        return {
          value: all[index],
          done: index++ >= all.length
        }
      }
    }
  }
}

todos.each(function (item) {
  console.log(item)
//吃饭
//睡觉
//打豆豆
//语文
//数学
//外语
//喝茶
})
for (const item of todos) {
  console.log(item)
}

//28-Generator 函数--------------------------------------------------
function * foo () {
  console.log('1111')
  yield 100
  console.log('2222')
  yield 200
  console.log('3333')
  yield 300
}

const generator = foo()

console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
console.log(generator.next()) // 。。。
console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined
//1111
//{ value: 100, done: false }
//2222
//{ value: 200, done: false }
//3333
//{ value: 300, done: false }
//{ value: undefined, done: true }
//29-generator-usage--------------------------------------------------
// Generator 应用
// 案例1:发号器
function * createIdMaker () {
  let id = 1
  while (true) {
    yield id++
  }
}

const idMaker = createIdMaker()

console.log(idMaker.next().value)//1	
console.log(idMaker.next().value)//2	
console.log(idMaker.next().value)//3
console.log(idMaker.next().value)//4

// 案例2:使用 Generator 函数实现 iterator 方法

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
  [Symbol.iterator]: function * () {
    const all = [...this.life, ...this.learn, ...this.work]
    for (const item of all) {
      yield item
    }
  }
}

for (const item of todos) {
  console.log(item)//'吃饭' '睡觉' '打豆豆' '语文' '数学' '外语' '喝茶'
}


ECMAScript 2016

// includes---------------------------------------------------
// 直接返回是否存在指定元素
 console.log(arr.includes('foo'))
// 能够查找 NaN
// console.log(arr.includes(NaN))

// 指数运算符 ---------------------------------------------------
// console.log(Math.pow(2, 10))
console.log(2 ** 10)

ECMAScript 2017

const obj = {
   foo: 'value1',
   bar: 'value2'
}

// Object.values -----------------------------------------------------------
console.log(Object.values(obj))

// Object.entries ----------------------------------------------------------
console.log(Object.entries(obj))
 for (const [key, value] of Object.entries(obj)) {
   console.log(key, value)
 }
console.log(new Map(Object.entries(obj)))

// Object.getOwnPropertyDescriptors ----------------------------------------
//改变firstName之后获取的还是之前的key
const p1 = {
  firstName: 'Lei',
  lastName: 'Wang',
  get fullName () {
    return this.firstName + ' ' + this.lastName
  }
}
console.log(p1.fullName)
const p2 = Object.assign({}, p1)
p2.firstName = 'zce'
console.log(p2)
//Lei Wang
//{ firstName: 'zce', lastName: 'Wang', fullName: 'Lei Wang' }
const descriptors = Object.getOwnPropertyDescriptors(p1)
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'zce'
console.log(p2.fullName)
//zce Wang

// String.prototype.padStart / String.prototype.padEnd  --------------------
const books = {
  html: 5,
  css: 16,
  javascript: 128
}
for (const [name, count] of Object.entries(books)) {
  console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, '0')}`)
//html------------|005
//css-------------|016
//javascript------|128
}

// 在函数参数中添加尾逗号  -----------------------------------------------------
 function foo (
   bar,
   baz,
 ) {
}
 const arr = [
  100,
  200,
  300,
]
const arr = [
   100,
   200,
   300,
   400,
]
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ES6解构赋值的好处有以下几点: 1. 简化代码:ES6解构赋值可以让我们更简洁地从数组对象中提取,并将其赋给变量。这样可以减少冗余的代码,使代码更加简洁易读。 2. 提高可读性:通过使用解构赋值,我们可以清晰地表达出我们想要提取的的含义,使代码更易于理解和维护。 3. 方便的交换变量:使用解构赋值可以方便地交换两个变量的,而不需要使用额外的中间变量。 4. 快速提取对象属性解构赋值可以快速提取对象中的属性,并将其赋给变量。这样可以方便地访问和操作对象属性。 5. 函数参数的灵活应用:解构赋值可以用于函数的参数中,可以方便地从传入的对象数组中提取所需的,使函数的参数更加灵活。 6. 处理JSON数据:解构赋值可以方便地从JSON数据中提取所需的,使得处理JSON数据更加方便快捷。 7. 支持默认解构赋值可以为变量设置默认,当提取的不存在时,可以使用默认来代替,避免出现undefined的情况。 8. 多层嵌套解构:ES6解构赋值支持多层嵌套的解构,可以方便地从复杂的数据结构中提取所需的。 9. 可以与扩展运算符结合使用:解构赋值可以与扩展运算符(...)结合使用,可以方便地提取数组对象中的部分,并将其与其他合并或进行其他操作。 总之,ES6解构赋值可以使我们的代码更加简洁、可读性更高,并且提供了更多的灵活性和便利性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值