-
据上可得:无论是数组还是对象,经过解构赋值之后的新变量,两者之间相互没有影响,那么我们是否可以下定义了:解构赋值是深拷贝?
-
不要急,我们再来看一组代码:
let arr3 = [[1,2],[3,4],[5,6]];
let newArr3 = […arr3];
newArr3[0][1] = 33333;
console.log(newArr2, arr2)
打印结果是:
-
我们可以看到:两组数据相互影响了
-
那么问题就来了,为什么上面的代码可以实现互不影响,下面却不行?
-
答案是:解构不是真正的深拷贝,是伪深拷贝
-
结论:解构只能深拷贝一维数组与一维对象,多维数组和对象无效
1.3 如何实现一个深拷贝?
这里有两种方式
- JSON()的相关API,代码如下:
let list = [
{id: 1, stuName: ‘小明’, class:‘五年二班’},
{id: 2, stuName: ‘小红’, class:‘五年三班’},
{id: 3, stuName: ‘小绿’, class:‘五年四班’}
]
// JSON.stringify : 对象转json字符串
// JSON.parse : 将json字符串转换成json对象
let newList = JSON.parse(JSON.stringify(list));
let newObj = {
id: 4,
stuName: ‘小白’,
class: ‘五年四班’
}
newList.push(newObj)
newList[2].class = ‘六年一班’;
console.log(list, newList)
打印结果如下:
-
由此可见,JSON相关方法,确实可以实现深拷贝。
-
但是此类方法只能适用于部分场景
-
如果对象里有 function这种关键字是不行的,JSON()方法会将它转化为字符串,那么,自然不行,性质都不一样了
2. 用原生JS写一个真正的深拷贝,适用于所有场景:
-
原理简单粗暴:将对象里的值一个一个取过来,重新定义
-
代码如下:
// 标准的深拷贝
function deepClone(source){
// 判断复制的目标是数组还是对象
const targetObj = source.constructor === Array ? [] : {};
// 遍历目标
for(let keys in source){
if(source.hasOwnProperty(keys)){
// 如果值是对象,就递归一下
if(source[keys] && typeof source[keys] === ‘object’){
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{
// 如果不是,就直接赋值 (基本类型)
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
- 测试是否管用:
let objD = {
name: ‘小猪’,
age: 20,
detail: {
color: ‘白色’,
type: ‘种猪’
}
}
let newObjD = deepClone(objD);
newObjD.detail.color = “黑色”;
console.log(objD, newObjD)
- 实际结果:
-
实验表明,确实是管用的
-
FAQ:深拷贝的代码务必手写下来,真的很重要
1.1 原型是什么?有什么用?
-
原型 prototype是 js 中极其重要的概念之一,也是面试高频问题点,也是比较容易引起混淆的地方
-
原型的概念:
-
首先,我们明确原型是一个对象
-
每个函数都有一个属性叫做原型(函数特有的),这个属性指向一个对象
-
原型是函数对象的属性,不是所有对象的属性,对象经过构造函数new出来,那么这个new出来的对象的构造函数有一个属性叫原型
-
当哦我们定义一个函数的时候,这个函数的原型属性也就被定义出来了,并且可以使用它。如果不对它进行显示赋值的话,那么它的初始值就是一个空的对象Object(默认添加的)
-
简单来说:原型是函数的一个特有属性
function fn1(a , b){
return a*b;
}
console.log(fn1.prototype); // 函数默认添加的原型
console.log(fn1.constructor); // 构造器指向
-
从上面我们可以看到:函数fn1的原型是Object(默认添加的,所以为空)
-
函数的构造器是 Function
-
当然,我们也可以手动给它添加原型属性和方法:
function fn1(a , b){
return a*b;
}
// 手动添加原型属性和方法
// 通过 new 关键字可以继承
fn1.prototype.name = “小明”;
fn1.prototype.age = 18;
fn1.prototype.fn = function(){
console.log(“年纪是:”+this.age);
}
1.2 原型链( __ proto __ )
-
定义:有限的实例对象和原型之间组成有限链,就是用来实现共享属性和继承的
-
从1.1中,我们知道了:所有的函数都是 Function 的实例。在构造函数上都有一个原型属性 prototype,该属性也是一个对象;那么在原型对象上有一个 constructor 属性,该属性指向的就是构造函数;
-
而实例对象上有一个 _ proto _ 属性,该属性也指向原型对象,并且该属性不是标准属性,不可以用在编程中,该属性用于浏览器内部使用(只用于看,不能操作)
你需要理解这几句话:
-
在函数里有一个属性prototype
-
由该函数创建的对象默认会连接到该属性上
-
_proto_是站在对象角度来说的
-
prototype 是站在构造函数角度来说的
-
如下图:
看图看不懂?代码来说话:
function Person() {
}
Person.prototype.name = “养猪的王某人”;
Person.prototype.age = 18;
Person.prototype.getAge = function () {
console.log(this.age);
}
let person1 = new Person();
console.log(person1);
console.log(person1.name); // 继承父级的属性
console.log(person1.constructor);
- 打印结果:
图解:
-
person1 通过 new关键字继承 Person(),可以得到原型链上的属性和方法
-
说的通俗一点,小明上学,自己挣不到钱,可以用住他爸妈的房子,花他们的钱,这就属于继承(血脉继承)。
-
函数内的构造器(constructor)指向构造他的函数
原型链的查找规则:
- 由下往上找,一层一层找,直到找到 null,如果找到 null还没找到,就报错。
-
寻找自身属性,如果找到了,就返回该值
-
如果没找到,继续往上找(逐级)
-
直到找到 null为止
-
如果还没找到,抛出错误
看了这么久,休息一会儿吧
-
this 的指向在函数创建的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁。
-
一般来说,谁调用,指向谁(并不是所有情况)
-
最外层的 this (指向window)
- 方法内的 this(依然指向window)
function a(){
let userName = “张三”;
console.log(this) // window
}
a(); // 相当于window.a()
- 对象内的 this(看调用情况)
let o = {
userName: ‘张三’,
fn:function(){
console.log(this.userName);
}
}
o.fn(); // 打印结果:张三 o调用 this 指向o
- 箭头函数本身没有作用域,所以 this 指向它的上级作用域
var id = 66;
function fn5(){
setTimeout(()=>{
console.log(this.id + ‘====id’)
},500)
}
fn5({id:21});
-
箭头函数,this 指向定义时候的对象,fn5在window作用域下,所以this指向window;
-
箭头函数的外层,fn5函数的this就是window
-
箭头函数的this与它的执行没有关系,在定义的时候就决定了
function Person(){
this.name = ‘朱小明’;
this.fn = function(){
console.log(‘名字是:’ + this.name)
}
}
let person1 = new Person();
-
创建一个空的对象
-
链接到原型
-
绑定this指向,执行构造函数
-
确保返回的是对象
-
代码来解释一下:
let obj = new Object();
obj.proto= Person.prototype; // 设置原型链
let result = Person.call(obj); // 让Person的this指向obj,并执行Person函数体
// 判断Person的返回值类型:
// 如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象
if (typeof(result) == “object”){
person1 = result;
}
else{
person1 = obj;;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

更多面试题
**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等
何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-q8uM98bI-1712800202512)]
[外链图片转存中…(img-ndqfaeQ7-1712800202513)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-ql2Cpwa6-1712800202513)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

更多面试题
**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等
[外链图片转存中…(img-iddjNfjM-1712800202513)]