js的深拷贝和浅拷贝?啥情况讲解下??底层堆栈空间??object.prototype.toString.call(),还有bind,的具体使用?还有instanceof对象的实例,还有哪个方法可以直接获取到我定义的数据是什么类型的。哟哪几种数据类型?包括哪些?
1. 堆(Heap)与 栈(Stack):内存的真相
在 JS 里,内存被分为两块:
-
栈(Stack) :空间小、速度快。存放基本数据类型 (String, Number, Boolean, Null, Undefined, Symbol, BigInt)和引用类型的地址。
-
堆(Heap) :空间大。存放引用数据类型(Object, Array, Function)。
2. 深拷贝 vs 浅拷贝
这是由于"堆栈分离"导致的现象:
-
浅拷贝(Shallow Copy):
-
情况:只复制了栈里的"地址"。
-
后果:新旧对象指向同一个堆空间。改了新对象的属性,旧对象的也跟着变。
-
实现 :
Object.assign()、{...obj}、arr.slice()。
-
-
深拷贝(Deep Copy):
-
情况:在堆里开辟了一块全新的空间,把内容全写进去。
-
后果:两者完全独立,互不影响。
-
实现:
-
JSON.parse(JSON.stringify(obj))(简单粗暴,但不能处理函数和循环引用)。 -
递归手写(面试最爱)。
-
structuredClone(obj)(现代浏览器原生支持)。
-
-
3. 类型检测:到底我是谁?
JS 有 8 种 数据类型:
-
基本类型 :
Number,String,Boolean,Undefined,Null,Symbol,BigInt。 -
引用类型 :
Object(包括 Array, Function, Date 等)。
怎么获取准确类型?
-
typeof:快,但对null和Array都会返回"object"(这是 JS 的陈年 BUG)。 -
instanceof:判断对象实例 是否属于某个构造函数(顺着原型链找)。比如[] instanceof Array是true。 -
Object.prototype.toString.call()(终极方案): 它可以直接穿透一切伪装,告诉你的底层"户籍"。JavaScript
Object.prototype.toString.call([]); // "[object Array]" Object.prototype.toString.call(123); // "[object Number]" Object.prototype.toString.call(null); // "[object Null]" Object.prototype.toString.call(new Date()); // "[object Date]"
4. bind 的具体使用
bind 是 call 和 apply 的兄弟,但它最特别。
-
作用 :创建一个新函数,强制把这个新函数的
this绑定到你指定的对象上。 -
区别 :
call/apply会立即执行函数,而bind是返回一个新函数,不立即执行。
实战场景: 在 Vue2 或者是旧的 React 类组件里,防止方法里的 this 丢失。
JavaScript
const device = {
name: '感应器',
getName: function() {
console.log(this.name);
}
};
const unboundGet = device.getName;
unboundGet(); // undefined (因为此时 this 指向全局)
const boundGet = device.getName.bind(device);
boundGet(); // '感应器' (强制锁定了 this)
5. instanceof 的底层原理
它其实就是在玩**"连连看"**。 L instanceof R 的原理是:看 L 的 __proto__ 是不是等于 R 的 prototype。如果不等于,就继续往 L 的爷爷辈(__proto__.__proto__)找,直到找到或者到头为止。
js的深拷贝和浅拷贝还不明白??本博主教育你!!
1. 核心:为什么会有这两种拷贝?
这得从 JS 的内存管理说起。JS 把数据存在两个地方:栈(Stack) 和 堆(Heap)。
-
基本类型 (数字、字符串等):直接存在栈里,值就在那。
-
引用类型 (对象、数组):内容存在堆 里,但在栈里存了一个"地址"(就像一把指向仓库的钥匙)。
2. 浅拷贝(Shallow Copy):只复制钥匙
当你做浅拷贝时,JS 只是在栈里又配了一把一模一样的钥匙。
-
结果:两个变量指向堆里的同一个"仓库"。
-
后果:你拿着新钥匙进仓库改了东西,老王拿着旧钥匙进去一看,东西也变了。
常见操作:
-
Object.assign({}, oldObj) -
{ ...oldObj }(展开运算符) -
Array.prototype.slice()
代码演示:
JavaScript
let a = { name: '张三', favorite: { food: '火锅' } };
let b = { ...a }; // 浅拷贝
b.name = '李四';
console.log(a.name); // '张三' (基本类型在栈里,互不影响)
b.favorite.food = '串串';
console.log(a.favorite.food); // '串串' (引用类型指向同一个堆,一起变了!)
3. 深拷贝 (Deep Copy):直接克隆一个新仓库
深拷贝会把堆里的东西全部复制一份,塞进一个新的堆空间,并给你一把指向新空间的钥匙。
-
结果:两个变量完全独立。
-
后果:你在新仓库里把天拆了,老王的旧仓库也纹丝不动。
常见操作:
-
最简单(有缺陷) :
JSON.parse(JSON.stringify(obj))- 缺点:不能拷贝函数、undefined、正则,会丢失原型链。
-
现代原生 :
structuredClone(obj)- 优点:2022年后的浏览器原生支持,非常强大。
-
终极武器 :手写递归或使用
Lodash的_.cloneDeep。
4. 怎么获取数据类型?(你要的那个方法)
你刚才问有没有一个方法能直接看穿所有类型?有!就是我提到的 "万能判定法"。
typeof 太弱了(数组、对象、null 它都说是 object),我们要用这个:
JavaScript
| 特性 | 浅拷贝 | 深拷贝 |
|---|---|---|
| 复制内容 | 复制对象的引用(地址) | 复制对象的所有层级结构 |
| 内存位置 | 栈中地址不同,堆中内容相同 | 栈中地址不同,堆中内容也不同 |
| 相互影响 | 修改嵌套对象时,会互相影响 | 彻底隔离,互不影响 |
| 性能 | 快 | 慢(递归耗时) |
function getType(target) {
// 截取字符串,只保留中间的类型名,比如 "Array"
return Object.prototype.toString.call(target).slice(8, -1);
}
console.log(getType([])); // "Array"
console.log(getType({})); // "Object"
console.log(getType(null)); // "Null"
console.log(getType(new Date())); // "Date"