携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
前言
大白话说JS内容包括:DOM的一些操作,Promise相关, 微任务宏任务,作用域,变量提升,闭包,变量类型,深浅拷贝,原型和作用域链,后续争取把js重点都记录上,深入浅出。
变量类型,深浅拷贝
常知的变量类型有:基本类型(String、Number、Boolean、Symbol、Undefined、Null,BigInt),和引用类型(object , array ,function )。基本类型在桟中执行,使用具体数值直接存储;引用类型则在堆中,且 存储的数据是地址 而非具体数值。
顺带说下类型的判断:
- typeof:分不清普通对象(typeof null =object)还是数组对象( typeof []=object)
- instanceof:用来测A 是不是B的原型的,能清楚判断对象类型但也只能测对象类型
- constructor: 利用创建时产生的原型链追溯类型,null 和 undefined 是无效的对象,所以测不了
- Object.prototype.toString.call():都能测
//类型判断
typeof("")
[] instanceof Array;// true
{} instanceof Object;// true
newDate() instanceof Date;// true
let arr=[];
console.log(b.constructor === Array);
console.log("".constructor === String);
Object.prototype.toString.call(newDate()) ;// [object Date]
Object.prototype.toString.call([]) ;// [object Array]
// 基础类型
let a = 10;
let b = a;
a = 20;
console.log('b:', b); //输出b: 10
// 引用类型
let c = { age: 20 };
let d = c;
c.age = 21;
console.log('d:', d); //输出d: { age: 21 }
在上面的例子中,原始类型的变量有私有内存各自存储数值,一方的改变不会牵动自身;引用类型d指向c的存储地址,c值改变后,d用的依旧是c的地址所以输出21。
上面其实引出了深拷贝和浅拷贝的概念,浅拷贝:复制的同时一方变另一放跟着变(修改的是栈内存中的同一个值),深拷贝,完全复制了一个对象,一方的改变不会牵动另一方(修改堆的不同的值)。
实际项目中常用 let newArray = [...Array] 来浅拷贝对象。
如何实现浅拷贝?
如果是数组,可以使用slice,concat,Array.from , 展开运算符 ,来返回一个新数组的特性实现拷贝。但是如果数组嵌套了其他引用类型, concat 方法将复制不完整。
原理:
function copy (obj){
if(typeof obj === 'object' && obj !== '' ){
let copy = Array.isArray(obj) ? [] :{};
for(var p in obj){
copy[p]=obj[p];
}
return copy;
}
else {return obj;}
}
例子:
var obj = {
A: '1',
B: {
b: '12',
c: 28,
},
}
var copy1 = [];
Object.assign(copy,obj);//或者 var copy = Object.assign({},obj);
var copy2 = [].concat(obj);
var copy3 = {...obj};
copy.color = '33'; // 改变原数据第一层属性 A (基本数据类型)
copy.person.name = '44'; // 改变原数据第一层属性 B(引用数据类型)
console.log(obj,copy); // 原数据的引用类型会改变,copy全变
深拷贝才是重点:
JSON.parse(JSON.stringify()) :如果原对象中有undefined、Symbol、函数时,会导致该键值被丢失,有RegExp、Error对象,会被转换为空对象{},有Date,会被转换成字符串),有NaN、Infinity和-Infinity,则序列化的结果会变成null,一句话,它只能序列化对象的可枚举的自有属性,有局限性。
所以在项目中,我们一般用现有的深拷贝库,如lodash的Api,:_.cloneDeep: 直接克隆
var new_arr = JSON.parse( JSON.stringify(old_arr) );
原理是JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象,通过这两个方法,也可以实现对象的深复制,但是这个方法不能够拷贝函数 。
手写一个深拷贝(巩固理解):
function deepClone (obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
//判断ojb子元素是否为对象,如果是,递归复制,如果不是,简单复制
objClone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
}
}
}
return objClone;
}
let a = [1, 2, 3, 4],
b = deepClone(a);
a[0] = 2;
console.log(a, b);
深拷贝是拷贝对象各个层级的属性,看对象的属性是否是对象或数组类型,进行相应属性的一一复制。
ES6新增特性:
-
let和const(看变量提升,块级作用域,能否重定义),以及Symbol,Bigint变量。
-
解构赋值:let arr = [1,2,3,4,5]; const [a,b,c,d,e] = arr; 业务中很常用
-
模块化:export default { 暴露的对象 } 导出; import { a } from ‘暴露的对象’ 导入
-
拓展运算符(...):可以将多个数组或对象解构形成一个新元素(在去重,浅拷贝,解构很常用) :
const c = [...new Set([...a,...b])]; -
箭头函数:()=>{} ,箭头函数和普通函数的区别(this,实例化,argument)
-
扁平化数组:Object.values(数组).flat(Infinity),Infinity为数组的维度,flat不支持IE浏览器
-
模版字符串:let str3=
我爱中国的${str2} -
数组新增的api:原来的:传送门 新增:(includes,startsWith,endsWith)
-
双问号
??,与||作用相同:作用就是判断该对象有没有默认值,没有就采取问号后面的值,比如输入框非空判断if((value ?? '') !== ''),?.则是判断该对象有无该属性或方法obj?.eat,没有就返回undefined,不会报错,在业务中算是常用,(在TS中,还有!.就告诉编译器,一定有这属性,别给我报这属性可能不存在的错)