剑指offer —— 2021字节跳动春招面试题深度讲解(JS篇)

  • 1. 深拷贝与浅拷贝
  • 2. 原型与原型链
  • 3. this 指向问题
  • 4. new 关键字做了什么?
  • 5. 原生JS如何实现事件委托?
  • 6. 手写一个冒泡排序
  • 8. 如何一行代码实现数组去重?
  • 9. null与undefined有什么区别?
  • 本文选题都是字节跳动中一些 经典且有一定难度的JS面试题
  • 含金量杠杠的!,并且会进行深入的讲解,专业讲解分析,包教包会

1. 深拷贝与浅拷贝

1.1 深拷贝与浅拷贝有什么区别?

  • 深拷贝和浅拷贝只针对象: Object, Array 这样的复杂对象(引用数据类型),基本数据类型不存在深浅拷贝,只存在赋值操作
  • 简单来说,浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。

举个例子:

  • 比如A和B分手了,如果这两人分手后还有联系,藕断丝连,那就是浅拷贝,如果 分手之后再无联系,各自过各自的生活,互不影响,这叫深拷贝。

1.2 解构是深拷贝吗?为什么?

  • 这里就是面试官给你挖坑了,考察你的基础扎实程度。
  • 我们先来看一组代码:

数组解构赋值

let arr2 = [1,2,3,4];
let newArr2 = [...arr2]; //此处是解构赋值
newArr2.push(5);
console.log(arr2, newArr2) // arr2的结果: 1,2,3,4 newArr2的结果: 1,2,3,4,5

对象解构赋值

let objA = {name: '小明',age: 8
}let {name , age} = objA;
name = '小花';
age = 10;
console.log(name, age, objA); // 1.name为小花,2.age为10 3.objA没有变化
  • 据上可得:无论是数组还是对象,经过解构赋值之后的新变量,两者之间相互没有影响,那么我们是否可以下定义了:解构赋值是深拷贝?

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

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)
<script>console.log(this) // window
</script>
  • 方法内的 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;;}
  • 默认情况下函数返回值为 undefined,即没有显示定义返回值的话
  • 构造函数例外,new 构造函数在没有 return 的情况下默认返回新创建的对象

5. 原生JS如何实现事件委托?

  • 事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件
  • 举个栗子:
    • 有三个同事预计会在周一收到快递。为签收快递,
    • 有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。
    • 前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,
    • 甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

具体实现:

<!DOCTYPE html>
<html>
<head><title>事件委托</title>
</head>
<body><ul id="ul"><li>0</li><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul><button id="btn">点我添加一个li</button>
</body>
<script>
// 事件委托具体实现
let ul = document.getElementById("ul");ul.onclick = function (event) {event = event || window.event;let target = event.target;// 获取目标元素if (target.nodeName == 'LI') {alert(target.innerHTML);}}// 为按钮绑定点击事件let btn = document.getElementById('btn');btn.onclick = function () {let li = document.createElement('li');// 新增li的内容为ul当前子元素的个数li.textContent = ul.children.length;ul.appendChild(li);}
</script>
</html>

6. 手写一个冒泡排序

  • 两个for循环就搞定了
  • 相邻两个比较大小,如果后者比前者小,就交换两者位置,以此类推
let array = [5, 4, 3, 2, 1];
let temp = 0;
for (var i = 0; i <array.length; i++){for (var j = 0; j <array.length - i; j++){if (array[j] > array[j + 1]){temp = array[j + 1];array[j + 1] = array[j];array[j] = temp;}
}

8. 如何一行代码实现数组去重?

[...new Set([1,2,3,1,'a',1,'a'])]

9. null与undefined有什么区别?

  • null 是一个表示"无"的对象,转为数值时为 0,布尔值为 false
  • undefined 是一个表示"无"的原始值,转为数值时为 NaN
  • 当声明的变量还未被初始化时,变量的默认值为 undefined
  • null 用来表示尚未存在的对象,undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义

1. 希望本文能对大家有所帮助,如有错误,敬请指出

2. 原创不易,还请各位客官动动发财的小手支持一波(关注、评论、点赞、收藏)
3. 拜谢各位,好人明天中一百万!

深夜爆肝JS好文!2021字节跳动春招面试题深度讲解相关推荐

  1. 2021字节跳动春招技术面试题:mysql间隙锁触发条件

    java基础 1.1java的8种基本数据类型装箱拆箱 1.2重写重载封装继承多态 1.3 Stack Queue 1.7 Concurrent包 1.8面向对象 1.9 String StringB ...

  2. Android开发之MVVM模式实践(六),2021字节跳动春招技术面试题

    以上是我们创建协程的实现方式,我们可以通过指定Dispatchers来决定协程到底在什么线程中工作,而其实Kotlin的协程核心库中也为我们提供封装好了的scope,例如MainScope,源码如下: ...

  3. 字节跳动春招攻略:学长学姐笔经面经,还有出题人「锦囊」

    正在参加春季校招的同学们,此刻可能是最紧张的了: 简历投出去了,什么时候才能收到笔试通知啊? 收到了笔试通知,最后两天还来得及做什么准备? 面试会问什么问题?怎么才能提升通过率.拿到更好的offer? ...

  4. 爆肝整理!2022年字节跳动 java 后端开发岗面试题整理

    前言 最近有收到很多粉丝朋友私信,说自己在2021年的秋招并不理想,想在这段时间好好准备复习一下好在2022的金三银四里面跳槽,找一份自己喜欢的工作.之前一直没有时间为大家整理,这两天把今年字节跳动的 ...

  5. 阿里、京东、字节跳动春招,Java岗offer不好拿?一文带你搞定

    前言 以下内容均为二月面试真题整理,面试内容均来自阿里.京东.腾讯.字节跳动等一线大厂,由网友集合反馈整理! 如有雷同,请在评论区提醒,全部内容GitHub可查阅. 由于篇幅原因,内容会比较杂乱,程序 ...

  6. 2021字节跳动秋招整理牛客网---Java后端提前批面试

    一.字节提前批 java 后端一面 1.1 你说你用了 RPC 远程调用,讲讲原理是怎么样的 RPC(Remote Procedure Call)远程调用,通过某种手段,协议,约定调用非本地服务 RP ...

  7. 字节跳动春招前端三轮面经

    作者:瑟瑟花抖小前端 链接:https://www.nowcoder.com/discuss/167553 来源:牛客网 一面: 一面小哥哥很nice,问的问题也偏基础,全程谈笑风生 什么是堆,什么是 ...

  8. 字节跳动春招——雀魂启动

    小包最近迷上了一款叫做雀魂的麻将游戏,但是这个游戏规则太复杂,小包玩了几个月了还是输多赢少. 于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且去除了一些特殊和牌方式(例如七 ...

  9. 2021 字节跳动面试参考手册(第一期)!!!权威来袭~~~

    2021 字节跳动面试参考手册(第一期) 一.Java 基础面试题 1.String 能被继承吗?为什么? 不可以,因为 String 类有 final 修饰符,而 final 修饰的类是不能被继承的 ...

最新文章

  1. HDU1250(高精度加法)
  2. 又拍云,音视频CDN加速利器
  3. DB2 删除某用户下的所有表
  4. php转换编码去掉bom,UTF-8编码怎么去掉BOM头?
  5. 基于JAVA+SpringBoot+Vue+Mybatis+MYSQL的在线音乐网站
  6. linux 托盘两个输入法图标,linux mint12安装ibus之后,语言栏不跟随光标和系统托盘输入法图标不能显示问题解决...
  7. 异步任务结果显示策略
  8. 模拟便于直接存取的索引文件结构_07016.2.0使用Solr7对结构化csv文件建立全文索引...
  9. oracle 存储过程深入学习与应用
  10. IBM Watson启示录:AI不应该仅仅是炫技
  11. Win10笔记本不显示WiFi列表
  12. 模拟京东快递单号查询
  13. dom4j解析xml格式字符串获取标签属性和内容
  14. JAVA学习笔记JEECG BOOT介绍
  15. 数字图像处理|Matlab-基于颜色的车牌识别程序
  16. 某OA ajax.do 未授权漏洞任意文件上传getshell复现
  17. 小学学计算机应该学什么礼物,小学毕业礼物排行榜 运动学习两不耽误
  18. RKE vs. RKE2:对比两种 Kubernetes 发行版
  19. ES中的Query与Filter的区别
  20. 软件系统:兼容性问题汇总

热门文章

  1. 【无标题】MySQL优化
  2. 大数据关键技术、及计算模式介绍,以及大数据与云计算、物联网的关系
  3. Apollo 应用与源码分析:Monitor监控 - 基本概念与入口分析
  4. 上线“评论外显”功能,TME旗下波点音乐精耕音乐“自留地”
  5. NPDP在国内的含金量
  6. QQ机器人OPQBot
  7. 【Win10+deepin+凤凰OS 三系统安装教程(一)】 Win10下安装deepin双系统
  8. KEIL编译器【C语言编译选项优化等级说明】【支持C99(变量声明在执行语句之后)】【反汇编设置】【C语言联合汇编】【use microlib选项】
  9. 4N25光耦合器:简单的应用电路
  10. 利用机器学习识别验证码(从0到1)