类型判断
JS数据类型
- 基本数据类型:String、Number、Boolean、Symbol、Null、Undefined
- 引用数据类型:Object
数据类型的判断
- typeof
typeof 'aa' // "string"
typeof 1; // "number"
typeof true; // "boolean"
typeof Symbol(); // "symbol"
typeof []; // "object"
typeof {}; // "object"
typeof undefined; ; // "undefined"
typeof null; // "object"
typeof new Date(); // "object"
typeof new Function(); // "object"
typeof new RegExp(); // "object"
可以看到typeof无法明确区分出Array、null、Object、Date、Reg
- instanceof
用来判断已知是对象的target的具体类型,判断A是否是B的实例,检测的是原型
[] instanceof Array // true
Symbol() instanceof Symbol // true
{} instanceof Object // true
new Date() instanceof Date // true
new RegExp() instanceof RegExp // true
new Function() instanceof Function // true
- constructor
[].constructor === Array // true
Symbol().constructor === Symbol // true
{} instanceof Object // true
new Date().constructor === Date // true
new RegExp().constructor === RegExp // true
new Function().constructor === Function // true
- prototype
Object.prototype.toString.call(target) === ‘[object Type]’
Object.prototype.toString.call(1) === '[object Number]'
Object.prototype.toString.call('') === '[object String]'
Object.prototype.toString.call(true) === '[object Boolean]'
Object.prototype.toString.call(null) === '[object Null]'
Object.prototype.toString.call(undefined) === '[object Undefined]'
Object.prototype.toString.call([]) === '[object Array]'
Object.prototype.toString.call(new Date) === '[object Date]'
Object.prototype.toString.call(function a(){}) === '[object Function]'
判断数组的方法
function adjustArray(o) {
if(!o) {
return false;
}
if(typeof Array.isArray === 'function') {
return Array.isArray(o);
} else {
return Object.prototype.toString.call(o) === '[object Array]';
}
}
深浅拷贝
浅拷贝
浅拷贝的意思就是只复制引用(指针),而未复制真正的值。用=复制
深拷贝
- 方法一
// 递归
export function deepCopy(target) {
if(!target || typeOf target != 'object') {
return target;
}
let newObj = target.constructor == Array ? [] : {};
for (let i in target) {
if(target.hasOwnProperty(i)){
if(target[i] && typeof target[i] == 'object'){
newObj[i] = deepCopy(target[i]);
} else {
newObj[i] = target[i];
}
}
}
return newObj;
}
- 方法二
// JSON 简单情形(undefined、function、symbol 会在转换过程中被忽略)
let newObj = JSON.parse(JSON.stringfy(target));
首层浅拷贝
首层浅拷贝:对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。
。
- js数组有两个方法 concat 和 slice 是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组。他们实现的是首层浅拷贝
- Object.assign() 拷贝的是属性值。是首层浅拷贝。Object.assign(target, source) 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。
- 拓展运算符(…)实现的同样是对对象进行首层浅拷贝
// 示例
const originArray = [1, 'aa',[ 'bb',2]];
const originObj = {a:1,b:{bb:2}};
const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[3].push(3);
console.log(originArray); // [1, 'aa',[ 'bb',2, 3]];
const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 'rrr';
console.log(originObj); // {a:1,b:{bb:'rrr'}}
首层浅拷贝的实现
对对象第一层进行遍历赋值
function complexClone(source) {
const targetObj = source.constructor === Array ? [] : {};
for (let key in source) { // 遍历目标
if (source.hasOwnProperty(key)) {
targetObj[key] = source[key];
}
}
return targetObj;
}
let temp = [1,2,3, [4,5]];
let cloneArr = complexClone(temp);
cloneArr[3][0] = 'aaaaa';
console.log(temp) // [1,2,3, ['aaaaa',5]];
总结:
赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值;
JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;
JSON.stringify 实现的是深拷贝,但是对目标对象有要求(非 undefined,function);
若想真正意义上的深拷贝,请递归。
深度优先、广度优先遍历
和树的遍历类似,
- 深度优先遍历(DFS):优先发现子节点,子节点遍历完成后再遍历兄弟节点;
- 广度优先遍历:优先遍历同级兄弟节点,记录所有兄弟节点之后,再回头遍历子节点。
Advanced-Frontend中有两篇答题内容解释的很清楚,推荐大家看一下
深度优先、广度优先遍历概念
深度优先拷贝、广度优先拷贝实现