# ES2015

ES2015 (opens new window)

ES5

TIP

  • 解决原有语法上的一些问题或者不足,那例如像let和const所提供的。块级作用域。
  • 对原有语法进行增强,使之变得更为便捷易用。例如,像解构展开还有参数默认值模板字符串等等。
  • 就是全新的对象,全新的方法,还有全新的功能。例如,像promise还有proxy以及像object的assign的方法之类。- 全新的数据类型和数据结构,例如像symbol,还有set和map等等。

# let 和 const

# let 与块级作⽤域

之前作用域: 函数作用域 全局作用域

  • 声明的成员只会在所声明的块中生效
if (true) {
  // var foo = 'zce'
  let foo = 'zce'
  console.log(foo)
}
  • let 在 for 循环中的表现
for (var i = 0; i < 3; i++) {
  for (var i = 0; i < 3; i++) {
    console.log(i)
  }
  console.log('内层结束 i = ' + i)
}

for (var i = 0; i < 3; i++) {
  for (let i = 0; i < 3; i++) {
    console.log(i)
  }
  console.log('内层结束 i = ' + i)
}
  • 循环绑定事件,事件处理函数中获取正确索引
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}
elements[2].onclick()//3

var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = (function (i) {
    return function () {
      console.log(i)
    }
  })(i)
}
elements[0].onclick() // 0 产生了闭包

var elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}
elements[0].onclick()//0
  • for 循环会产生两层作用域
for (let i = 0; i < 3; i++) {
    let i = 'foo'
    console.log(i)//foo
  }
  • let 修复了变量声明提升现象

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

console.log(foo) // 报错
let foo = 'zce'

# const

只读的常量/恒量

  • 恒量声明过后不允许重新赋值
const name = 'zce'
// 恒量声明过后不允许重新赋值
name = 'jack'

  • 恒量要求声明同时赋值
// 恒量要求声明同时赋值
const name
name = 'zce'
  • 恒量只是要求内层指向不允许被修改
// 恒量只是要求内层指向不允许被修改
const obj = {}
// 对于数据成员的修改是没有问题的
obj.name = 'zce'

obj = {}

alt text

# ES5中数组遍历有多少方法

# for 循环

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

# forEach

// forEach
arr.forEach(function (item) {
  if (item === 2) {
    // continue
  }
  // console.log(item)
})

# every

// every
arr.every(function (item) {
  if (item === 2) {

  } else {
    // console.log(item)
  }
  return true
})

# for in

// for in
// arr.a = 8
for (let index in arr) {
  if (index * 1 === 2) {
    continue
  }
  console.log(index, arr[index])
}
const Price = {
  A: [1.5, 2.3, 4.5],
  B: [3, 4, 5],
  C: [0.5, 0.8, 1.2]
}

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

# for of

// for of
for (let item of arr) {
  // console.log(item)
}

# 转换数组

# [].slice.call

// let args = [].slice.call(arguments) // collection
// let imgs = [].slice.call(document.querySelectorAll('img')) // NodeList
// console.log(args)

# Array.from

// let args = Array.from(arguments)
// let imgs = Array.from(document.querySelectorAll('img'))
// imgs.forEach()
// Array.from(arrayLike,mapFn,thisArg)
// let array = Array(5)
// for (let i = 0, len = array.length; i < len; i++) {
//   array[i] = 1
// }
// console.log(array)
// let array = Array.from({ length: 5 }, function () { return 1 })
// console.log(array)
// {0:'a',1:'b',length:2}

# 生成新数组

# Array

// let array = Array(5)
// let array = []

# Array.of

// let array = Array.of(1)
// console.log(array)

# Array.fill

// let array = Array(5).fill(7)
// console.log(array)
// Array.fill(value,start,end)
let array = [1, 2, 3, 4, 5]
// console.log(array.fill(8, 2, 4))
// let find = array.filter(function (item) {
//   return item % 2 === 0
// })
// console.log(find)
// Array.prototype.find
let find = array.findIndex(function (item) {
  return item % 2 === 0
})
console.log(find)
// Array.prototype.findIndex

# 数组的解构

// 数组的解构

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, more] = arr
// console.log(more)// undefined

// 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)

# 对象的解构

// 对象的解构

const obj = { name: 'zce', age: 18 }

// const { name } = obj
// console.log(name)

// const name = 'tom'
// const { name: objName } = obj
// console.log(objName)

// const name = 'tom'
// const { name: objName = 'jack' } = obj
// console.log(objName)

const { log } = console
log('foo')
log('bar')
log('123')

# 字符串方法

# Template literals(模板字⾯量)

// 模板字符串

// 反引号包裹
// const str = `hello es2015, this is a string`

// 允许换行
// const str = `hello es2015,

// this is a \`string\``

// console.log(str)

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

# startsWith

TIP

startsWith(str, pos); 该方法用于判定字符串是否是以另外一个字符串开头 str: 开头字符串 pos: 偏移位置

// startsWith
// 定义一个新的字符串
let str = "今天天气很冷";
// 判断str是否是以"今天"开头
let result = str.startsWith("今天");
// console.log(result);
// 判断"天天气是否以‘今天’开头"
let result1 = str.startsWith("今天", 1);
String.prototype.startsWith = function(str, pos) {
	var pos = pos || 0;
	// 获取新字符串 
	var newStr = this.slice(pos);
	return newStr.indexOf(str) === 0 ? true : false;
}

# endsWith

TIP

endsWith(str, pos); 该方法用于判定字符串是否是以另外一个字符串结尾 str: 结尾字符串 pos: 偏移位置 注: 该值是slice的第二个参数 默认从头开始截取 如果不传递该参数,该参数默认是调用endsWith方法的字符串的长度

// 定义一个新的字符串
let str = "今天天气很冷";
// 调用endsWith方法
let result = str.endsWith("天天", 3);
console.log(result);
// 重写该方法
String.prototype.endsWith = function(str, pos) {
	// 判定pos是否有值
	// 如果有值,则偏移 应该从头开始截取到该位置 不包含该位置的字符
	// 如果没有值 则使用字符串的长度
	var pos = pos || this.length;
	// 截取新的字符串
	var newStr = this.slice(0, pos);
	// 定义正则表达式
	var reg = new RegExp(str + "$", "g");
	// 判定
	return reg.test(newStr);
}

# includes

TIP

includes(str, pos); 该方法用于判定字符串中是否包含另外一个字符串 str: 被包含的字符串 pos: 偏移量 返回值:布尔值 true表示包含 false表示不包含

// includes方法用于判定一个字符串是否包含另外一个字符串
let str = "今天天气很冷";
// 调用includes方法
let result = str.includes("天气", 3);
console.log(result);
String.prototype.includes = function(str, pos) {
	// 匹配pos
	var pos = pos || 0;
	// 获取字符串
	var newStr = this.slice(pos);
	// 判定newStr中是否具备str
	return newStr.indexOf(str) === -1 ? false : true;
}

# repeat

该方法用于将字符串重复多少次 一个参数是重复次数

// 定义一个字符
let str = '灬';
//  重复100次
let newStr = str.repeat(100);
console.log(newStr, newStr.length)
// 还原
String.prototype.repeat = function(num) {
	// 定义一个字符串
	var str = "";
	// 循环num次
	for (var i = 0; i < num; i++) {
		str += this;
	}
	return str;
}

# 数组方法

ES6对数组进行了更多的方法的扩展。方法有两种:第一种,静态方法,也就是类的方法。第二种,普通方法,也就是类的实例的方法。

# Array.of

TIP

该方法用于定义数组。 目前已有的定义数组的方法: 字面量: [] 构造函数:new Array() 构造函数定义数组有一个问题:当参数是数字,并且只有一个参数时,会表示数组的长度。当参数有多个时,每一个参数都是数组的成员。

// 问题:当一个参数时,到底代表长度还是代表成员。
var arr3 = Array(4);  // 将参数当做新数组的长度
var arr4 = Array(4, 5);

// 解决:通过Array.of
var arr5 = Array.of(4);  // 将参数当做新数组的成员
var arr6 = Array.of(4, 5);
// 还原
Array.of = function() {
	return [].slice.call(arguments);
}

# Array.from

TIP

该方法的作用就是将类数组对象转为数组。 类数组对象: 具备数字下标、具备length属性、又不是数组。这样的对象我们统一称之为类数组对象。

// 获取lis
var $lis = $("li");
// 获取lis
var lis = document.getElementsByTagName("li");

// 转为数组 通过Array.from
var arr = Array.from($lis);
var arr1 = Array.from(lis);
// 还原
Array.from = function(likeArray) {
	return [].slice.call(likeArray);
}

# find

TIP

arr.find(handler) 该方法用于查询数组中某项 handler: 函数 该函数返回一个布尔值 符合该函数的成员会被返回 一旦查询到 就会停止查询

// find方法 该方法接收一个函数作为参数函数要返回一个布尔值 当该值为true时 返回成员。
var arr = ["张三", "李四", "王五", "赵六", "田七", "郭七"];
// 寻找arr数组中 带有“七”这个字的成员
var arr1 = arr.filter(function(value, index) {
	return value.includes("七");
})
// find方法
var value = arr.find(function(value, index) {
	return value.includes("七");
})
// 还原:
Array.prototype.find = function(handler) {
	// 循环执行
	for (var i = 0; i < this.length; i++) {
		// 判定 
		if (handler(this[i], i, this)) {
			// 如果是真 就返回当前成员
			return this[i];
		}
		// 否则继续循环
	}
}

# findIndex

TIP

arr.findIndex(handler) 该方法用于查询数组中某项的索引 handler: 函数 该函数返回一个布尔值 符合该函数的成员的索引会被返回 一旦查询到 就会停止查询

// findIndex方法 该方法接收一个函数作为参数函数要返回一个布尔值 当该值为true时 返回成员的索引。
var arr = ["张三", "李四", "王五", "赵六", "田七", "郭七"];

// find方法
var value = arr.findIndex(function(value, index) {
	console.log(value, arguments)
	return value.includes("七");
})
// 还原:
Array.prototype.find = function(handler) {
	// 循环执行
	for (var i = 0; i < this.length; i++) {
		// 判定 
		if (handler(this[i], i, this)) {
			// 如果是真 就返回当前成员
			return i;
		}
		// 否则继续循环
	}
}

# copyWithin

TIP

arr.copyWithin(pos, start, end) 该方法用于数组的内部复制 pos: 覆盖的开始位置 start: 复制的开始位置 (包含) end: 复制的结束位置 (不包含) 3个值都是下标。 返回值:原数组

// copyWithin()方法  作用:数组的内部复制 会改变原数组
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var arr1 = arr.copyWithin(0, 5, 7);
// 还原
Array.prototype.copyWithin = function(pos, start, end) {
	// 复制start到end的数组
	var newArr = this.slice(start, end);
	// 保存原来的length
	var length = this.length;
	// 循环
	for (var i = 0; i < newArr.length; i++) {
		this[pos + i] = newArr[i];
	}
	// 设置length属性
	this.length = length;
	// 返回原数组
	return this;
}
// 还原
Array.prototype.copyWithin = function(pos, start, end) {
	// 复制start到end的数组
	var newArr = this.slice(start, end);
	// 保存原来的length
	var length = this.length;
	// 从pos位置开始删除原数组的项 删newArr.length项
	// 因为newArr是一个数组,而我们数组现阶段是拆分不开的。所以我们只能够使用数组,那么一个方法与数组,联想到apply方法。
	// 第一个参数是this,因为splice就是在操作原来的数组所以this传递在第一位。
	// 第二个参数是数组,数组的每一项表示splice所需要的参数 
	// apply的第二个参数是一个数组 数组的第一项表示删除的位置 也就是pos
	// apply的第二个参数是一个数组 数组的第二项表示删除几个 也就是newArr.length
	// 后续的每一个都是要添加的项 就是newArr中的每一项 所以凑出来的数组第一项应该是pos 第二项是newArr.length 剩余的项都在newArry中。
	// [pos, newArr.length] 得到一个数组 该数组与newArr拼接就组成最终的参数数组。
	// 所以代码是 [pos, newArr.length].concat(newArr);
	this.splice.apply(this, [pos, newArr.length].concat(newArr)); 
	// 设置length属性
	this.length = length;
	// 返回原数组
	return this;
}

# Default parameters(默认参数)

# 函数默认参数

// 函数参数的默认值

// 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)

# ...语法 ()

# 剩余参数

// 剩余参数

// function foo () {
//   console.log(arguments)
// }

function foo (first, ...args) {
  console.log(args)
}

foo(1, 2, 3, 4)

# 展开数组

// 展开数组参数

const arr = ['foo', 'bar', 'baz']

// console.log(
//   arr[0],
//   arr[1],
//   arr[2],
// )

// console.log.apply(console, arr)

console.log(...arr)

# Arrow functions

# 箭头函数的定义

TIP

定义语法: () => {} (): 形参列表 =>: 箭头 {}: 函数体

// ES6中新增了一种箭头函数
var fun3 = () => {

} 

# this不变

TIP

以前用function关键字定义出来的函数,或者使用new Function定义的函数,它们里面的this会发生变化。 谁调用该函数,该函数中的this就指向谁。 用箭头函数定义的函数,里面的this不会发生变化。 原版的函数:

// 原版的函数中的this
function fun() {
	console.log(this);
}
fun(); // window
document.body.onclick = fun; // 当点击body时,this指向document.body
fun.call(document); // document 
// 箭头函数与 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}`) // undefined
  },
  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()

# 箭头函数不能作为构造函数

原因:一方面因为this不变,另一方面是因为实现了class关键字 alt text

# 箭头函数中,没有arguments

// 没有了arguments
var fun = (a, b, c, d) => {
	console.log(arguments);
}
fun(1, 2, 3, 4);

alt text

# 获取剩余参数

TIP

ES6中,因为没有了arguments,所以新增了一个语法: ... 可以用...语法获取剩余参数:

// 使用...获取剩余参数
var fun = (a, b, c, ...args) => {
	console.log(args); // 因为当前函数只定义了a, b, c三个形参,可是外面传递了5个,所以剩余的都放在args这个数组中了。...args表示定义了一个数组并将剩余的参数放入该数组。
}
fun(1, 2, 3, 4, 5)
// 使用...获取剩余参数
var fun = (...args) => {
	console.log(args); // 只有一个args参数,所以此时args的内容就是实参组成的数组。
}

fun(1, 2, 3, 4, 5)

# 函数的参数默认值

ES6中增加了函数的参数默认值 符号是 “=”

// 函数的参数默认值
function sum(a=3, b=4) {
	return a + b;
}
console.log(sum(1, 2)); // 3 
console.log(sum());  // 7

参数默认值:当用户传递了参数的时候,使用用户传递的实参。当用户没有传递参数的时候,使用默认值。

# 对象

# 对象的定义

第一个变化:定义对象时,如果属性名和属性值一致,则可以省略冒号和属性值。

var haha = "你好";
var obj = {
	haha: haha
}
var obj1 = {
	haha
}

# 计算属性

可以使用[]将它包括起来。就可以使用运算表达式了

alt text

// 对象字面量

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)
  },
  // Math.random(): 123 // 不允许
  // 通过 [] 让表达式的结果作为属性名
  [bar]: 123
}

// obj[Math.random()] = 123

console.log(obj)
obj.method1()

# 当定义方法时,可以省略: function

// 3 定义对象的方法时,可以省略: function了
var obj = {
	say: function() {
		console.log("你好")
	},
	say1 () {
		console.log("你好帅")
	}
}

# Object.is

TIP

Object.is(one, two); 该方法用于判定两者是否全等 one: 值 two: 另一个值 返回值: 布尔值 如果one和two全等则true 反之则false

// 比较NaN
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false
// 比较 0 和 -0
console.log(Object.is(0, -0)); // false
console.log(0 === -0); // true
console.log(
  // 0 == false              // => true
  // 0 === false             // => false
  // +0 === -0               // => true
  // NaN === NaN             // => false
  // Object.is(+0, -0)       // => false
  // Object.is(NaN, NaN)     // => true
)

# Object.assign(one)

TIP

该方法用于让某个对象拥有其它对象的属性 one:一个对象 剩余的参数数量任意 最终结果:让one拥有剩余的所有参数的属性。

// 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)
// console.log(result === target)

// 应用场景

function func (obj) {
  // obj.name = 'func obj'
  // console.log(obj)

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

const obj = { name: 'global obj' }

func(obj)
console.log(obj)

# Proxy

TIP

ES6中,新增了一个构造函数,Proxy。它能够代理一个目标对象,帮助该对象处理一些事情。 初始化: var proxy = new Proxy(targetObject, handlerObject); targetObject: 被代理的对象 Boss handlerObject:代理对象的处理方式 get:当通过代理对象访问被代理对象的属性时,执行的函数 set:当通过代理对象设置被代理对象的属性时,执行的函数 proxy: 代理对象 秘书

# Proxy 对象

// Proxy 对象

# 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)

  • 优势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);


  • 优势3:Proxy 不需要侵入对象

// 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)

# Reflect

Reflect 内部封装了一系列对对象的底层操作 统一了对象了操作方式

静态方法
Reflect.apply(target, thisArgument, argumentsList)
对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。

Reflect.construct(target, argumentsList[, newTarget])
对构造函数进行 new 操作,相当于执行 new target(...args)。

Reflect.defineProperty(target, propertyKey, attributes)
和 Object.defineProperty() 类似。如果设置成功就会返回 true

Reflect.deleteProperty(target, propertyKey)
作为函数的delete操作符,相当于执行 delete target[name]。

Reflect.get(target, propertyKey[, receiver])
获取对象身上某个属性的值,类似于 target[name]。

Reflect.getOwnPropertyDescriptor(target, propertyKey)
类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined。

Reflect.getPrototypeOf(target)
类似于 Object.getPrototypeOf()。

Reflect.has(target, propertyKey)
判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

Reflect.isExtensible(target)
类似于 Object.isExtensible().

Reflect.ownKeys(target)
返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响).

Reflect.preventExtensions(target)
类似于 Object.preventExtensions()。返回一个Boolean。

Reflect.set(target, propertyKey, value[, receiver])
将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。

Reflect.setPrototypeOf(target, prototype)
设置对象原型的函数。返回一个 Boolean,如果更新成功,则返回 true。
// 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))

# Classes(类)

# 定义类

// class 关键词

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

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

class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}

const p = new Person('tom')
p.say()

# static 方法

// static 方法

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();

# extends 继承

// 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()

# Set数据结构

这是一个新的数据结构,它与数组有关。 不允许重复的

// 初始化一个Set
var set = new Set();
// 初始化一个Set
var set = new Set([1, 1, 2, 1, 3, 1, 4, 5, 5 , 5, 5, 5]);

set alt text

方法:add 用于新增一项,如果该项已经存在,则忽略。如果该项不存在,则添加进去。
方法:clear 用于清空
方法:delete 用于删除一项
方法:has 用于判定是否存在
方法:forEach 用于迭代内部数据
方法:entires、keys、values、用于返回迭代器对象
// Set 数据结构

const s = new Set()

s.add(1).add(2).add(3).add(4).add(2)

// console.log(s)

// s.forEach(i => console.log(i))

// for (let i of s) {
//   console.log(i)
// }

// console.log(s.size)

// console.log(s.has(100))

// console.log(s.delete(3))
// console.log(s)

// s.clear()
// console.log(s)

// 应用场景:数组去重

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 的特点就是不会产生引用,
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

# Map数据结构

可以用任意类型作为键

var map = new Map();

map

方法:clear 清空所有内容
方法:delete 删除某一项
方法:entries、keys、values、forEach 迭代器方法
方法:has 判断某一项是否存在与map中
方法:get 获取某项
方法:set 设置某项
// Map 数据结构

// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{ a: 1 }] = 'value'

// console.log(Object.keys(obj))
// console.log(obj['[object Object]'])

const m = new Map()

const tom = { name: 'tom' }

m.set(tom, 90)

console.log(m)

console.log(m.get(tom))

// m.has()
// m.delete()
// m.clear()

m.forEach((value, key) => {
  console.log(value, key)
})

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

# Symbol 数据类型

独一无二的数据标识符 目前为止,ECMAScript中一共有:number、string、boolean、undefined、null、symbol、object七种数据类型。

// Symbol 数据类型

// 场景1:扩展对象,属性名冲突问题

// // shared.js ====================================

// const cache = {}

// // a.js =========================================

// cache['a_foo'] = Math.random()

// // b.js =========================================

// cache['b_foo'] = '123'

// console.log(cache)

// =========================================================

// const s = Symbol()
// console.log(s)
// console.log(typeof s)

// 两个 Symbol 永远不会相等

// console.log(
//   Symbol() === Symbol()
// )

// Symbol 描述文本

// console.log(Symbol('foo'))
// console.log(Symbol('bar'))
// console.log(Symbol('baz'))

// 使用 Symbol 为对象添加用不重复的键

const obj = {};
obj[Symbol()] = "123";
obj[Symbol()] = "456";
console.log(obj);

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

// 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()]
person.say();

# Symbol.for

// Symbol 补充

// console.log(
//   // Symbol() === Symbol()
//   Symbol('foo') === Symbol('foo')
// )

// Symbol 全局注册表 ----------------------------------------------------

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2)

console.log(
  Symbol.for(true) === Symbol.for('true')
)

// 内置 Symbol 常量 ---------------------------------------------------

console.log(Symbol.iterator)
console.log(Symbol.hasInstance)

// const obj = {
//   [Symbol.toStringTag]: 'XObject'
// }
// console.log(obj.toString())

// Symbol 属性名获取 ---------------------------------------------------

const obj = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}

// for (var key in obj) {
//   console.log(key)
// }
// console.log(Object.keys(obj))
// console.log(JSON.stringify(obj))

console.log(Object.getOwnPropertySymbols(obj))

# for ... of 循环

该循环专门用来循环迭代器的。

TIP

  • 可以使用break 跳出循环
// 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)
//   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)
// }

// 遍历 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
// }

# 迭代器(Iterator)

for of 循环 实现了interable接口 比如:数组 Map Set

 迭代器(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)
}

# 实现可迭代接口(Iterable)

// 实现可迭代接口(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)
}

# 迭代器设计模式

// 迭代器设计模式

// 场景:你我协同开发一个任务清单应用

// 我的代码 ===============================

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
        }
      }
    }
  }
}

// 你的代码 ===============================

// for (const item of todos.life) {
//   console.log(item)
// }
// for (const item of todos.learn) {
//   console.log(item)
// }
// for (const item of todos.work) {
//   console.log(item)
// }

todos.each(function (item) {
  console.log(item)
})

console.log('-------------------------------')

for (const item of todos) {
  console.log(item)
}

# Promises

# Generators

TIP

这是一个新的特殊的函数。 它的定义方式 function * name() {}
它是一个异步编程的解决方案。它的特点是可以让函数分段执行。Generator函数的内部由多个yield关键字组成,每一个yield表示一段执行。

function * Generator() {
	console.log("起床");
	yield "起床";
	console.log("洗脸");
	yield "洗脸";
	console.log("刷牙");
	yield "刷牙";
	console.log("吃饭");
	yield "吃饭";
	console.log("上课");
	yield "上课";
	console.log("下课");
	yield "下课";
	console.log("放学");
	yield "放学";
	console.log("dota");
	yield "dota";
	console.log("睡觉");
	return "睡觉";
}
var g = Generator();

Generator 得到的g是迭代器对象,调用next方法

console.log(g.next());

next

# 使用Generator函数解决异步编程

// 定义一个函数发送ajax
function sendAJAX1() {
	console.log(2);
	$.ajax({
		url: "/checkname",
		type: "get",
		dataType: "json",
		success: function(data) {
			console.log(3);
			g.next(data.msg)
		}
	})
}
// 定义第二个函数 发送ajax
function sendAJAX2(msg) {
	console.log(5);
	$.ajax({
		url: "/checkname1",
		type: "get",
		dataType: "json",
		data: {
			data: msg
		},
		success: function(data) {
			console.log(6);
			g.next(data.msg);
		}
	})
}
// 定义第三个AJAX
function sendAJAX3(msg) {
	console.log(8)
	$.ajax({
		url: "/checkname2",
		type: "get",
		dataType: "json",
		data: {
			data: msg
		},
		success: function(data) {
			console.log(9)
		}
	})
}
// 定义一个Generator函数
function * Generator() {
	console.log(1);
	// 发送第一个ajax
	var msg = yield sendAJAX1();
	console.log(4)
	// 发送第二个ajax
	var msg1 = yield sendAJAX2(msg);
	console.log(7);
	// 发送第三个ajax
	yield sendAJAX3(msg1);
}

// 生成迭代器对象
var g = Generator();
g.next();