Loading

看完我的笔记不懂也会懂----ECMAscript 567

ECMAscript 567

严格模式

如何开启严格模式?

function strictMode(){
    'use strict'
    //something
}

开启严格模式的特性:

  1. 必须用var关键字声明变量
    'use strict'
    name = 'Fitz'
    console.log(name)   //报错 nams is not defined
    
  2. 自定义函数中的this不能指向window
    'use strict'
    function Test(){
        this.name = 'Fitz'
    }
    
    Test(); //此时直接执行函数相当于  window.name = 'Fitz'
    // 但是在严格模式下自定义函数this不能指向window  所以  报错
    
  3. eval()中的代码有独立作用域
    'use strict'
    var i=666
    eval('var i=3; console.log(i)')   //i=3
    console.log(i)    //i=666
    
  4. 对象中的属性名不能出现相同
    'use strict'
    obj = {
        name: 'dd',
        name: 'cc'
    }   //报错
    
  5. 全局对象是undefined
    非严格模式中 ==>  window
    严格模式    ==>  undefined
    

字符串扩展

  1. str.includes(str): 判断是否包含指定的字符串

    let str = 'this is a string'
    console.log(str.includes('is'))        //true
    console.log(str.includes(' '))         //true        
    console.log(str.includes('das'))       //false      
    console.log(str.includes('a s'))       //true
    
  2. str.startsWith(str): 判断是否以指定字符串开头

    let str = 'this is a string'
    console.log(str.startsWith('t'))       //true        
    console.log(str.startsWith('a'))       //false   
    
  3. str.endsWith(str): 判断是否以指定字符串结尾

    let str = 'this is a string'
    console.log(str.endsWith('g'))         //true 
    console.log(str.endsWith('h'))         //false
    
  4. str.repeat(count): 重复指定次数

    let str = 'this is a string'
    console.log(str.repeat(2))             //this is a stringthis is a string 
    

数值的扩展

  1. Number.isFinite(i): 判断是否是有限大的数

    let num = 666
    let num2 = 666.0
    let num3 = 3.1415926535
    
    console.log(Number.isFinite(num))   //true
    console.log(Number.isFinite(Infinity))//false
    
  2. Number.isNaN(i): 判断是否是判断是否是NaN

    let num = 666
    let num2 = 666.0
    let num3 = 3.1415926535
    
    console.log(Number.isNaN(num))      //false
    console.log(Number.isNaN(NaN))      //true
    
  3. Number.isInteger(i): 判断是否是整数

    let num = 666
    let num2 = 666.0
    let num3 = 3.1415926535
    
    console.log(Number.isInteger(num))  //true
    console.log(Number.isInteger(num2)) //true
    
  4. Number.parseInt(i): 将字符串转换为对应数值

    let num = 666
    let num2 = 666.0
    let num3 = 3.1415926535
    console.log(Number.parseInt(num2))  //666
    console.log(Number.parseInt(num3))  //3
    
  5. Number.trunc(i): 直接去除小数部分

    let num = 666
    let num2 = 666.0
    let num3 = 3.1415926535
    console.log(Math.trunc(num2))       //666
    console.log(Math.trunc(num3))       //3
    

Object对象方法扩展

  1. Object.create(prototype,[descriptors])

    1. 作用:
      1. 以指定对象为原型创建新的对象
      2. 为新对象指定新属性,并对属性执行描述

    2. 常用的属性

      1. value: 自定义属性值
      2. writable:标识描述属性是否能修改,默认为false
      3. configurable: 标识当前属性是否可以被删除,默认为false
      4. enumerable: 标识当前属性能够使用for in 枚举, 默认为false
    var obj = {
                username: 'dd',
                age: 30
            }
    
    var obj1 = Object.create(obj,{
            att1: {
                value: '我是att1的属性值',
                writable: true,
                enumerable: true,
                configurable: true
            },
            password: {
                value: '666',
                writable: true,
                enumerable: true,
                configurable: true
            }
        })
    
  2. Object.defineProperties(object,[descriptors])

    //扩展原型属性
    var obj3 = {
        name: 'Lx',
        age: 20
    }
    Object.defineProperties(obj3,{
        //info属于扩展属性
        //this --->  obj3
        info: {
            //当扩展属性改变时,自动调用该方法(obj.info = xx)
            set: function(data){
                console.log('data -->' + data)
                var splitData = data.split(' ')
                this.name = splitData[0]
                this.age = splitData[1]
            },
    
            //获取扩展属性的值,自动调用该方法  (obj.info)
            get: function(){
                return this.name + '->' + this.age
            }
        }
        //扩展属性的set与get本质上调用的是
        //set propertyName()
        //get propertyName()
    })
    
    console.log(obj3.info); //Lx->20
    obj3.info = 'ddd 99'    //data -->ddd 99
    console.log('change success --> ' + obj3.info)  //change success -->ddd->99
    
    var obj4 = {
        name: 'sun',
        age: 999,
        get personInfo(){
            console.log('get()')
            return 'name is -> ' + this.name + 'age is -> ' + this.age
        },
        set personInfo(info){
            console.log('set()')
            var spliter = info.split(' ')
            this.name = spliter[0]
            this.age = spliter[1]
        }
    }
    
    console.log(obj4.personInfo);   //get()    name is -> sunage is -> 999
    obj4.personInfo = 'Liu 13'  //set()
    console.log(obj4.personInfo);   //get()    name is -> Liu age is -> 13
    
  3. Object.is(v1,v2) 判断两个数据是否完全相等

    1. 原理是先转换成字符串再判断两者是否相等
    console.log(0 === -0)           //true
    console.log(NaN === NaN)        //false
    console.log(Object.is(0,-0))    //false
    console.log(Object.is(NaN,NaN)) //true
    
  4. Object.assign(target,source1,source2..) 将原对象的属性复制到目标对象上

    let myObj = {}
    let myObj2 = {name: '我会被复制到另一个对象中'}
    let myObj3 = {age: 999}
    let myObj4 = {gender: 'male'}
    Object.assign(myObj,myObj2,myObj3,myObj4)
    console.log(myObj)  //{name: "我会被复制到另一个对象中", age: 999,gender: "male"}
    
  5. 直接操作__proto__属性

    //直接操作 __proto__
    let myObj5 = {}
    let myObj6 = {dream: 'rich'}
    myObj5.__proto__ = myObj6
    console.log(myObj5)
    console.log(myObj5.dream)   //rich
    

数组的扩展

  1. map(function(item,index)}{})
    遍历数组返回一个新数组,数组中的值是在加工后的

    var arr = [1,3,4,5,67,12,4]
    var newArr = arr.map(function(item,index){
        return item ** 2
    })
    console.log(newArr)  //[1, 9, 16, 25, 4489, 144, 16]
    console.log(arr)    //不会改变元素组
    
  2. filter(function(item,index)}{})
    遍历数组返回一个新数组,数组中的值是在条件为true的

    var arr = [1,3,4,5,67,12,4]
    var newArr2 = arr.filter(function(item,index){
            return index > 2    //过滤条件
        })
        console.log(newArr2)  //[5,67,12,4] 值为: 过滤条件 === true
        console.log(arr)    //不会改变元素组
    

数组方法的扩展

  1. Array.from(v) 将伪数组对象或可遍历对象转换为真数组

    //定义一个伪数组
    let fakeArr = {
        0: 'first!',
        1: 'hello',
        2: '3',
        length: 3
    }
    
    Array.from(fakeArr).forEach((item,index) => {
        console.log(`item ==> ${item}  index ==> ${index}`);
    })
    
    //伪数组不能直接调用真数组的方法
    /* Array.forEach((item,index) => {
        console.log(`item ==> ${item}  index ==> ${index}`);
    }) */
    
  2. Array.of(v1,v2,v3) 将一系列值转换为数组

    let result = Array.of(1,false,'da')
    console.log(result)     //[1, false, "da"]
    
  3. find(function(value,index,arr){return true}) 找出第一个且只有一个满足条件返回true的元素

    let arr = [1,3,5,6,7,19]
    let result = arr.find(function(item,index){
        //只会返回第一个且只有一个满足条件的元素
        return item > 4
    })
    console.log(result) //5
    
  4. findeIndex(function(value,index,arr){return true}) 找出第一个且只有一个满足条件返回true的元素下标

    let arr = [1,3,5,6,7,19]
    let result = arr.findIndex(function(item,index){
        //只会返回第一个且只有一个满足条件的元素的下标
        return item > 4
    })
    console.log(result) //2
    

bind、call、apply用法详解

bind()、call()、apply()
三者区别:

  1. 三者都能指定this
  2. bind()是将函数返回而不是立即调用,通常用在为回调函数指定this
  3. call()与apply()是立即调用函数
  4. call()传参方式是传入多个参数
  5. apply()传参方式是将多个参数放到数组中传入

call()与apply()

var obj = {
        name: 'fitz'
    }
function foo(name){
    console.log(this)
    console.log('my name is -->' + name);
}

foo('alpha')   //window    alpha
foo.call(obj,'logo')  //obj logo

bind()

var obj = {
        name: 'fitz'
    }
function foo(name){
    console.log(this)
    console.log('my name is -->' + name);
}

foo('alpha')   //window    alpha

//bind()
var bar = foo.bind(obj,'success')
bar()  //obj    my name is --> success

//bind()
foo.bind(obj,'success')()  //obj my name is --> success



//bind()主要用于为回调函数指定this
setTimeOut(function(){
    console.log(this)
}.bind(obj),1000)

let const

let的作用类似于var,用于声明变量
let声明变量的特点:

  1. let有块级作用域

    var name = 'test var'
    {
       var name = 'dd'
    }
    console.log(name);  //dd var没有块级作用域
    
    
    let name = 'test var'
    {
        let name = 'dd'
    }
    console.log(name);  //test var let有块级作用域
    
  2. let 不能再同一作用域内重复声明变量

    let name = 'dd'
    let name = 'cc' //报错Uncaught SyntaxError: Identifier 'name' has already been declared
    
  3. let不会变量提升

    //var声明变量会有变量提升的特点
    console.log(name)   //undefined 此时name已经被声明但是没有复制
    var name = 'test'
    
    //上面的代码等同于
    var name
    console.log(name)
    name = 'test'
    
    
    
    //let没有变量提升
    console.log(name)   //报错Cannot access 'name' before initialization
    let name = 'let is good'
    
    //let的优势在 循环遍历监听 中的体现
    
    //使用var
    //1s过后打印10个10
    for (var i=0; i<10; i++){
        setTimeout(function(){
            console.log(i) // 10 10 10 ... 10
        },1000)
    }
    
    //使用let
    //1s过后打印 1-9
    for (let i=0; i<10; i++){
        setTimeout(function(){
            console.log(i) // 1 2 3 4 5 6 7 8 9
        },1000)
    }
    
  4. 形成一个暂时性死区(TDZ)

    //暂时性死区表现在,在声明变量前无法打印变量值,而是会引发ReferenceError错误
    console.log(name)
    let name = 'Error will be happen'
    //ReferenceError: Cannot access 'name' before initialization
    

const作用是定义一个常量
const声明变量的特点:

  1. 定义赋值后,值不能再被修改
    const name = 'can not change'
    name = 'test to change' //报错Uncaught TypeError: Assignment to constant variable.
    
  2. 其他与let特性相同

let与const声明的变量在全局对象window中访问不到

let a = 'A'
const b = 'B'

//var声明的变量会被挂载到window上,但是let与const声明的变量不会,直接访问便可
console.log(window.a)   //undefined
console.log(a)
console.log(window.b)   //undefined
console.log(b)

变量的解构赋值

从对象或者数组中提取数据,并赋值给多个变量

let myObj = {
        name: 'Fitz',
        age: 20
    }

    let myArray = [1,3,5,7,9]

    //变量的结构赋值 ===> 对象
    let {name, age} = myObj
    console.log(name)
    console.log(age)
        
        
        
//变量的结构赋值 ===> 数组
let [a,b] = myArray
console.log(a)
console.log(b)

//取特定位置的值
let [,,,four,five] = myArray
console.log(four)
console.log(five)

用途介绍: 给函数的形参赋值

//用途
//这是一种需要被改善的用法
function foo(obj){
    console.log(obj.name)
    console.log(obj.age)
}
foo(myObj)

//这是改善后使用变量结构赋值的用法
function bar({name,age}){
    console.log(name)
    console.log(age)
}
bar(myObj)

模板字符串

作用: 简化字符串的拼接

var obj = {
    name: 'Fitz'
}

//这是需要改进的方法(拼接字符串)
console.log('my ' + obj.name + 'is cool')
//这是改进的方法(模板字符串)
console.log(`my ${obj.name} is cool`)

对象的简写方式

对象属性名与变量名相同时,可以省略不写

//引子
let name = 'Fitz'
let age = 20
let obj = {
    name: name,
    age: age,
}
console.log(obj)

//简写属性
let obj2 ={
    name,
    age,
}
console.log(obj2)

对象内的方法也能简写

//引子
let name = 'Fitz'
let obj = {
    name: name,
    sayHello: function(){
        return `${this} and hello!`
    }
}
console.log(obj)
console.log(obj.sayHello())

//简写属性
let obj2 ={
    name,
    sayHello(){     //方法简写
        return `${this} and hello!`
    }
}
console.log(obj2)
console.log(obj2.sayHello())

箭头函数(arrow)

作用: 定义匿名函数

//使用箭头函数方式一
var func = () => console.log('i am a arrow func')
func()

//使用箭头函数方式二
(() => console.log('i am a arrow func too'))()

形参个数与语法

//没有形参时候,()  不能省略
() => console.log('dd')

//一个形参时候,()  可以省略
a => console.log(a)

//多个形参时候,()  不能省略
(x,y) => console.log(x,y)

函数体与语法

//函数体内只有一个语句或表达式
(x,y) => x+y    //此时函数会自动 return 结果, {}省略

//如果{}不省略则需要手动 return 返回结果


//函数体内有多个语句或表达式
var func = (x,y) => {
    let result = x**y
    return result + 1
}

console.log(func(2,3))   //9

箭头函数的特点

  1. 箭头函数没有自己的this
  2. 箭头函数不是在调用的时候决定的,而是在定义的时候箭头函数所处在的对象(包裹住箭头函数的对象)就是它的this
    ===> 透彻理解
    1. 箭头函数的this取决于箭头函数是否被函数包裹着(外层是否有函数)
    2. 如果箭头函数被外层函数包裹,箭头函数的this就是包裹住箭头函数的那个函数的this(箭头函数的this === 外层函数的this)
    3. 没有被函数包裹的话就是this就是window
//测试箭头函数的this
(() => console.log(this))()    //window
let outerFunc = {
    flag: 'i am outerFunc',
    testArrow: function(){
        let innerArrowFunc = () => console.log(this)//outerFunc
        innerArrowFunc()//箭头函数被testArrow包裹这
        //所以this就是testArrow这个函数的this
    }
}
outerFunc.testArrow() //此时testArrow的this ==> outerFunc

三点运算符

用于替代arguments但是比arguments更加灵活
...args 比 arguments 优点在于:...args收集后是一个真数组可以进行遍历,而arguments是一个伪数组不能直接遍历

function foo(...args){
    console.log(args)
    args.forEach(function(item,index){
        console.log(item, index);
    })
}

foo(1,3,5)

三点运算符必须放在最后

function bar(a,b,...inLastest){
    console.log(a)
    console.log(b)
    console.log(inLastest)
}

...args 与 args

  1. ...args是遍历数组中的每一个元素
  2. args是一整个数组
let arr = [1,2,3,4,5]
function test(x,y,...args){
    console.log(...args)    //3,4,5
    console.log(args)       //[3,4,5]
}

使用三点运算符进行扩展运算

let arr = [2,3,4,5]
let arr2 = [1, ...arr , 6]
console.log(arr2)   //[1,2,3,4,5,6]
console.log(...arr2)//1,2,3,4,5,6

形参默认值

function foo(x=3,y=6){
    return x * y
}

console.log(foo()); //9

promise对象

promise的三种状态

  1. pending 初始化状态
  2. fullfilled 成功状态
  3. rejected 失败状态
let promise = new Promise( (resolve,reject) => {
    //此时状态为pending
    console.log('第一')
    //执行异步操作
    setTimeout(()=>{
        console.log('我是定时器')

        //修改状态为 fullfilled,调用成功回调
        resolve('resolve() ==>')

        //修改状态为rejected,调用失败回调
        //reject('reject() ==>')
    },2000)
    
    console.log('我是第二')
})

promise.then(
    //成功的回调函数
    (data)=>{
    console.log(data + ' success');
    },
    
    //失败的回调函数
    (error)=>{
    console.log(error + ' fail');
    }
)

promise封装ajax

function getNews(url){
    //使用promise
    let promise = new Promise((resolve,reject)=>{
        let xhr = new XMLHttpRequest()
        //xhr.open('GET','http://localhost:1080/promise-ajax.js')
        xhr.open('GET',url)
        xhr.send()
        xhr.onreadystatechange = function(){
            if (xhr.readyState === 4){
                if (xhr.status >= 200 && xhr.status < 300){
                    //成功收到ajax请求结果,更改为成功状态
                    resolve('Success' + xhr.responseText)
                }else{
                    reject('Error 没有新闻内容')
                }
            })
        }
    })
    return promise
}

getNews('http://localhost:1080/promise-ajax.js')
    .then(
        (responseText)=>{
        console.log(responseText)
        //成功后,向另一个路由发送评论ajax请求
        //这里return的也是promise对象
        return getNews('http://localhost:1080/comment.js')
        },
        (error)=>{
            console.log(error)
        }
    )
    //这个then用于处理评论的ajax请求
    .then(
        (comment)=>{
        console.log(comment)
        },
        (error)=>{
            console.log(error)
        }
    )

Symbol数据类型

引子
JavaScript中ES6以前总共有6种数据类型,其中5种(String,Boolean,Number,Null,Undefined)是基本数据类型,1种(Object)是引用数据类型

Symbol的作用

  1. 用于解决ES5中由于属性名都是字符串,导致的重名、污染环境的问题

symbol的使用

let obj = {
        username: 'Fitz',
        age: 20
    }

//symbol可以作为对象的一个属性
//但是不能通过obj.symbol这种方式使用
//只能通过obj[symbol]的方式使用
obj[symbol] = 'i am symbol'
console.log(obj);   //{username: "Fitz", age: 20, Symbol(): "i am symbol"}

Symbol的特点

  1. Symbol属性对应的值是唯一的,解决命名冲突的问题
  2. Symbol的值不能与其他数据进行计算,也不能与字符串进行拼串操作
    console.log(`${symbol1} compare of ${symbol2}`) //报错Uncaught TypeError: Cannot convert a Symbol value to a string
    
  3. for...in 与 for...of遍历时不会遍历出symbol属性
    let obj = {
        username: 'Fitz',
        age: 20
    }
    obj[symbol] = 'i am symbol'
    
    //使用for in或者 for of进行遍历无法获取到symbol
    for (let item in obj){
        console.log(item);  //username  age
    }
    

Iterator迭代器

概念:iterator是一种接口机制,为不同的数据结构提供统一的访问机制
作用:

  1. 为各种数据结构提供一个统一、简便的访问接口
  2. 是数据结构中的城院按某种次序排列
  3. 主要为for...of循环使用

工作原理:

  1. 创建一个迭代器对象(指针对象),指向数据结构中的起始位置
  2. 当第一次调用next方法时,指针会往后移动,一直到数据结构中的最后一个成员
  3. 每次调用next方法返回的是一个包含value与done的对象 ==>
    1. 遍历结束后

简单模拟实现iterator

let arr = [1,3,5,'abc']

function myIterator(obj){
    let i = 0
    let isDone = false;
    return {
        //返回一个next方法
        next(){
            if(arr[i] === undefined){
                isDone = true
            }
            return {
                value: arr[i++],
                done: isDone
            }
        }
    }
}

var iter = myIterator(arr)
console.log(iter.next());   //{value: 1, done: false}
console.log(iter.next());   //{value: 3, done: false}
console.log(iter.next());   //{value: 5, done: false}
console.log(iter.next());   //{value: 'abc', done: false}
console.log(iter.next());   //{value: undefined, done: true}

将iterator接口部署到指定的数据类型后可以使用for...of去循环遍历

  1. 已经默认部署了iterator的对象:数组,字符串,arguments,set容器, map容器
    let arr2 = [1,2,3,4,5,6]
    let str = 'iterator'
    function testIterator(){
        for (i of arguments){
            console.log(`${i} <== arguments`);
        }
    }
    
    for (i of arr2){
        console.log(`${i} <== arr2`);
    }
    for (i of str){
        console.log(`${i} <== str`);
    }
    testIterator('arg1','arg2','arg3','arg4','arg5')
    

iterator与symbol.iterator的关系
在指定的数据结构中添加Symbol.iterator的作用是部署iterator接口,当使用for...of去遍历某一个数据结构的时候,会首先去找Symbol.iterator

let target = {
        [Symbol.iterator]: function(){
            let i = 0
            let isDone = false;
            let that = this
            return {
                //返回一个next方法
                next(){
                    if(that[i] === undefined){
                        isDone = true
                    }
                    return {
                        value: that[i++],
                        done: isDone
                    }
                }
            } 
        }
    }

三点运算符与对象解构赋值都运用了iterator

let arr2 = [2,3,4,5]
let arr = [1,...arr2,6]
console.log(arr)
let [item1,item2] = arr
console.log(item1,item2)

Generator生成器

概念

  1. ES6提供的解决异步编程的方案之一
  2. Generator是一个状态机,内部封装了不同状态的数据
  3. 是一个用来生成Iterator的对象
    1. Generator包含Iterator
  4. 可以惰性求值,yield可暂停,next()可以启动,每次返回的都是yield后表达式的结果

特点:

  1. 语法:function与函数名之间有一个*号
  2. 内部用yield表达式定义不同的状态
    function* aGenerator(){
        //我是一个Generator
        yield something
    }
    

向next()方法传入实参可以在启动Generator时,作为yield的返回值
next()方法时从上一次yield的地方开始运行

执行流程分析

//创建一个Generator
//Generator需要next()启动,遇到yield就停止
function* aGenerator(){
    console.log('开始遍历')
    yield 'create by Fitz'
    let value = yield '返回值是next()方法的实参'
    console.log(value)
    console.log('遍历完成')
}

let gen = aGenerator()
console.log(gen.next()) //遇到yield停止
/* 
开始遍历
{value: "create by Fitz", done: false}
 */
console.log(gen.next())
/* 
{value: "返回值是next()方法的实参", done: false}
 */
 //此时停止在let value = yield '返回值是next()方法的实参'
console.log(gen.next('我是返回值')) 
/*
调用next('我是返回值')方法
此时在let value = yield '返回值是next()方法的实参'这一句开始运行
所以返回值value就是next()方法传入的实参 ==> '我是返回值'
*/

/* 结果:
我是返回值
遍历完成
{value: undefined, done: true}
 */

async异步函数

概念: 真正意义上去解决异步回调的问题,以同步的流程表达异步操作
本质: 就只是Generator的语法糖

语法:

async function foo(){
    await 异步操作  //遇到await就暂停等待异步操作完成后,再往下执行
    await 异步操作
}

特点:

  1. 不需要像Generator那样需要调用next(),而是在完成await后面定义的异步操作后,自动往下运行
  2. 返回的总是 promise对象 这意味着可以使用.then(()=>{},()=>{})进行操作
    async foo(){
        return 'Fitz'
    }
    console.log(foo())  //promise{fullfill: 'Fitz'}
    
    foo().then(
        data => console.log(data),
        error => console.log(error)
    )
    

class类

class类的例子

class Person{
    //类的构造方法
    constructor(name,age){
        this.name = name
        this.age = age
    }

    //类的一般方法
    //只能使用对象方法的简写方式
    sayName(){
        console.log(this.name);
    }
}

let person = new Person('Fitz',20)
console.log(person)
person.sayName()

通过constructor定义类的构造方法

class useConstructor{
    constructor (name,age){
        this.name = name
        this.age = age
    }
}

类的继承

//定义一个 中国明星 类
//因为明星也是人,所以从 人类  中继承
class Person{
    constructor (name,age){
    this.name = name
    this.age = age
    }

    //类的一般方法
    //只能使用对象方法的简写方式
    sayName(){
        console.log(this.name)
    }
}

//定义一个 外国明星 类
//因为明星也是人,所以从 人类  中继承
class ChineseStar extends Person{

}

let chineseStar = new ChineseStar('jack chen',60)
console.log(chineseStar)

通过super来调用父类的构造方法

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

    //类的一般方法
    //只能使用对象方法的简写方式
    sayName(){
        console.log(this.name)
    }
}

class Student extends Person{
    constructor (name,age,grade){
        super(name,age)//调用父类的构造方法

        //super()的实参是由ForeignStar这个类的constructor提供
        //而constructor的实参是在类实例中传入
        /* 
            super()的作用相当于直接将父类的constructor复制过来
            //类的构造方法
            constructor(name,age){
                this.name = name
                this.age = age
            }
         */

        this.grade = grade
    }
}
let student = new Student('Fitz',20,'phd')
console.log(student)

重写从父类中继承的一般方法

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

    //类的一般方法
    //只能使用对象方法的简写方式
    sayName(){
        console.log(this.name,this.age)
    }
}

class Student extends Person{
    constructor (name,age,grade){
        super(name,age)
        this.grade = grade
    }

    /*父类中有一个sayName方法,但是只能打印name与age。此时如果还需要打印grade的话
    就需要重写父类的sayName方法
    */
    sayName(){
        console.log(this.name,this.age,this.grade)
    }
}
let student = new Student('Fitz',20,'phd')
console.log(student)
student.sayName()   //'Fitz',20,'phd'
//遵循的是就近原则,所以调用的是子类中的sayName方法

克隆/拷贝

克隆数据有两种方式:

  1. 深克隆: 克隆生成新的数据,修改这个数据不会影响到原数据
    let str = 'abc'
    let str2 = str  //深克隆
    str2 = 'bcd'
    console.log(str)    //'abc'
    
  2. 浅克隆:克隆后不会产生新的数据,而是克隆、引用内存的地址值,修改数据会影响到原来的数据
    let obj = {name: 'Fitz'}
    let obj2 = obj      //浅克隆
    obj2.name = 'Lx'
    console.log(obj.name)   //'Lx'
    

克隆数据的几种方法 (深浅拷贝针对的是数组、对象):

  1. 直接复制给一个变量 浅拷贝

    let obj = {name: 'Fitz'}
    let obj2 = obj      //浅克隆
    obj2.name = 'Lx'
    console.log(obj.name)   //'Lx'
    
  2. Object.assign() 浅拷贝

    let obj = {name: 'Fitz'}
    let obj2 = Object.assign(obj)      //浅克隆
    console.log('obj ==>',obj) 
    console.log('obj2 ==>',obj2) 
    
    console.log(obj.name)   //'Fitz'
    obj2.name = 'Lx'
    console.log(obj.name)   //'Lx'
    console.log(obj2.name)   //'Lx'
    
  3. Array.prototype.concat() 浅拷贝

    let arr = [1,2,{name: 'Fitz'}]
    arr2 = arr.concat()     
    arr2[0] = 666       //深克隆
    arr2[2].name = 'LX' //浅克隆
    console.log(arr)    //[1, 2, {…}]
    console.log(arr2)   //[666, 2, {…}]
    
  4. Array.prototype.slice() 浅拷贝

    let myArr = [1,2,3,{name: 'Fitz'}]
    let myArr2 = myArr.slice()
    myArr2[0] = 666
    myArr2[3].name = 'Change'
    console.log(myArr)
    console.log(myArr2)
    
  5. JSON.parse(JSON.stringify()) 深拷贝

    let toJsonStr = {name: 'Fitz'}
    let result = JSON.stringify(toJsonStr)
    console.log(result) //{"name":"Fitz"}
    let clone = JSON.parse(JSON.stringify(toJsonStr))   //深克隆
    console.log(clone)  //{name: "Fitz"}
    clone.name = 'LX'
    console.log(clone)      //{name: "LX"}
    console.log(toJsonStr)  //{name: "Fitz"}
    

实现深度克隆

深克隆主要的目标是对象、数组,因为普通数据类型都是深克隆

现深度克隆方法的主要思路:

  1. 找到数据结构中的所有对象与数组
  2. 将其中的值全部遍历出来(遍历出的必须是基本数据类型)
  3. 然后进行复制,这个复制一定是深克隆

需要知道的知识:

  1. 检测数据的2种方法

  2. typeof
    返回值只有:Symbol,String,Number,Boolean,Function,Object,Undefined
    NaN ==> Number
    null,array ==> Object

  3. Object.prototype.toString()

    //该函数可以检测数据结构的类型
     function checkType(target){
         return Object.prototype.toString.call(target).slice(8,-1)
     }
    
  4. for...in枚举 对数组、对象的作用

    let obj = {name: 'Fitz',age: 20}
    let arr = [1,3,5,7,9]
    
    for (let i in obj){
        console.log(i)  //name age
    }
    
    for (let i in arr){
        console.log(i)  //0,1,2,3,4
    }
    /* 
        结论:
            对象枚举出的是属性值
            数组枚举出的是下标值
     */
    

实现深度克隆

//该函数可以检测数据结构的类型
function checkType(target){
    return Object.prototype.toString.call(target).slice(8,-1)
}


//实现深度克隆
function deepClone(target){
    let type = checkType(target)   //得到数据类型
    let result     //初始化

    //处理最终的包装容器
    if(type === 'Object'){
       result = {}
    }else if(type === 'Array'){
       result = []
    }else{
        //当数据类型是基本数据类型时
       return target   //递归的基线条件
    }

    //处理目标数据
    //同时使用Object与Array
    for (let i in target){
       //当是Object时,返回属性值
       //当是Array时,返回下标
       let value = target[i]   //都能拿到obj与arr中所有的元素
       
       //当是多维的Object与Array
       if(checkType(value) === 'Object' || checkType(value) === 'Array'){
           //例如: [1,[2],{a: 1}]   {a: [1,[2],{b: 2}]}
           //使用递归
           result[i] = deepClone(value)
       }else{  //当Object与Array是一维的
           result[i] = value
       }
    }

    return result
}


//测试
let arrForDeepClone = [1,[2],{a: 1}]
let objForDeepClone = {a: [1,[2],{b: 2}]}
let arrForDeepClone2 = deepClone(arrForDeepClone)
let objForDeepClone2 = deepClone(objForDeepClone)
arrForDeepClone2[1][0] = 666
console.log(arrForDeepClone,arrForDeepClone2)
objForDeepClone['a'][2]['b'] = 666
console.log(objForDeepClone,objForDeepClone2)

Set容器

什么是Set容器?
概念: Set容器是有无序、不可重复的多个value的集合体

Set容器的方法

  1. Set()

    //创建一个set容器实例
    let set = new Set()
    console.log(set)    //Set(0) {}
    
  2. Set(Array)

    //创建一个set容器实例
    let set = new Set([1,1,2,3,4,2,3,6,2,54])
    console.log(set)    //Set(6) {1, 2, 3, 4, 6, 54}
    
  3. add(value)

    //创建一个set容器实例
    let set = new Set([1,1,2,3,4,2,3,6,2,54])
    console.log(set)    //Set(6) {1, 2, 3, 4, 6, 54}
    
    set.add(666)
    console.log(set)    //Set(7) {1, 2, 3, 4, 6, 54, 666}
    
  4. delete(value)

    //创建一个set容器实例
    let set = new Set([1,1,2,3,4,2,3,6,2,54])
    console.log(set)    //Set(6) {1, 2, 3, 4, 6, 54}
    
    set.delete(4)
    console.log(set)    //Set(6) {1, 2, 3, 6, 54, 666}
    
  5. has(value)

    //创建一个set容器实例
    let set = new Set([1,1,2,3,4,2,3,6,2,54])
    console.log(set)    //Set(6) {1, 2, 3, 4, 6, 54}
    
    console.log(set.has(3))    //true
    
  6. clear()

    //创建一个set容器实例
    let set = new Set([1,1,2,3,4,2,3,6,2,54])
    console.log(set)    //Set(6) {1, 2, 3, 4, 6, 54}
    
    set.clear()        
    console.log(set)           //Set(0) {}
    
  7. size

    //创建一个set容器实例
    let set = new Set([1,1,2,3,4,2,3,6,2,54])
    console.log(set)    //Set(6) {1, 2, 3, 4, 6, 54}
    
    console.log(set.size)      //6
    

Map容器

什么是Map容器?
概念: Map容器是无序的key不重复的 多个key-value集合体

Map容器的方法:

  1. Map()

    let map = new Map()
    console.log(map)    //Map(0) {}
    
  2. Map(Array)

    let map = new Map([
        ['name1','Fitz'],
        ['name2','Da'],
        ])
    console.log(map)    //Map(2) {"name1" => "Fitz", "name2" => "Da"}
    
  3. set(key,value)

    let map = new Map([
        ['name1','Fitz'],
        ['name2','Da'],
        ])
    console.log(map)    //Map(2) {"name1" => "Fitz", "name2" => "Da"}
    
    map.set('name3','Lx')
    console.log(map)    //{"name1" => "Fitz", "name2" => "Da", "name3" => "Lx"}
    
  4. get(key)

    let map = new Map([
        ['name1','Fitz'],
        ['name2','Da'],
        ])
    console.log(map)    //Map(2) {"name1" => "Fitz", "name2" => "Da"}
    
    console.log(map.get('name2'))   //da
    
  5. delete(key)

    let map = new Map([
        ['name1','Fitz'],
        ['name2','Da'],
        ])
    
    map.delete('name3')
    console.log(map)    //Map(2) {"name1" => "Fitz", "name2" => "Da"}
    
  6. has(key)

    let map = new Map([
        ['name1','Fitz'],
        ['name2','Da'],
        ])
    
    console.log(map.has('name2'))   //true
    
  7. clear()

    let map = new Map([
        ['name1','Fitz'],
        ['name2','Da'],
        ])
    console.log(map)    //Map(2) {"name1" => "Fitz", "name2" => "Da"}
    
    map.clear()
    console.log(map)    //Map(0) {}
    
  8. size

    let map = new Map([
        ['name1','Fitz'],
        ['name2','Da'],
        ])
    console.log(map)    //Map(2) {"name1" => "Fitz", "name2" => "Da"}
    
    console.log(map.size)   //2
    

Array.prototype.includes


posted @ 2020-10-18 00:29  虚伪渲染敷衍  阅读(112)  评论(0编辑  收藏  举报