+-
ES6
首页 专栏 javascript 文章详情
0

ES6

杨杨杨杨杨杨杨振 发布于 2 月 5 日

let 与块级作用域

JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。
块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。

var elements = [{}, {}, {}]; for (var i=0; i < elements.length; i++) { elements[i].onclick = function() { console.log(i); } } elements[1].onclick(); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 3 3 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(); elements[1].onclick(); elements[2].onclick(); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 0 1 2

使用let

var elements = [{}, {}, {}]; for (let i = 0; i < elements.length; i++) { elements[i].onclick = function () { console.log(i); } } elements[0].onclick(); elements[1].onclick(); elements[2].onclick(); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 0 1 2

数组的解构

const arr = [100,200,300]; const [a,b,c] = arr; console.log(a,b,c); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 100 200 300

只定义一个变量 需要 把其它位置空出来

const arr = [100,200,300]; const [,,c] = arr; console.log(c); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 300

使用...定义剩下的变量

只能定义在最后的位置

const arr = [100,200,300]; const [a, ...rest] = arr; console.log(rest); //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 200, 300 ] const arr = [100,200,300]; const [...rest] = arr; console.log(rest); //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 100, 200, 300 ] const arr = [100,200,300]; const [...rest,c] = arr; console.log(rest); //// F:\拉钩前端\lagou\task1\task1.js:15 const [...rest,c] = arr; ^^^^^^^

解构的时候赋初始值

const arr = [100, 200, 300]; const [a, b, c = 123, d = 'defaultValue'] = arr; console.log(c, d); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 300 defaultValue

解构实例:解析路径

//传统写法 const path = '/foo/bar/baz'; const temp = path.split('/'); const rootdir = temp[1]; console.log(temp); console.log(rootdir); //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ '', 'foo', 'bar', 'baz' ] foo //解构写法 const path = '/foo/bar/baz'; const [,rootdir] = path.split('/'); console.log(rootdir); //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo

对象的解构

对象是通过key,数组是通过下标

const obj = {name:'yangzhen' , age:24}; const { name , age , sex } = obj; console.log(sex,name,age); //// PS F:\拉钩前端\lagou> node .\task1\task1.js undefined yangzhen 24

不使用别名

const obj = {name:'yangzhen' , age:24}; const name = 'jack'; const { name , age , sex } = obj; console.log(sex,name,age); //// const { name , age , sex } = obj; ^ SyntaxError: Identifier 'name' has already been declared

使用别名 赋初始值

const obj = {name:'yangzhen' , age:24}; const name = 'jack'; const { name:myname , age , sex = 18 } = obj; console.log(sex,myname,age); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 18 yangzhen 24

字符串的扩展方法

const { log } = console const message = 'Error: foo is noe defined.' log ( message.startsWith('Error'), message.endsWith('.'), message.includes('is') ) //// true true true

函数参数的默认值

原始方法(对于布尔类型)

//函数参数的默认值 function foo(enable) { //原始方法(对于布尔类型) enable = enable === undefined ? true : false console.log(enable) } foo() //// true foo(false) //// false

原始方法(对于其它类型)

//函数参数的默认值 function foo(enable) { //原始方法 enable = enable || 0 console.log(enable) } foo() //// 0 foo(5) //// 5

ES6语法

function foo(enable = true){ console.log(enable) } foo() //// true foo(false) //// false

剩余参数

方式一:使用arguments

function foo(){ console.log(arguments) } foo(1,2,3,4) //// PS E:\lagou前端\demo> node .\part1\test1.js [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 } function foo(){ const [a,b,c,d] = arguments console.log(a,b,c,d) } foo('a',2,3,4) //// PS E:\lagou前端\demo> node .\part1\test1.js a 2 3 4

方式二:使用...

...args放在最后 只能使用一次

function foo(first,...args){ console.log(first) console.log(args) } foo('a',2,3,4) //// PS E:\lagou前端\demo> node .\part1\test1.js a [ 2, 3, 4 ]

···展开数组

const arr = [1,2,3,4] console.log(...arr) //// PS E:\lagou前端\demo> node .\part1\test1.js 1 2 3 4

箭头函数与this

普通函数:this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个。

箭头函数没有prototype(原型),所以箭头函数本身没有this。箭头函数内部的this是词法作用域,由上下文确定,this指向在定义的时候继承自外层第一个普通函数的this。函数体内的this对象,就是定义时所在的对象,与使用时所在的对象无关。

const name = 'jack' const person = { name: 'tom', sayHi: function(){ console.log(`my name is ${this.name}`) } } person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom const name = 'jack' const person = { name: 'tom', sayHi: function(){ console.log(`my name is ${name}`) } } person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is jack

使用箭头函数

箭头函数不会改变this的指向

const name = 'jack' //// 这个name 并不是this.name const person = { name: 'tom', sayHi: () => { console.log(`my name is ${this.name}`) } } person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is undefined const name = 'jack' const person = { name: 'tom', sayHi: () => { console.log(`my name is ${name}`) } } console.log(this) console.log(this.name) person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js {} undefined my name is jack

传统方式_this的使用

const person = { name: 'tom', sayHi: function() { console.log(`my name is ${this.name}`) }, sayHiAsync:function(){ setTimeout(function () { console.log(`my name is ${this.name}`) }, 1000); } } person.sayHi() person.sayHiAsync() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom my name is undefined const person = { name: 'tom', sayHi: function() { console.log(`my name is ${this.name}`) }, sayHiAsync:function(){ _this = this; setTimeout(function () { console.log(`my name is ${_this.name}`) }, 1000); } } person.sayHi() person.sayHiAsync() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom my name is tom

setTimeout使用箭头函数 可以避免_this

箭头函数内部的this是词法作用域,由上下文确定

const person = { name: 'tom', sayHi: () => { console.log(`my name is ${this.name}`) }, sayHiAsync:function(){ setTimeout(()=> { console.log(`my name is ${this.name}`) }, 1000); } } person.sayHi() person.sayHiAsync() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is undefined my name is tom

对象字面量增强

const bar = '345' const obj = { foo: 123, //bar:bar bar, //method1:function(){ // console.log(this) //} method1(){ console.log(this) }, [Math.random()]:456 // 可以直接增加 } //在传统方法中,对象动态的增加属性 obj[Math.random()] = 123 console.log(obj) obj.method1() //// PS E:\lagou前端\demo> node .\part1\test1.js { foo: 123, bar: '345', method1: [Function: method1], '0.8616180359346404': 123 } { foo: 123, bar: '345', method1: [Function: method1], '0.8616180359346404': 123 }

对象扩展方法

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) //// PS E:\lagou前端\demo> node .\part1\test1.js { a: 123, c: 456, b: 789, d: 789 } true

案例

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

Proxy代理对象

const person = { name: 'yang', age: 18 } //参数1:代理对象 //参数2:代理的处理对象 const personProxy = new Proxy(person,{ //通过get方法去监视属性访问 //参数1:所代理的目标对象 //参数2:外部所访问的属性名 get(target,property){ console.log(target,property) return 100 }, //通过set方法去完成属性设置 set(){ } }) console.log(personProxy.name) //// PS E:\lagou前端\demo> node .\part1\test1.js { name: 'yang', age: 18 } name 100 const person = { name: 'yang', age: 18 } //参数1:代理对象 //参数2:代理的处理对象 const personProxy = new Proxy(person,{ //通过get方法去监视属性访问 //参数1:所代理的目标对象 //参数2:外部所访问的属性名 get(target,property){ return property in target ? target[property] : 'default' }, //通过set方法去完成属性设置 set(){ } }) console.log(personProxy.name) console.log(personProxy.xxxx) //// PS E:\lagou前端\demo> node .\part1\test1.js yang default const person = { name: 'yang', age: 18 } //参数1:代理对象 //参数2:代理的处理对象 const personProxy = new Proxy(person,{ //通过get方法去监视属性访问 //参数1:所代理的目标对象 //参数2:外部所访问的属性名 get(target,property){ return property in target ? target[property] : 'default' }, //通过set方法去完成属性设置 //1.所代理的目标对象 //2.要写入的属性名 //3.要写入的值 set(target,property,value){ if(property === 'age') { if (!Number.isInteger(value)) { throw new TypeError(`${value} is not an int`) } } target[property] = value } }) personProxy.age = 30 personProxy.sex = 'man' console.log(personProxy) //// PS E:\lagou前端\demo> node .\part1\test1.js { name: 'yang', age: 30, sex: 'man' }

proxy 和 Object.defineProperty()

Object.defineProperty()只能监听对象的读写

proxy还可以有更多的操作

const person = { name: 'yang', age: 18 } const personProxy = new Proxy(person,{ deleteProperty(target,property){ console.log('delete',property) delete target[property] } }) delete personProxy.age console.log(person) //// PS E:\lagou前端\demo> node .\part1\test1.js delete age { name: 'yang' }

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(200) console.log('list',list) //// PS E:\lagou前端\demo> node .\part1\test1.js set 0 100 set length 1 set 1 200 set length 2 list [ 100, 200 ]

Reflect 统一的对象操作API

静态类,封装了一系列对对象的底层操作

Reflet 成员方法就是Proxy处理对象的默认实现

去掉Reflect

const person = { name: 'yang', age: 18 } const personProxy = new Proxy(person,{ get(target,property) { console.log('watch logic') // return Reflect.get(target,property) } }) console.log(personProxy.age) //// PS E:\lagou前端\demo> node .\part1\test1.js watch logic undefined

添加Reflect

const person = { name: 'yang', age: 18 } const personProxy = new Proxy(person,{ get(target,property) { console.log('watch logic') return Reflect.get(target,property) } }) console.log(personProxy.age) //// PS E:\lagou前端\demo> node .\part1\test1.js watch logic 18

Reflect的作用:统一的提供一套用于操作对象的API

不使用Reflect对对象的操作

const person = { name: 'yang', age: 18 } //判断 对象是否有 属性 console.log('name' in person) //获取对象所有的Key console.log(Object.keys(person)) //删除对象的属性 console.log(delete person['age']) console.log(person) //// PS E:\lagou前端\demo> node .\part1\test1.js true [ 'name', 'age' ] true { name: 'yang' }

使用Reflect 对对象的操作

const person = { name: 'yang', age: 18 } //判断 对象是否有 属性 console.log(Reflect.has(person,'name')) //获取对象所有的Key console.log(Reflect.ownKeys(person)) //删除对象的属性 console.log(Reflect.deleteProperty(person,'age')) console.log(person) //// PS E:\lagou前端\demo> node .\part1\test1.js true [ 'name', 'age' ] true { name: 'yang' }

Promise 一种更优的异步编程解决方案

解决了传统异步编程中函数嵌套过深的问题

class 关键字

传统使用prototype

function Person (name) { this.name = name; } Person.prototype.say = function () { console.log(`my name is ${this.name}`) } const p = new Person('tom') p.say() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom

使用class

class Person { constructor(name) { this.name = name } say() { console.log(`my name is ${this.name}`) } } const p = new Person('tom') p.say() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom

静态方法

class Person { constructor(name) { this.name = name } say() { console.log(`my name is ${this.name}`) } static create(name) { return new Person(name) } } const p = Person.create('tom') p.say() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom

继承

class Person { constructor(name) { this.name = name } say() { console.log(`my name is ${this.name}`) } static create(name) { return new Person(name) } } class Student extends Person { constructor(name,number) { super(name) this.number = number } hello() { super.say() console.log(`my number is ${this.number}`) } } const s = new Student('jack',100) s.hello() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is jack my number is 100

Set 数据类型

const s = new Set() //add返回的是Set 所以可以链式调用 s.add(1).add(2).add(3).add(4).add(4).add(5) //set 元素 没有重复 所以 自动去重 console.log(s) //// Set { 1, 2, 3, 4, 5 } //Set的遍历 s.forEach(i => { console.log(i) //// 1 2 3 4 5 }) //Set的常用方法 console.log(s.size) //// 5 console.log(s.has(100)) ////false console.log(s.delete(3)) ////true console.log(s) //// Set { 1, 2, 4, 5 }

Set 对 数组的去重处理

Array.from() 可以将Set 转化为Array

const arr = [1,2,3,1,2,1,4,1] //const result = Array.from(new Set(arr)) const result = [...new Set(arr)] console.log(result) PS E:\lagou前端\demo> node .\part1\test1.js [ 1, 2, 3, 4 ]

Map 数据结构

普通键值对

对象的key只能是String类型,不是String类型会被自动转化为String

const obj = {} obj[true] = 'value' obj[123] = 'value' obj[{ a: 1 }] = 'value' console.log(Object.keys(obj)) //// PS E:\lagou前端\demo> node .\part1\test1.js [ '123', 'true', '[object Object]' ]

Map 类型

const m = new Map() const tom = { name:'tom'} m.set(tom,90) console.log(m) console.log(m.get(tom)) //Map的遍历 m.forEach((value,key)=>{ console.log(value,key) }) //// PS E:\lagou前端\demo> node .\part1\test1.js Map { { name: 'tom' } => 90 } 90 90 { name: 'tom' }

Symbol 新的数据类型

最主要的作用就是为对象添加独一无二的属性名

const s = Symbol() console.log(s) console.log(typeof s) console.log(Symbol() === Symbol()) //添加描述文本 //注意:即使添加同一描述文本 Symbol 也是不一样的 console.log(Symbol('foo')) //// PS F:\拉钩前端\lagou> node .\task1\task1.js Symbol() symbol false Symbol(foo)

设置对象私有成员

const name = Symbol(); const person = { [name]:'yang', say(){ console.log(this[name]) } } console.log(person[Symbol()]) person.say() //// PS F:\拉钩前端\lagou> node .\task1\task1.js undefined yang

for of 循环

是一种数据统一的遍历方式

const arr = [100,200,300,400,500] for (item of arr) { console.log(item) ////100 200 300 400 if(item > 300){ break } } //不能跳出循环 arr.forEach(item =>{ console.log(item) ////100 200 300 400 }) //some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。 //some() 方法会依次执行数组的每个元素: //如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。 //如果没有满足条件的元素,则返回false。 //every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。 //every() 方法使用指定函数检测数组中的所有元素: //如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。 //如果所有元素都满足条件,则返回 true。 const result = arr.some((item)=>{ return item > 300 }) console.log(result) ////true for (i in arr) { console.log(arr[i]) ////100 200 300 400 500 }

Set的 for of 遍历

const s = new Set(['foo','bar']) for (const item of s) { console.log(item) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo bar

Map的 for of 遍历

const m = new Map() m.set('foo','123') m.set('bar','456') for(const item of m ) { console.log(item) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 'foo', '123' ] [ 'bar', '456' ]

使用数组的解构得到K V

const m = new Map() m.set('foo','123') m.set('bar','456') for(const [key,value] of m ) { console.log(key,value) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo 123 bar 456

实现可迭代接口 Iterable

对象是不可以直接迭代的

在对象中挂一个iterator方法,在这个方法中返回一个迭代器对象

接口约定:内部必须要有一个用于迭代的next方法

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) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js 循环 foo 循环 bar 循环 baz

迭代器模式

实现迭代器的目的

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

//============== 我的代码 ============== const todos= { life:['吃饭','睡觉','打豆豆'], learn:['语言','数学','英语'], } //============== 你的代码 ============== for(const item of todos.life){ console.log(item) } for(const item of todos.learn){ console.log(item) }

如果我需要在我的todos数据列表里添加数据时,你的代码就需要作出相应的更改,对于调用来说,代码耦合度太高

//============== 我的代码 ============== const todos= { life:['吃饭','睡觉','打豆豆'], learn:['语言','数学','英语'], work:['唱歌'] } //============== 你的代码 ============== 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) }

如何解决:

如果我的代码能够对外统一提供一个遍历的接口

对于调用者而已,就不用去关心它内部的数据结构,也不用担心结构的改变

我们可以自己写一个方法来实现:

//============== 我的代码 ============== const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语言', '数学', '英语'], work: ['唱歌'], each: function (callback) { const all = [].concat(this.life, this.learn, this.work) for (item of all) { callback(item) } } } //============== 你的代码 ============== todos.each(function (item) { console.log(item); })

用迭代器的方法实现

//============== 我的代码 ============== const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语言', '数学', '英语'], work: ['唱歌'], [Symbol.iterator]:function() { //使用对象展开符 等同于 [].concat const all = [...this.life,...this.learn,...this.work] let index = 0 return{ next:function(){ return{ value:all[index], done: index++ >= all.length //true 就迭代完成了 } } } } } //============== 你的代码 ============== for(item of todos) { console.log(item) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js 吃饭 睡觉 打豆豆 语言 数学 英语 唱歌

迭代器的核心 就是对外提供统一遍历接口,让外部不用去担心内部的数据结构是怎么样的

这里展示的each方法只适用于这里展示的数组结构

而ES2015种的迭代器 是从语言层面去实现的迭代器模式,所以说它适用于任何数据结构,只要实现了iterable 接口,那我们就可以实现使用for…of 遍历对象

生成器函数

解决异步编程回调嵌套过深所导致的问题

function * foo(){ console.log('test') return 100 } const result = foo() console.log(result) console.log(result.next()) //// Object [Generator] {} test { value: 100, done: true }

惰性执行

yield

function * foo(){ console.log('111') yield 100 console.log('222') yield 200 console.log('333') yield 300 } const generator = foo() console.log(generator) console.log(generator.next()) console.log(generator.next()) console.log(generator.next()) console.log(generator.next()) //// Object [Generator] {} 111 { value: 100, done: false } 222 { value: 200, done: false } 333 { value: 300, done: false } { value: undefined, done: true }

Generator 应用

案例一:发号器

function * createIdMaker(){ let id = 1 while(true) { yield id++ } } const idMaker = createIdMaker() console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value) //// PS E:\lagou前端\demo> node .\part1\test1.js 1 2 3 4

案例二:使用Generator 函数实现Iterator方法

const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语言', '数学', '英语'], work: ['唱歌'], [Symbol.iterator]: function* () { const all = [...this.life, ...this.learn, ...this.work] for (item of all) { yield item } } } for (const item of todos) { console.log(item) } //// PS E:\lagou前端\demo> node .\part1\test1.js 吃饭 睡觉 打豆豆 语言 数学 英语 唱歌

ES2016

Array.prototype.includes

我们先看看indexOf方法

const arr = ['foo',1,NaN,false] console.log(arr.indexOf('foo')) console.log(arr.indexOf(1)) console.log(arr.indexOf(NaN)) console.log(arr.indexOf(false)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js 0 1 -1 3

indexOf 无法检查出NaN

NaN是一个特殊的数字值(typeof NaN的结果为number),是not a number的缩写,表示不是一个合法的数字。
注意点:NaN是唯一一个和自身不相等的值:NaN === NaN // false

includes 可以检查出NaN 返回布尔值

const arr = ['foo',1,NaN,false] console.log(arr.includes('foo')) console.log(arr.includes(1)) console.log(arr.includes(NaN)) console.log(arr.includes(false)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js true true true true

指数运算符

console.log(Math.pow(2,10)) console.log(2 ** 10) //// PS F:\拉钩前端\lagou> node .\task1\task1.js 1024 1024

ES2017

Object.values

const obj = { foo: 'value1', bar: 'balue2' } console.log(Object.values(obj)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 'value1', 'balue2' ]

Object.entries

数组形式返回键值对

const obj = { foo: 'value1', bar: 'balue2' } console.log(Object.entries(obj)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ [ 'foo', 'value1' ], [ 'bar', 'balue2' ] ]

因此可以使用for of

const obj = { foo: 'value1', bar: 'balue2' } for (const [key, value] of Object.entries(obj)) { console.log(key, value) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo value1 bar balue2

使用entries 转化 Map对象

const obj = { foo: 'value1', bar: 'balue2' } console.log(new Map(Object.entries(obj))) //// PS F:\拉钩前端\lagou> node .\task1\task1.js Map { 'foo' => 'value1', 'bar' => 'balue2' }
javascript
阅读 37 发布于 2 月 5 日
收藏
分享
本作品系原创, 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
avatar
杨杨杨杨杨杨杨振
1 声望
0 粉丝
关注作者
0 条评论
得票 时间
提交评论
avatar
杨杨杨杨杨杨杨振
1 声望
0 粉丝
关注作者
宣传栏
目录

let 与块级作用域

JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。
块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。

var elements = [{}, {}, {}]; for (var i=0; i < elements.length; i++) { elements[i].onclick = function() { console.log(i); } } elements[1].onclick(); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 3 3 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(); elements[1].onclick(); elements[2].onclick(); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 0 1 2

使用let

var elements = [{}, {}, {}]; for (let i = 0; i < elements.length; i++) { elements[i].onclick = function () { console.log(i); } } elements[0].onclick(); elements[1].onclick(); elements[2].onclick(); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 0 1 2

数组的解构

const arr = [100,200,300]; const [a,b,c] = arr; console.log(a,b,c); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 100 200 300

只定义一个变量 需要 把其它位置空出来

const arr = [100,200,300]; const [,,c] = arr; console.log(c); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 300

使用...定义剩下的变量

只能定义在最后的位置

const arr = [100,200,300]; const [a, ...rest] = arr; console.log(rest); //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 200, 300 ] const arr = [100,200,300]; const [...rest] = arr; console.log(rest); //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 100, 200, 300 ] const arr = [100,200,300]; const [...rest,c] = arr; console.log(rest); //// F:\拉钩前端\lagou\task1\task1.js:15 const [...rest,c] = arr; ^^^^^^^

解构的时候赋初始值

const arr = [100, 200, 300]; const [a, b, c = 123, d = 'defaultValue'] = arr; console.log(c, d); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 300 defaultValue

解构实例:解析路径

//传统写法 const path = '/foo/bar/baz'; const temp = path.split('/'); const rootdir = temp[1]; console.log(temp); console.log(rootdir); //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ '', 'foo', 'bar', 'baz' ] foo //解构写法 const path = '/foo/bar/baz'; const [,rootdir] = path.split('/'); console.log(rootdir); //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo

对象的解构

对象是通过key,数组是通过下标

const obj = {name:'yangzhen' , age:24}; const { name , age , sex } = obj; console.log(sex,name,age); //// PS F:\拉钩前端\lagou> node .\task1\task1.js undefined yangzhen 24

不使用别名

const obj = {name:'yangzhen' , age:24}; const name = 'jack'; const { name , age , sex } = obj; console.log(sex,name,age); //// const { name , age , sex } = obj; ^ SyntaxError: Identifier 'name' has already been declared

使用别名 赋初始值

const obj = {name:'yangzhen' , age:24}; const name = 'jack'; const { name:myname , age , sex = 18 } = obj; console.log(sex,myname,age); //// PS F:\拉钩前端\lagou> node .\task1\task1.js 18 yangzhen 24

字符串的扩展方法

const { log } = console const message = 'Error: foo is noe defined.' log ( message.startsWith('Error'), message.endsWith('.'), message.includes('is') ) //// true true true

函数参数的默认值

原始方法(对于布尔类型)

//函数参数的默认值 function foo(enable) { //原始方法(对于布尔类型) enable = enable === undefined ? true : false console.log(enable) } foo() //// true foo(false) //// false

原始方法(对于其它类型)

//函数参数的默认值 function foo(enable) { //原始方法 enable = enable || 0 console.log(enable) } foo() //// 0 foo(5) //// 5

ES6语法

function foo(enable = true){ console.log(enable) } foo() //// true foo(false) //// false

剩余参数

方式一:使用arguments

function foo(){ console.log(arguments) } foo(1,2,3,4) //// PS E:\lagou前端\demo> node .\part1\test1.js [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 } function foo(){ const [a,b,c,d] = arguments console.log(a,b,c,d) } foo('a',2,3,4) //// PS E:\lagou前端\demo> node .\part1\test1.js a 2 3 4

方式二:使用...

...args放在最后 只能使用一次

function foo(first,...args){ console.log(first) console.log(args) } foo('a',2,3,4) //// PS E:\lagou前端\demo> node .\part1\test1.js a [ 2, 3, 4 ]

···展开数组

const arr = [1,2,3,4] console.log(...arr) //// PS E:\lagou前端\demo> node .\part1\test1.js 1 2 3 4

箭头函数与this

普通函数:this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个。

箭头函数没有prototype(原型),所以箭头函数本身没有this。箭头函数内部的this是词法作用域,由上下文确定,this指向在定义的时候继承自外层第一个普通函数的this。函数体内的this对象,就是定义时所在的对象,与使用时所在的对象无关。

const name = 'jack' const person = { name: 'tom', sayHi: function(){ console.log(`my name is ${this.name}`) } } person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom const name = 'jack' const person = { name: 'tom', sayHi: function(){ console.log(`my name is ${name}`) } } person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is jack

使用箭头函数

箭头函数不会改变this的指向

const name = 'jack' //// 这个name 并不是this.name const person = { name: 'tom', sayHi: () => { console.log(`my name is ${this.name}`) } } person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is undefined const name = 'jack' const person = { name: 'tom', sayHi: () => { console.log(`my name is ${name}`) } } console.log(this) console.log(this.name) person.sayHi() //// PS E:\lagou前端\demo> node .\part1\test1.js {} undefined my name is jack

传统方式_this的使用

const person = { name: 'tom', sayHi: function() { console.log(`my name is ${this.name}`) }, sayHiAsync:function(){ setTimeout(function () { console.log(`my name is ${this.name}`) }, 1000); } } person.sayHi() person.sayHiAsync() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom my name is undefined const person = { name: 'tom', sayHi: function() { console.log(`my name is ${this.name}`) }, sayHiAsync:function(){ _this = this; setTimeout(function () { console.log(`my name is ${_this.name}`) }, 1000); } } person.sayHi() person.sayHiAsync() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom my name is tom

setTimeout使用箭头函数 可以避免_this

箭头函数内部的this是词法作用域,由上下文确定

const person = { name: 'tom', sayHi: () => { console.log(`my name is ${this.name}`) }, sayHiAsync:function(){ setTimeout(()=> { console.log(`my name is ${this.name}`) }, 1000); } } person.sayHi() person.sayHiAsync() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is undefined my name is tom

对象字面量增强

const bar = '345' const obj = { foo: 123, //bar:bar bar, //method1:function(){ // console.log(this) //} method1(){ console.log(this) }, [Math.random()]:456 // 可以直接增加 } //在传统方法中,对象动态的增加属性 obj[Math.random()] = 123 console.log(obj) obj.method1() //// PS E:\lagou前端\demo> node .\part1\test1.js { foo: 123, bar: '345', method1: [Function: method1], '0.8616180359346404': 123 } { foo: 123, bar: '345', method1: [Function: method1], '0.8616180359346404': 123 }

对象扩展方法

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) //// PS E:\lagou前端\demo> node .\part1\test1.js { a: 123, c: 456, b: 789, d: 789 } true

案例

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

Proxy代理对象

const person = { name: 'yang', age: 18 } //参数1:代理对象 //参数2:代理的处理对象 const personProxy = new Proxy(person,{ //通过get方法去监视属性访问 //参数1:所代理的目标对象 //参数2:外部所访问的属性名 get(target,property){ console.log(target,property) return 100 }, //通过set方法去完成属性设置 set(){ } }) console.log(personProxy.name) //// PS E:\lagou前端\demo> node .\part1\test1.js { name: 'yang', age: 18 } name 100 const person = { name: 'yang', age: 18 } //参数1:代理对象 //参数2:代理的处理对象 const personProxy = new Proxy(person,{ //通过get方法去监视属性访问 //参数1:所代理的目标对象 //参数2:外部所访问的属性名 get(target,property){ return property in target ? target[property] : 'default' }, //通过set方法去完成属性设置 set(){ } }) console.log(personProxy.name) console.log(personProxy.xxxx) //// PS E:\lagou前端\demo> node .\part1\test1.js yang default const person = { name: 'yang', age: 18 } //参数1:代理对象 //参数2:代理的处理对象 const personProxy = new Proxy(person,{ //通过get方法去监视属性访问 //参数1:所代理的目标对象 //参数2:外部所访问的属性名 get(target,property){ return property in target ? target[property] : 'default' }, //通过set方法去完成属性设置 //1.所代理的目标对象 //2.要写入的属性名 //3.要写入的值 set(target,property,value){ if(property === 'age') { if (!Number.isInteger(value)) { throw new TypeError(`${value} is not an int`) } } target[property] = value } }) personProxy.age = 30 personProxy.sex = 'man' console.log(personProxy) //// PS E:\lagou前端\demo> node .\part1\test1.js { name: 'yang', age: 30, sex: 'man' }

proxy 和 Object.defineProperty()

Object.defineProperty()只能监听对象的读写

proxy还可以有更多的操作

const person = { name: 'yang', age: 18 } const personProxy = new Proxy(person,{ deleteProperty(target,property){ console.log('delete',property) delete target[property] } }) delete personProxy.age console.log(person) //// PS E:\lagou前端\demo> node .\part1\test1.js delete age { name: 'yang' }

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(200) console.log('list',list) //// PS E:\lagou前端\demo> node .\part1\test1.js set 0 100 set length 1 set 1 200 set length 2 list [ 100, 200 ]

Reflect 统一的对象操作API

静态类,封装了一系列对对象的底层操作

Reflet 成员方法就是Proxy处理对象的默认实现

去掉Reflect

const person = { name: 'yang', age: 18 } const personProxy = new Proxy(person,{ get(target,property) { console.log('watch logic') // return Reflect.get(target,property) } }) console.log(personProxy.age) //// PS E:\lagou前端\demo> node .\part1\test1.js watch logic undefined

添加Reflect

const person = { name: 'yang', age: 18 } const personProxy = new Proxy(person,{ get(target,property) { console.log('watch logic') return Reflect.get(target,property) } }) console.log(personProxy.age) //// PS E:\lagou前端\demo> node .\part1\test1.js watch logic 18

Reflect的作用:统一的提供一套用于操作对象的API

不使用Reflect对对象的操作

const person = { name: 'yang', age: 18 } //判断 对象是否有 属性 console.log('name' in person) //获取对象所有的Key console.log(Object.keys(person)) //删除对象的属性 console.log(delete person['age']) console.log(person) //// PS E:\lagou前端\demo> node .\part1\test1.js true [ 'name', 'age' ] true { name: 'yang' }

使用Reflect 对对象的操作

const person = { name: 'yang', age: 18 } //判断 对象是否有 属性 console.log(Reflect.has(person,'name')) //获取对象所有的Key console.log(Reflect.ownKeys(person)) //删除对象的属性 console.log(Reflect.deleteProperty(person,'age')) console.log(person) //// PS E:\lagou前端\demo> node .\part1\test1.js true [ 'name', 'age' ] true { name: 'yang' }

Promise 一种更优的异步编程解决方案

解决了传统异步编程中函数嵌套过深的问题

class 关键字

传统使用prototype

function Person (name) { this.name = name; } Person.prototype.say = function () { console.log(`my name is ${this.name}`) } const p = new Person('tom') p.say() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom

使用class

class Person { constructor(name) { this.name = name } say() { console.log(`my name is ${this.name}`) } } const p = new Person('tom') p.say() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom

静态方法

class Person { constructor(name) { this.name = name } say() { console.log(`my name is ${this.name}`) } static create(name) { return new Person(name) } } const p = Person.create('tom') p.say() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is tom

继承

class Person { constructor(name) { this.name = name } say() { console.log(`my name is ${this.name}`) } static create(name) { return new Person(name) } } class Student extends Person { constructor(name,number) { super(name) this.number = number } hello() { super.say() console.log(`my number is ${this.number}`) } } const s = new Student('jack',100) s.hello() //// PS E:\lagou前端\demo> node .\part1\test1.js my name is jack my number is 100

Set 数据类型

const s = new Set() //add返回的是Set 所以可以链式调用 s.add(1).add(2).add(3).add(4).add(4).add(5) //set 元素 没有重复 所以 自动去重 console.log(s) //// Set { 1, 2, 3, 4, 5 } //Set的遍历 s.forEach(i => { console.log(i) //// 1 2 3 4 5 }) //Set的常用方法 console.log(s.size) //// 5 console.log(s.has(100)) ////false console.log(s.delete(3)) ////true console.log(s) //// Set { 1, 2, 4, 5 }

Set 对 数组的去重处理

Array.from() 可以将Set 转化为Array

const arr = [1,2,3,1,2,1,4,1] //const result = Array.from(new Set(arr)) const result = [...new Set(arr)] console.log(result) PS E:\lagou前端\demo> node .\part1\test1.js [ 1, 2, 3, 4 ]

Map 数据结构

普通键值对

对象的key只能是String类型,不是String类型会被自动转化为String

const obj = {} obj[true] = 'value' obj[123] = 'value' obj[{ a: 1 }] = 'value' console.log(Object.keys(obj)) //// PS E:\lagou前端\demo> node .\part1\test1.js [ '123', 'true', '[object Object]' ]

Map 类型

const m = new Map() const tom = { name:'tom'} m.set(tom,90) console.log(m) console.log(m.get(tom)) //Map的遍历 m.forEach((value,key)=>{ console.log(value,key) }) //// PS E:\lagou前端\demo> node .\part1\test1.js Map { { name: 'tom' } => 90 } 90 90 { name: 'tom' }

Symbol 新的数据类型

最主要的作用就是为对象添加独一无二的属性名

const s = Symbol() console.log(s) console.log(typeof s) console.log(Symbol() === Symbol()) //添加描述文本 //注意:即使添加同一描述文本 Symbol 也是不一样的 console.log(Symbol('foo')) //// PS F:\拉钩前端\lagou> node .\task1\task1.js Symbol() symbol false Symbol(foo)

设置对象私有成员

const name = Symbol(); const person = { [name]:'yang', say(){ console.log(this[name]) } } console.log(person[Symbol()]) person.say() //// PS F:\拉钩前端\lagou> node .\task1\task1.js undefined yang

for of 循环

是一种数据统一的遍历方式

const arr = [100,200,300,400,500] for (item of arr) { console.log(item) ////100 200 300 400 if(item > 300){ break } } //不能跳出循环 arr.forEach(item =>{ console.log(item) ////100 200 300 400 }) //some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。 //some() 方法会依次执行数组的每个元素: //如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。 //如果没有满足条件的元素,则返回false。 //every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。 //every() 方法使用指定函数检测数组中的所有元素: //如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。 //如果所有元素都满足条件,则返回 true。 const result = arr.some((item)=>{ return item > 300 }) console.log(result) ////true for (i in arr) { console.log(arr[i]) ////100 200 300 400 500 }

Set的 for of 遍历

const s = new Set(['foo','bar']) for (const item of s) { console.log(item) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo bar

Map的 for of 遍历

const m = new Map() m.set('foo','123') m.set('bar','456') for(const item of m ) { console.log(item) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 'foo', '123' ] [ 'bar', '456' ]

使用数组的解构得到K V

const m = new Map() m.set('foo','123') m.set('bar','456') for(const [key,value] of m ) { console.log(key,value) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo 123 bar 456

实现可迭代接口 Iterable

对象是不可以直接迭代的

在对象中挂一个iterator方法,在这个方法中返回一个迭代器对象

接口约定:内部必须要有一个用于迭代的next方法

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) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js 循环 foo 循环 bar 循环 baz

迭代器模式

实现迭代器的目的

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

//============== 我的代码 ============== const todos= { life:['吃饭','睡觉','打豆豆'], learn:['语言','数学','英语'], } //============== 你的代码 ============== for(const item of todos.life){ console.log(item) } for(const item of todos.learn){ console.log(item) }

如果我需要在我的todos数据列表里添加数据时,你的代码就需要作出相应的更改,对于调用来说,代码耦合度太高

//============== 我的代码 ============== const todos= { life:['吃饭','睡觉','打豆豆'], learn:['语言','数学','英语'], work:['唱歌'] } //============== 你的代码 ============== 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) }

如何解决:

如果我的代码能够对外统一提供一个遍历的接口

对于调用者而已,就不用去关心它内部的数据结构,也不用担心结构的改变

我们可以自己写一个方法来实现:

//============== 我的代码 ============== const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语言', '数学', '英语'], work: ['唱歌'], each: function (callback) { const all = [].concat(this.life, this.learn, this.work) for (item of all) { callback(item) } } } //============== 你的代码 ============== todos.each(function (item) { console.log(item); })

用迭代器的方法实现

//============== 我的代码 ============== const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语言', '数学', '英语'], work: ['唱歌'], [Symbol.iterator]:function() { //使用对象展开符 等同于 [].concat const all = [...this.life,...this.learn,...this.work] let index = 0 return{ next:function(){ return{ value:all[index], done: index++ >= all.length //true 就迭代完成了 } } } } } //============== 你的代码 ============== for(item of todos) { console.log(item) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js 吃饭 睡觉 打豆豆 语言 数学 英语 唱歌

迭代器的核心 就是对外提供统一遍历接口,让外部不用去担心内部的数据结构是怎么样的

这里展示的each方法只适用于这里展示的数组结构

而ES2015种的迭代器 是从语言层面去实现的迭代器模式,所以说它适用于任何数据结构,只要实现了iterable 接口,那我们就可以实现使用for…of 遍历对象

生成器函数

解决异步编程回调嵌套过深所导致的问题

function * foo(){ console.log('test') return 100 } const result = foo() console.log(result) console.log(result.next()) //// Object [Generator] {} test { value: 100, done: true }

惰性执行

yield

function * foo(){ console.log('111') yield 100 console.log('222') yield 200 console.log('333') yield 300 } const generator = foo() console.log(generator) console.log(generator.next()) console.log(generator.next()) console.log(generator.next()) console.log(generator.next()) //// Object [Generator] {} 111 { value: 100, done: false } 222 { value: 200, done: false } 333 { value: 300, done: false } { value: undefined, done: true }

Generator 应用

案例一:发号器

function * createIdMaker(){ let id = 1 while(true) { yield id++ } } const idMaker = createIdMaker() console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value) //// PS E:\lagou前端\demo> node .\part1\test1.js 1 2 3 4

案例二:使用Generator 函数实现Iterator方法

const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语言', '数学', '英语'], work: ['唱歌'], [Symbol.iterator]: function* () { const all = [...this.life, ...this.learn, ...this.work] for (item of all) { yield item } } } for (const item of todos) { console.log(item) } //// PS E:\lagou前端\demo> node .\part1\test1.js 吃饭 睡觉 打豆豆 语言 数学 英语 唱歌

ES2016

Array.prototype.includes

我们先看看indexOf方法

const arr = ['foo',1,NaN,false] console.log(arr.indexOf('foo')) console.log(arr.indexOf(1)) console.log(arr.indexOf(NaN)) console.log(arr.indexOf(false)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js 0 1 -1 3

indexOf 无法检查出NaN

NaN是一个特殊的数字值(typeof NaN的结果为number),是not a number的缩写,表示不是一个合法的数字。
注意点:NaN是唯一一个和自身不相等的值:NaN === NaN // false

includes 可以检查出NaN 返回布尔值

const arr = ['foo',1,NaN,false] console.log(arr.includes('foo')) console.log(arr.includes(1)) console.log(arr.includes(NaN)) console.log(arr.includes(false)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js true true true true

指数运算符

console.log(Math.pow(2,10)) console.log(2 ** 10) //// PS F:\拉钩前端\lagou> node .\task1\task1.js 1024 1024

ES2017

Object.values

const obj = { foo: 'value1', bar: 'balue2' } console.log(Object.values(obj)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ 'value1', 'balue2' ]

Object.entries

数组形式返回键值对

const obj = { foo: 'value1', bar: 'balue2' } console.log(Object.entries(obj)) //// PS F:\拉钩前端\lagou> node .\task1\task1.js [ [ 'foo', 'value1' ], [ 'bar', 'balue2' ] ]

因此可以使用for of

const obj = { foo: 'value1', bar: 'balue2' } for (const [key, value] of Object.entries(obj)) { console.log(key, value) } //// PS F:\拉钩前端\lagou> node .\task1\task1.js foo value1 bar balue2

使用entries 转化 Map对象

const obj = { foo: 'value1', bar: 'balue2' } console.log(new Map(Object.entries(obj))) //// PS F:\拉钩前端\lagou> node .\task1\task1.js Map { 'foo' => 'value1', 'bar' => 'balue2' }