深夜爆肝JS好文!2024字节跳动春招面试题深度讲解(1)

本文探讨了JavaScript中的解构赋值与深拷贝的区别,指出解构并非深拷贝,仅适用于一维数组和对象。介绍了使用JSON.stringify和原生JavaScript实现深拷贝的方法,以及原型和原型链的概念,最后涉及this指向和new关键字的作用。
摘要由CSDN通过智能技术生成
  • 据上可得:无论是数组还是对象,经过解构赋值之后的新变量,两者之间相互没有影响,那么我们是否可以下定义了:解构赋值是深拷贝?

  • 不要急,我们再来看一组代码:

let arr3 = [[1,2],[3,4],[5,6]];

let newArr3 = […arr3];

newArr3[0][1] = 33333;

console.log(newArr2, arr2)

打印结果是:

在这里插入图片描述

  • 我们可以看到:两组数据相互影响了

  • 那么问题就来了,为什么上面的代码可以实现互不影响,下面却不行?

  • 答案是:解构不是真正的深拷贝,是伪深拷贝

  • 结论:解构只能深拷贝一维数组与一维对象,多维数组和对象无效

1.3 如何实现一个深拷贝?

这里有两种方式

  1. 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:深拷贝的代码务必手写下来,真的很重要

2. 原型与原型链


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还没找到,就报错。
  1. 寻找自身属性,如果找到了,就返回该值

  2. 如果没找到,继续往上找(逐级)

  3. 直到找到 null为止

  4. 如果还没找到,抛出错误

看了这么久,休息一会儿吧

在这里插入图片描述

3. this 指向问题


  • 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与它的执行没有关系,在定义的时候就决定了

在这里插入图片描述

4. new 关键字做了什么?


function Person(){

this.name = ‘朱小明’;

this.fn = function(){

console.log(‘名字是:’ + this.name)

}

}

let person1 = new Person();

  1. 创建一个空的对象

  2. 链接到原型

  3. 绑定this指向,执行构造函数

  4. 确保返回的是对象

  5. 代码来解释一下:

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前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

更多面试题

**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等

资料获取方式:点击蓝色传送门获取

何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-q8uM98bI-1712800202512)]

[外链图片转存中…(img-ndqfaeQ7-1712800202513)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-ql2Cpwa6-1712800202513)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

更多面试题

**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等

资料获取方式:点击蓝色传送门获取

[外链图片转存中…(img-iddjNfjM-1712800202513)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值