共计 15638 个字符,预计需要花费 40 分钟才能阅读完成。
前言
本篇章次要介绍了援用类型的特点,诸如正则表达式、工夫类型、根本包装类、Object、数组等,至于 function 局部,内容较多,将另起篇章。
面试答复
1. 援用类型:援用类型有 array、object、function。援用类型属于堆内存,占用的空间不固定,保留和复制的是指针。援用类型罕用 instanceof 去判断,如果自身不晓得数据属于什么类型的话,就用 object.prototype.toString.call 办法去判断,不论是 instanceof 还是 call 办法,他们实质上都是通过原型链判断。
2. 数组去重:最罕用的是利用 Set 这种数据结构,搭配 Array.from 或者扩大运算符来实现。至于其余形式,不论是利用 indexOf、includes、sort(sort 排序后雷同的元素会相邻),首先都会有一层遍历。
3. 深浅拷贝:深浅拷贝的区别在于,浅拷贝只复制指针,新旧对象仍应用同一块内存。而深拷贝会创立一个截然不同的对象,开拓新的内存,因而批改新对象不会影响原对象。简略点,就用 JSON.parse 包裹着 JSON.stringify,这种形式算深拷贝,因为它开拓了新的内存空间,只不过 JSON.stringify 会失落一些数据,比方 undefined、function。精确点,就须要采取递归的形式去深拷贝,判断传入的数据是不是 object,如果不是就抛出去,如果是那么就持续递归。
4.new 关键字:new 关键字先是创立新的对象,而后继承传入的函数原型,批改 this 指向并且执行构造函数,最初返回新的对象。
知识点
援用类型都是堆(heap),有两个特色:1. 保留和复制都是指针。2. 占用空间不固定(大小不定), 也不会主动开释,堆内存中。
1.RegExp
根本规定
对于 RegExp 对象还是常常会应用到,比方限度 input 输入框(校验邮箱是否合乎)、匹配变量值做 if 判断等等。RegExp 类型既能够通过字面量申明的形式创立,也能够通过 new 的形式创立;
字面量申明:/pattern/attributes,例:let re1 = /ABC/g
new 申明:new RegExp(pattern, attributes),例 let regex=new RegExp(‘/abc/’,g)
pattern 既能够是正则表达式,也能够是字符串,如果是字符串,则如下:
let reg = /hello/g
let test = reg.test("hello World")
console.log(test) // true
attributes 为可选的字符串,含属性 “g”、”i” 和 “m”,别离用于指定全局匹配、不辨别大小写的匹配和多行匹配
上面着重梳理几条规定,用来可能根本的辨认正则表达式,具体属性能够参考:RegExp 根底规定
1./ xxx /g,所有的正则表达式均写在 / / 内,反斜杠代表本义,如 \d,示意数字
2.^
示意行的结尾,^\d
示意必须以数字结尾
$
示意行的完结,\d$
示意必须以数字完结
有头有尾则必须完全符合规定才可匹配,如 ^\d{3}$
则示意,间断三个数字才可匹配。
3.[]示意可能匹配的范畴,匹配的是单个字符
4. 如果波及特殊字符如 '-'
,在正则表达式中,要用'\-'
本义,如,/^\d{3}\-\d{3,8}$/
。
5.n{X,Y}示意至多呈现 X 个间断 n,至少呈现 Y 个间断 n 时,匹配。Y 可不写。闭区间,含 X,Y
6.* 等于{0,} 即大于等于 0 次;+ 代表{1,} 即大于等于 1 次;?代表{0,1} 即 0 次或 1 次
7. 用 ()
示意的就是要提取的分组。比方:^(\d{3})\-(\d{3,8})$
8. 取反:[^abc]
,查找任何不在方括号之间的字符。
依据下面 8 条规定来辨认一下下述含意:
let re = /^\d{3}\-\d{3,8}[0-9][a-z]$/;
去掉 //, 主体为 ^\d{3}\-\d{3,8}$
^,示意开始
\d{3},示意间断三个数字
\-,示意特殊字符 -
\d{3,8},示意至多间断 3 个数字至少间断 8 个数字
[0-9],示意这个地位的字符必须为 0 -9
[a-z],示意这个地位的字符必须为 a -z
$,示意完结
罕用办法
RegExp 对象有 4 个办法:test()、exec()、compile()、match()
。
test():test() 办法检索字符串中的指定值。返回值是 true 或 false。
match():match()办法返回值是合乎规定的值(数组)。
exec():exec() 办法检索字符串中的指定值。返回值是被找到的值。如果没有发现匹配,则返回 null
compile():compile()办法用于扭转 RegExp。
2.Date
定义
Date 获取的工夫为本地工夫,即电脑或手机上的设施工夫,如果你设施工夫不精确,那么你获取的工夫同样也会不精确;如若波及时区问题,能够应用 UTC 工夫 (零时区)。
根底 API
// 获取以后工夫
let b = new Date()
// 获取年份
b.getFullYear()
// 获取月份
b.getMonth()+1
// 获取天
b.getDate()
// 获取小时
b.getHours()
// 获取分钟
b.getMinutes()
// 获取秒数
b.getSeconds()
// 获取毫秒
b.getMilliseconds()
// 获取星期几
b.getDay()
// 获取字符串模式
b.toString()
// 字符串模式仅工夫局部
b.toTimeString()
// 字符串模式仅日期局部
b.toDateString()
// 获取工夫戳
b.getTime()(当地时区)或者 b.valueOf()
// 获取 UTC 工夫
b.toUTCString()
// 获取指定日期的工夫类型
new Date(yyyy,mth,dd,hh,mm,ss); mth 为 0 -11
new Date('2017-05-24 12:30:23')
// 日期加减,年月日时分秒同理,可查问相干 API
let dt = new Date('2021-11-10');
dt.setDate(dt.getDate() + 10);
console.log(dt.toString()); // Tue Nov 20 2021 08:00:00 GMT+0800 (中国规范工夫)
利用场景
1. 针对要求高精确性的工夫,举个例子:
假如:本地工夫、服务器工夫不统一,而这个假如产生的概率很大。
需要:一个案件须要点击拨打电话后才可提交案件
操作:点击拨打按钮记录的是本地工夫(用来展现);提交案件记录的是服务器的工夫(后端须要记录案件提交的工夫);而这时候工夫不统一会带来许多剖析上的麻烦。比方你明明 12:30:00(本地工夫)打了电话,提交案件的工夫却是 12:00:00(服务器工夫)。实践上提交案件的工夫应该晚于拨打工夫,却因为工夫不统一,导致数据分析有异样。
解决办法:通过调用接口获取服务器工夫,对立工夫上的治理,防止造成数据分析的麻烦。
经验总结:如果波及到工夫问题,特地是对工夫准确要求较高的需要时,须要思考工夫获取起源的对立。
2. 针对工夫格局的对立与转换
问题原因:不同的浏览器对于 new Date()的解析反对是不同的,比方 ios 就只反对 ”2021/12/12 12:21:21″,而局部浏览器则只反对 ”2021-12-12 12:21:21″。而有时候需要对工夫格局有着另外的要求,也须要对工夫进行对立的解决。
经验总结:
首先,波及到工夫问题,首先要思考工夫格局能不能被辨认兼容
其次,须要与后端对立好工夫格局,防止因为工夫格局产生的报错,诸如显示、转换等,举荐办法是前后端传值对立应用工夫戳,工夫格局由前端进行转换。
最初,波及工夫格局的转换,常见的便是 new Date()、elementUI 中的 dateTimepicker,而工夫格局的转换我这边举荐插件 moment.js 或者 dayjs 或者自行封装一个繁难的工夫处理函数。
3. 根本包装类型
实质
为了便于操作根本类型值,ECMAScript 还提供了 3 个非凡的援用类型:Boolean、Number 和 String。而 boolean、number、string 根本类型之所以可能间接操作是因为在调用办法的过程中,默默进行根本包装类型的解决,如下:
let str1 = 'hello'; //string 根本类型
let str2 = str.indexOf('o'); // 在执行到这一句的时候 后盾会主动实现以下动作:(var _str = new String('hello') // 过对应的包装对象创立出一个和根本类型值雷同的对象
var s2 = _str.indexOf('o'); // 通过包装对象进行办法的调用,并赋值给 s2
_str = null; // 销毁包装对象)
相干知识点
装箱:根底类型 –> 援用类型
string:new String()
boolean:new Boolean()
number:new Number()
拆箱:援用类型 –> 根底类型
let numObj = new Number(123)
let strObj = new String('zxc')
let booObj = new Boolean(true)
console.log(typeof numObj) //object
console.log(typeof strObj) //object
console.log(typeof booObj) //object
// 拆箱
console.log(typeof numObj.valueOf() ); //number
console.log(typeof strObj.valueOf() ); //string
console.log(typeof booObj.valueOf() ); //boolean
4.Object
属性的加强写法
const name ='chiji'
const age = '18'
const obj = {
name, //name:name
age //age:age
}
console.log(obj.name) //chiji
console.log(obj.age) //18
JSON 的了解
JSON 是一种轻量级的数据交换格局,次要是为了跨平台交换数据用的,因而他的格局要求也绝对标准严格,比方 JSON 的属性名必须有双引号,如果值是字符串,也必须是双引号。因为在申请接口中应用的 JSON 格局,所以对于前端代码中的入参根本都是通过序列化的形式进行入参的解决,如:
let o = {a:1}
let ojson = JSON.stringify(o) //'{"a":1}' string 类型
let oobj = JSON.parse(ojson) //{a: 1} object 类型
遍历办法
let obj= {
a:'1',
b:2,
c:false
}
Object.keys
Object.keys(obj)
//['a', 'b', 'c']
Object.values
const obj = {
100:'a',
2:'b',
7:'c'
};
Object.values(obj)
//['b','c','a']
PS: 如果属性名为数值的属性,是依照数值大小,从小到大遍历的
Object.entries(obj)
Object.entries(obj)
//[['a', '1'],['b', 2],['c', false]]
for…in:数组也能够应用,然而该办法会遍历对象的整个原型链,性能十分差,且遍历程序不确定,不举荐应用
for(let key in obj){console.log(key + '---' + obj[key])
}
//a---1
//b---2
//c---false
Object.getOwnPropertyNames(obj):
Object.getOwnPropertyNames(obj)
// ['a', 'b', 'c']
Reflect.ownKeys(obj)
Reflect.ownKeys(obj)
// ['a', 'b', 'c']
对象转数组
Object.keys({name:'张三',age:14}) //['name','age']
Object.values({name:'张三',age:14}) //['张三',14]
Object.entries({name:'张三',age:14}) //[[name,'张三'],[age,14]]
对象合并
对象:let obj1 = {
a:1,
b:'zxp',
c:function(){},
d:{
abc:'123',
qwe:{}}
}
let obj2 = {a: 5};
--------- 合并 --------
let obj3 = {...obj1, ...obj2};
// 如果有反复的 key,则前面的会将后面的值笼罩掉
//Object.assign(target, source1, source2,...);
//let obj3 = Object.assign({},obj1, obj2);
// 如果不应用 {} 作为 target,合并后 obj1 的数据也会批改
//{
a:5,
b:'zxp',
c:function(){},
d:{
abc:'123',
qwe:{}}
}
5.Array
与 Object 的区别
1. 数组有序,Object 无序
2. 数组的元素能够没有属性名(然而有索引),对象的元素必须有值
3. 数组只能用整数作为数组元素的索引,而不能用字符串,且数组元素的应用只能通过方括号 (arr[0]) 来获取
4. 数组的空元素 empty 示意空位, 它不是一种数据类型, 而是因为人为批改 arr.length 或者写入时多写了逗号造成的。empty 和 undefined 在操作数组时的次要区别在于: 应用数组的 forEach()办法遍历时会主动疏忽空位, 而应用 for 循环则会将 empty 转换为 undefined 并遍历。
let arr = [1,2,3,4,5]
delete arr[3]
console.log(arr)
Tip:应用对象或数组的办法前肯定要先判断其是否存在,因为数组常常会连带办法应用,比方 arr.indexOf(‘123’),此时如果 arr 不存在,那么程序会间接报错。
不批改原数组的办法
1、2:判断数组中是否蕴含一个元素
3、4:找出第一个符合条件的数组成员
5:判断数组是否为空,因为数组是对象,因而存在援用地址的问题,所以不能应用 arr === [] 来判断数组是否为空
6、7:合并数组能够应用, 不过 6、7 都属于浅拷贝,即仅实用于对不蕴含援用对象的一维数组的拷贝,就是对象不是那种多层嵌套的
8:复制数组, 不过 8 属于浅拷贝,即仅实用于对不蕴含援用对象的一维数组的拷贝,就是对象不是那种多层嵌套的
9:适宜用于将数组元素拼接后转换成字符串, 但如果元素为 undefined 或 null,它会被转换为空字符串。
let arr = [1,'2',false,null,{a:1}]
1.includes(searchvalue, start):searchvalue 为查找元素,start 为查找起始 index
arr.includes(1)
//true
2.indexOf:同 1
arr.indexOf(1)>-1
//true
3.find(function(currentValue, index, arr)):currentValue 为以后元素,index 为元素索引,arr 为以后数组
arr.find((n)=>n>0)
//1 1 为值
4.findIndex():同 3
arr.findIndex(function(value){return value >0})
//0 0 为索引
5.length
arr.length ===0
//false
6.Spread 扩大运算符(...):
const arr1 = [11,22,33]
const arr2=[...arr,...arr1]
console.log(arr2)
//[1, '2', false, null, {a:1}, 11, 22, 33]
7.concat(arr1,arr2)
let combine = arr.concat([11,22,33])
//[1, '2', false, null, {a:1}, 11, 22, 33]
8.slice(start,end): 返回一个新数组,start 为起始 index,end 为完结 index
let copy = arr.slice()
copy
// [1,'2',false,null,{a:1}]
9.join(str)
let str =arr.join(',')
str
//'1,2,false,,[object Object]'
批改原数组的办法
let arr = [1,'2',false,{a:1},null]
1.push(item): 从数组最初面增加元素(一个或多个),返回值为增加完后的数组的长度
arr.push(666) // 6
arr // [1,'2',false,{a:1},null,666]
2.unshift(unshift): 从后面增加元素, 返回值是增加完后的数组的长度
arr.unshift(666) // 6
arr //[666,1,'2',false,{a:1},null]
3.pop():从数组最初面删除一个元素,返回值是删除的元素
arr.pop() // null
arr //[1,'2',false,{a:1}]
4.shift(): 从后面删除元素,只能删除一个, 返回值是删除的元素
arr.shift() // 1
arr //['2',false,{a:1},null]
5.reverse():颠倒数组中元素的排列程序
arr.reverse();
arr // [null, {a:1}, false,'2',1]
6.sort(): 间接应用 sort()办法,默认的排序办法会将元素转换为字符串,而后比拟字符串中字符的 UTF-16 的编码程序来进行排序,所以这里接管一个函数,返回值是比拟这两个数的绝对程序的值
var arr = [3,4,8,5,1,6,7]
var brr = arr.sort((a,b)=>a-b)
//[1,3,4,5,6,7,8]
7.splice(index,howmany,item1,...,item2):index 为起始地位,howmany 为解决的数据数量是个 number,item1 为新元素。若是删除,则返回被删除元素组成的数组, 不传值默认全删
let item = arr.splice(0, 3);
console.log(item);//[1, '2', false]
console.log(arr); [{a:1}, null]
8.fill(value,start,end): 将数组中指定区间的所有元素的值,都替换成某个固定的值, 并返回这个数组。value 是要填充的数字,start 是起始索引(数组下标),end 是完结索引(数组下标),含头不含尾,如果 start/end 为正数,则理论为 start+length/end+length
arr.fill(9);// [9, 9, 9,9, 9]
arr.fill(9,2);// [1, '2', 9, 9, 9]
arr.fill(9,1,3);//[1, 9, 9, {…}, null]
arr.fill(9,3,3);// [1, '2', false, {…}, null]
arr.fill(9,-3,-1);// [1, '2', 9, 9, null] , 能够了解为从数组倒数第三个开始,到数组倒数第一个完结(非数组下标)9.copyWithin(target,start,end):在以后数组外部,将指定地位的元素复制到其余地位, 并返回这个数组。target, 索引从该地位开始替换数组项(蕴含)。start,索引从该地位开始读取数组项,默认为 0. 如果为负值,则从右往左读,end,索引到该地位进行读取的数组项,默认是 Array.length, 如果是负值,示意倒数,含头不含尾。let arr = [1,'2',false,{a:1},null,3,3,3,3,3,3,3,3,3,3]
arr.copyWithin(3,0,4);
// 0,4 读取的是 1,'2',false,{a:1} , 只用这四个值顺次替换 1 遍
// 3 从数组下标为 3 的地位开始替换(蕴含)// [1, '2', false, 1, '2', false, {…}, 3, 3, 3, 3, 3, 3, 3, 3]
遍历办法
let arr = [1,'2',false,{a:1},null]
1.for...of:for (const value of arr) {console.log(value);
}
//1
//'2'
//false
//{a: 1}
//null
2.for...in: 尽量别用,Object 能用的办法 Arry 都能用,不过输入的值不肯定是你要的
for(let key in arr){console.log(arr[key])
}
//1
//'2'
//false
//{a: 1}
//null
3.array.forEach():
arr.forEach((val,index)=>{console.log('val:'+val+';index:'+index)
})
//val:1;index:0
//val:2;index:1
//val:false;index:2
//val:[object Object];index:3
//val:null;index:4
4.arr.map():
arr.map((item,index)=>{console.log('index',index)
console.log('item',item)
})
// index 0 item 1
// index 1 item 2
// index 2 item false
// index 3 item {a: 1}
// index 4 item null
PS:forEach()和 map()的区别
1.forEach()办法不会返回执行后果,而是 undefined; 而 map()办法会失去一个新的数组并返回
2. 因为 map()底层做了深度优化,同样的一组数组,map()的执行速度优于 forEach()
5.arr.every((item,index)=>{
// 这里写判断,须要每一项都合乎,最初才返回 true,否则返回 false
return item >100
})
6.arr.some((item,index)=>{
// 这里写判断,只有有一项都合乎,便 true,否则返回 false
return item >100
})
7.arr.filter((item,index)=>{
// 符合条件的,将作为数组的元素,最初返回一个数组
return typeof item==='number'
})
//[1]
8.arr.reduce((prev,cur,index,arr)=>{return prev + cur},init)
//arr 示意原数组;//init 示意初始值
//prev 示意上一次调用回调时的返回值,或者初始值 init;
//cur 示意以后正在解决的数组元素;//index 示意以后正在解决的数组元素的索引,若提供 init 值,则索引为 0,否则索引为 1;如果没有 init, 输入如下:'null[object Object]false21'
9.arr.reduceRight((prev,cur,index,arr)=>{return prev + cur},init)
//reduceRight()办法的性能和 reduce()性能是一样的,不同的是 reduceRight()从数组的开端向前执行
数组合并
数组:let arr1 = [false,'',1,'5']
let arr2 = [9,'8',true,function(){}]
let arr3 = arr1.concat(arr2);
//let arr3 = [...arr1,...arr2)];
//arr1.push(...arr2); 会扭转原数组
//[false,'',1,'5',9,'8',true,function(){}]
数组去重
let obj = {
a:1,
b:'zxp',
c:function(){},
d:{
abc:'123',
qwe:{}}
}
let arr = [1,1,1,'chiji',obj,obj,'c','c']
Set 对象 +Spread 扩大运算符
[...new Set(arr)] //[1,'chiji',obj,'c']
毛病:其实也不能算是缺点,只是满足不了需要
如果数组如下,let arr = [1,1,1,'chiji',obj,obj,{a:1},{a:1},'c','c',{}]
[...new Set(arr)] //[1,'chiji',obj,{a:1},{a:1},'c',{}]
因为实质上两个 {a:1} 的内存存储地址不一样,所以不会被去重,而往往在需要上,这种数据是须要咱们去重的, 空对象也是无奈被删除的。PS: 这边能够想想,如何去除 {}、[]、{a:1} 等状况,以及 object 的去重,搞个公共办法进去
数组扁平化
//1.Array.flat(n)是 ES10 扁平数组的 api,n 示意维度,n 值为 Infinity 时维度为无限大
[1,[2,3]].flat(1) //[1,2,3]
[1,[2,3,[4,5]]].flat(2) //[1,2,3,4,5]
[1,[2,3,[4,5]]].toString() //'1,2,3,4,5'
[1[2,3,[4,5[...]].flat(Infinity) //[1,2,3,4...n]
//2. 递归
let arr = [1,[2,3],[4,[5,6]],[7,[8,[9,10]]]]
function toOne(array){let newArr = []
let deepArr = (arr) =>{if(typeof arr === 'object'){
arr.map(item=>{let itemType = Object.prototype.toString.call(item)
if(itemType === '[object Array]'){deepArr(item)
}else{newArr.push(item)
}
})
}else{newArr.push(arr)
}
}
deepArr(array)
return newArr
}
toOne(arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
利用
1. 关键字
前端关键字有:delete、switch、case、default、break(跳出循环,map 办法中不可用)、for、in、continue(跳出本次循环)、while、if、else、return(进行办法的执行)、try、catch、this、do、with、finally、throw、let、var、function、instanceof、typeof、new 等,这边筛选局部进行解释。
delete 关键字
let arr = [1,2,3,4,5,6]
let obj= {
"a":"1",
"b":"2",
"c":"3",
"d":"4",
}
delete arr[0]
console.log(arr) //[empty, 2, 3, 4, 5, 6]
delete obj.b
console.log(obj) // {a: '1', c: '1', d: '1'}
new 关键字
接下来形容一些 new 操作符的的原理、过程及实现:
new 操作符的作用是通过构造函数来创立一个实例对象,那么 new 操作符做的事包含:1. 创立新的对象
2. 继承传入函数的原型
3. 批改 this 指向并执行构造函数
4. 返回新对象
实现:function _new(Fn, ...args) {let obj = {}; // 创立一个空对象
obj.__proto__ = Fn.prototype; // 继承函数原型就是,使实例对象的__proto__(原型)= 构造函数的 prototype(原型)
// 下面两步也能够合并为:let obj=Object.create(Fn.prototype);
let res = Fn.call(obj, ...args); // 批改 this 指向并执行构造函数
// 下面这步也能够写为:let res = Fn.apply(obj, args); 区别是一个参数为数组,一个参数为 xx,xx,xx 的模式
return res instanceof Object ? res : obj; // 如果 new 作用的指标是根底类型,则返回 obj 根底类型,如果是援用类型 object,则返回 res
}
验证:// 构造函数
function Person(name, age){
this.name = name;
this.age = age;
this.test = {
a:'test',
b:function(){}
}
return this.test // 如果构造函数有 return,且为 Object,则 p 为这个,否则 p 为 obj
}
let p = _new(Person, 'Tom', 20)
console.log(p)
//{a: 'test', b: ƒ}
PS: 至于为什么不能写成 let p = _new Person('Tom', 20)的模式,是因为_new 是咱们自定义的办法,须要以办法的模式调用;而 let p = new Person(xxxx)之所以能够如此应用,是因为 new 是 ECMA 规定的内置语法,如果你非要写成_new Person()模式,就相当于新增了一个内置语法,等同于 ES5 降级到 ES6
2. 解构赋值
数组:let [a, [[b], c]] = [1, [[2], 3]];
// a = 1 b = 2 c = 3
let [a = 1, b] = [];
// a = 1, b = undefined
let [a, ...b] = [1, 2, 3];
//a = 1 b = [2, 3]
let [a = 3, b = a] = [1, 2];
// a = 1, b = 2
对象:let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y}] } = obj;
// x = 'hello' y = 'world'
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10 b = 20 rest = {c: 30, d: 40}
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
3. 深浅拷贝
深浅拷贝的区别在于,浅拷贝只复制指向某个对象的指针,而不复制对象自身,新旧对象还是共享同一块内存。但深拷贝会另外发明一个截然不同的对象,新对象跟原对象不共享内存,批改新对象不会改到原对象。
具体表现即,深拷贝的数据与被拷贝对象齐全没有关系,不论被拷贝对象如何改变,都不会影响深拷贝的数据;而浅拷贝的数据如果内层不嵌套,则与深拷贝的体现统一,如果内层嵌套,则被拷贝对象嵌套局部的数据批改会使拷贝对象同步批改。
数据:
let obj = {
a:1,
b:'zxp',
c:function(){},
d:{
abc:'123',
qwe:{}}
}
let target = {a: 1, b: 1};
let arr = [1,'chiji',obj,'c']
浅拷贝:
//Object.assign(target,source):将源对象(source)的所有可枚举属性,复制到指标对象(target), 只能拷贝一层。Object.assign(target, obj);
// 数组
let copy = Array.from(new Set(arr)); // 留神:该办法不能用在原数组里有反复项,会被去重
//let copy = [...arr];
//let copy = arr.slice();
//let copy = Array.of (...arr);
//let copy = new Array(...arr);
//let copy = arr.concat();//concat() 办法用于连贯两个或多个数组
arr[0] = 2
//arr 批改,copy 不变
arr[2]['d']['abc'] = 987
//arr 批改,copy 批改,obj 批改
深拷贝:
let copy = JSON.parse(JSON.stringify(arr))
// 简便状况下能够应用该办法,然而该办法存在缺点:null,NaN, undefined, Infinity,Date 对象等数据类型在转化过程中会呈现失落、异样等状况,至于具体每个异常情况本人去搜寻一下
arr[0] = 2
//arr 批改,copy 不变
arr[2]['d']['abc'] = 987
//arr 批改,copy 不变
递归深拷贝:
function deepCopy(data){
let target
if(!data && typeof data !== 'object'){
// 如果是根底类型,那么就间接返回
target = data
}else{target = data instanceof Array ? [] : {}
// 遍历数据,将 key 输入为数组,不便进行遍历
Object.keys(data).forEach((key)=>{if(data[key] && typeof data[key] === 'object'){
// 如果数据的值为对象,那么递归持续遍历上来
target[key] = deepCopy(data[key])
}else{target[key] = data[key]
}
})
}
return target
}
4. 类型判断
let bool = true
let num = 1
let str = 'abc'
let und= undefined
let nul = null
let arr = [1,2,3,4]
let obj = {name:'chiji',age:25}
let fun = function(){console.log('hello')}
let sym = Symbol()
typeof:typeof 能够辨认出根本类型 boolean,number,undefined,string,symbol,然而不能辨认援用数据类型,会把 null、array、object 对立归为 object, 然而能够辨认出 function。
console.log(typeof bool) //boolean
console.log(typeof num) //number
console.log(typeof str) //string
console.log(typeof und) //undefined
console.log(typeof sym) //symbol
console.log(typeof fun) //function
console.log(typeof nul) //object
console.log(typeof arr) //object
console.log(typeof obj) //object
instanceof:能够辨认援用类型,如 array、object、function。
因为 js 的继承都是采纳原型链来继承的,因而应用 instanceof 还能够检测出 new 申明的类型的多层继承关系。
console.log(bool instanceof Boolean) // false
console.log(num instanceof Number) // false
console.log(str instanceof String) // false
console.log(und instanceof Object) // false
console.log(nul instanceof Object) // false
// 此处的根本类型均非通过根本包装类型 new 生成,因而原型链上找不到原型返回 false
console.log(arr instanceof Array) // true
console.log(obj instanceof Object) // true
console.log(fun instanceof Function) // true
console.log(sym instanceof Symbol) // false
Object.prototype.toString.call:此办法能够绝对较全的判断 js 的数据类型,然而比拟长,嫌麻烦的话能够依据场景抉择上述办法;如果检测的是个根本类型,实际上会主动转成对应的援用类型,比方 string 会转成 new String(),但对于自定义类型,就只能用 instanceof 了。
console.log(Object.prototype.toString.call(bool)) //[object Boolean]
console.log(Object.prototype.toString.call(num)) //[object Number]
console.log(Object.prototype.toString.call(str)) //[object String]
console.log(Object.prototype.toString.call(und)) //[object Undefined]
console.log(Object.prototype.toString.call(nul)) //[object Null]
console.log(Object.prototype.toString.call(sym)) //[object Symbol]
console.log(Object.prototype.toString.call(arr)) //[object Array]
console.log(Object.prototype.toString.call(obj)) //[object Object]
console.log(Object.prototype.toString.call(fun)) //[object Function]
Array.isArray:此办法为 ES5 新增,用于判断 JS 数组类型
console.log(Array.isArray(arr)) // true
5.intanceof 操作符的原理及实现
MDN 定义:instanceof 运算符用于测试构造函数的 prototype 属性是否呈现在对象原型链中的任何地位
了解:原理也就是后面了解的原型链知识点。这里的主体一个是构造函数的原型,一个是实例对象的原型。如果构造函数的原型在实例对象的原型上,则返回 true,反之,false。left instanceof right // 实际上 left 等同于实例对象,right 等同于构造函数
function _instanceof (left, right) {
let l = left.__proto__; // 实例对象的原型
while (l) {if (right.prototype === l) {return true;}
l = l.__proto__;
}
return false;
}
const obj = new Object({a:1})
const res = _instanceof(obj,Object) // 此处用法起因与 new 的 PS 统一
console.log(res)
最初
走过路过,不要错过,点赞、珍藏、评论三连~