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.js333

通过闭包

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.js012

应用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.js012

数组的解构

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

只定义一个变量 须要 把其它地位空进去

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

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

只能定义在最初的地位

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:15const [...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.js300 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.jsfoo

对象的解构

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

const obj = {name:'yangzhen' , age:24};const { name , age , sex } = obj;console.log(sex,name,age);////PS F:\拉钩前端\lagou> node .\task1\task1.jsundefined 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.js18 yangzhen 24

字符串的扩大办法

const { log } = consoleconst 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() //// truefoo(false) //// false

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

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

ES6语法

function foo(enable = true){    console.log(enable)}foo() //// truefoo(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.jsa 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.jsa[ 2, 3, 4 ]

···开展数组

const arr = [1,2,3,4]console.log(...arr)////PS E:\lagou前端\demo> node .\part1\test1.js1 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.jsmy 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.jsmy name is jack

应用箭头函数

箭头函数不会扭转this的指向

const name = 'jack' //// 这个name 并不是this.nameconst person = {    name: 'tom',    sayHi: () => {        console.log(`my name is ${this.name}`)    }}person.sayHi()////PS E:\lagou前端\demo> node .\part1\test1.jsmy 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{}undefinedmy 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.jsmy name is tommy 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.jsmy name is tommy 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.jsmy name is undefinedmy 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()] = 123console.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 } name100
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.jsyangdefault
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 = 30personProxy.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.ageconsole.log(person)////PS E:\lagou前端\demo> node .\part1\test1.jsdelete 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.jsset 0 100set length 1set 1 200set length 2list [ 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.jswatch logicundefined

增加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.jswatch logic18

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

不应用Reflect对对象的操作

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

应用Reflect 对对象的操作

const person = {    name: 'yang',    age: 18}//判断 对象是否有 属性console.log(Reflect.has(person,'name'))//获取对象所有的Keyconsole.log(Reflect.ownKeys(person))//删除对象的属性console.log(Reflect.deleteProperty(person,'age'))console.log(person)////PS E:\lagou前端\demo> node .\part1\test1.jstrue[ '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.jsmy 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.jsmy 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.jsmy 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.jsmy name is jackmy 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) //// 5console.log(s.has(100)) ////falseconsole.log(s.delete(3)) ////trueconsole.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.jsMap { { name: 'tom' } => 90 }9090 { 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.jsSymbol()symbolfalse 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.jsundefinedyang

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) ////truefor (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.jsfoobar

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.jsfoo 123bar 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.js1234

案例二:应用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.js01-13

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.jstruetruetruetrue

指数运算符

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

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.jsfoo value1bar balue2

应用entries 转化 Map对象

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