看完我的笔记不懂也会懂----ECMAscript 567
ECMAscript 567
严格模式
如何开启严格模式?
function strictMode(){
'use strict'
//something
}
开启严格模式的特性:
- 必须用var关键字声明变量
'use strict' name = 'Fitz' console.log(name) //报错 nams is not defined
- 自定义函数中的this不能指向window
'use strict' function Test(){ this.name = 'Fitz' } Test(); //此时直接执行函数相当于 window.name = 'Fitz' // 但是在严格模式下自定义函数this不能指向window 所以 报错
- eval()中的代码有独立作用域
'use strict' var i=666 eval('var i=3; console.log(i)') //i=3 console.log(i) //i=666
- 对象中的属性名不能出现相同
'use strict' obj = { name: 'dd', name: 'cc' } //报错
- 全局对象是undefined
非严格模式中 ==> window 严格模式 ==> undefined
字符串扩展
-
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
-
str.startsWith(str): 判断是否以指定字符串开头
let str = 'this is a string' console.log(str.startsWith('t')) //true console.log(str.startsWith('a')) //false
-
str.endsWith(str): 判断是否以指定字符串结尾
let str = 'this is a string' console.log(str.endsWith('g')) //true console.log(str.endsWith('h')) //false
-
str.repeat(count): 重复指定次数
let str = 'this is a string' console.log(str.repeat(2)) //this is a stringthis is a string
数值的扩展
-
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
-
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
-
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
-
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
-
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对象方法扩展
-
Object.create(prototype,[descriptors])
-
作用:
1. 以指定对象为原型创建新的对象
2. 为新对象指定新属性,并对属性执行描述 -
常用的属性
- value: 自定义属性值
- writable:标识描述属性是否能修改,默认为false
- configurable: 标识当前属性是否可以被删除,默认为false
- 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 } })
-
-
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
-
Object.is(v1,v2) 判断两个数据是否完全相等
- 原理是先转换成字符串再判断两者是否相等
console.log(0 === -0) //true console.log(NaN === NaN) //false console.log(Object.is(0,-0)) //false console.log(Object.is(NaN,NaN)) //true
-
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"}
-
直接操作__proto__属性
//直接操作 __proto__ let myObj5 = {} let myObj6 = {dream: 'rich'} myObj5.__proto__ = myObj6 console.log(myObj5) console.log(myObj5.dream) //rich
数组的扩展
-
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) //不会改变元素组
-
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) //不会改变元素组
数组方法的扩展
-
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}`); }) */
-
Array.of(v1,v2,v3) 将一系列值转换为数组
let result = Array.of(1,false,'da') console.log(result) //[1, false, "da"]
-
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
-
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()
三者区别:
- 三者都能指定this
- bind()是将函数返回而不是立即调用,通常用在为回调函数指定this
- call()与apply()是立即调用函数
- call()传参方式是传入多个参数
- 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声明变量的特点:
-
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有块级作用域
-
let 不能再同一作用域内重复声明变量
let name = 'dd' let name = 'cc' //报错Uncaught SyntaxError: Identifier 'name' has already been declared
-
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) }
-
形成一个暂时性死区(TDZ)
//暂时性死区表现在,在声明变量前无法打印变量值,而是会引发ReferenceError错误 console.log(name) let name = 'Error will be happen' //ReferenceError: Cannot access 'name' before initialization
const作用是定义一个常量
const声明变量的特点:
- 定义赋值后,值不能再被修改
const name = 'can not change' name = 'test to change' //报错Uncaught TypeError: Assignment to constant variable.
- 其他与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
箭头函数的特点
- 箭头函数没有自己的this
- 箭头函数不是在调用的时候决定的,而是在定义的时候箭头函数所处在的对象(包裹住箭头函数的对象)就是它的this
===> 透彻理解- 箭头函数的this取决于箭头函数是否被函数包裹着(外层是否有函数)
- 如果箭头函数被外层函数包裹,箭头函数的this就是包裹住箭头函数的那个函数的this(箭头函数的this === 外层函数的this)
- 没有被函数包裹的话就是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
- ...args是遍历数组中的每一个元素
- 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的三种状态
- pending 初始化状态
- fullfilled 成功状态
- 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的作用
- 用于解决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的特点
- Symbol属性对应的值是唯一的,解决命名冲突的问题
- Symbol的值不能与其他数据进行计算,也不能与字符串进行拼串操作
console.log(`${symbol1} compare of ${symbol2}`) //报错Uncaught TypeError: Cannot convert a Symbol value to a string
- 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是一种接口机制,为不同的数据结构提供统一的访问机制
作用:
- 为各种数据结构提供一个统一、简便的访问接口
- 是数据结构中的城院按某种次序排列
- 主要为for...of循环使用
工作原理:
- 创建一个迭代器对象(指针对象),指向数据结构中的起始位置
- 当第一次调用next方法时,指针会往后移动,一直到数据结构中的最后一个成员
- 每次调用next方法返回的是一个包含value与done的对象 ==>
- 遍历结束后
简单模拟实现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去循环遍历
- 已经默认部署了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生成器
概念
- ES6提供的解决异步编程的方案之一
- Generator是一个状态机,内部封装了不同状态的数据
- 是一个用来生成Iterator的对象
- Generator包含Iterator
- 可以惰性求值,yield可暂停,next()可以启动,每次返回的都是yield后表达式的结果
特点:
- 语法:function与函数名之间有一个*号
- 内部用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 异步操作
}
特点:
- 不需要像Generator那样需要调用next(),而是在完成await后面定义的异步操作后,自动往下运行
- 返回的总是 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方法
克隆/拷贝
克隆数据有两种方式:
- 深克隆: 克隆生成新的数据,修改这个数据不会影响到原数据
let str = 'abc' let str2 = str //深克隆 str2 = 'bcd' console.log(str) //'abc'
- 浅克隆:克隆后不会产生新的数据,而是克隆、引用内存的地址值,修改数据会影响到原来的数据
let obj = {name: 'Fitz'} let obj2 = obj //浅克隆 obj2.name = 'Lx' console.log(obj.name) //'Lx'
克隆数据的几种方法 (深浅拷贝针对的是数组、对象):
-
直接复制给一个变量 浅拷贝
let obj = {name: 'Fitz'} let obj2 = obj //浅克隆 obj2.name = 'Lx' console.log(obj.name) //'Lx'
-
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'
-
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, {…}]
-
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)
-
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"}
实现深度克隆
深克隆主要的目标是对象、数组,因为普通数据类型都是深克隆
现深度克隆方法的主要思路:
- 找到数据结构中的所有对象与数组
- 将其中的值全部遍历出来(遍历出的必须是基本数据类型)
- 然后进行复制,这个复制一定是深克隆
需要知道的知识:
-
检测数据的2种方法
-
typeof
返回值只有:Symbol,String,Number,Boolean,Function,Object,Undefined
NaN ==> Number
null,array ==> Object -
Object.prototype.toString()
//该函数可以检测数据结构的类型 function checkType(target){ return Object.prototype.toString.call(target).slice(8,-1) }
-
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容器的方法
-
Set()
//创建一个set容器实例 let set = new Set() console.log(set) //Set(0) {}
-
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}
-
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}
-
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}
-
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
-
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) {}
-
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容器的方法:
-
Map()
let map = new Map() console.log(map) //Map(0) {}
-
Map(Array)
let map = new Map([ ['name1','Fitz'], ['name2','Da'], ]) console.log(map) //Map(2) {"name1" => "Fitz", "name2" => "Da"}
-
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"}
-
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
-
delete(key)
let map = new Map([ ['name1','Fitz'], ['name2','Da'], ]) map.delete('name3') console.log(map) //Map(2) {"name1" => "Fitz", "name2" => "Da"}
-
has(key)
let map = new Map([ ['name1','Fitz'], ['name2','Da'], ]) console.log(map.has('name2')) //true
-
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) {}
-
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