美图面试(1 hour),整理出来了详细答案和解析
-
原生js获取dom元素
-
给dom元素添加 attribute
-
怎么删除dom元素
-
怎么用原生js监听click事件
-
移动端touch事件有哪些,有什么功能
-
怎么知道点击的横纵坐标
-
事件冒泡的概念,父子组件中事件触发的顺序,怎么阻止冒泡 两个比较常用的 方法
-
vue响应式
-
vue依赖收集在什么时候发生,每次都发生吗?父子组件都会更新吗
-
for循环1000次,组件会更新多少次
-
框架是怎么知道什么时候生成虚拟dom
-
this.$nextTinck()是干什么的?
-
vue的 take是干什么
-
js事件循环
-
js常见的异步操作,网络请求算吗?
-
这些任务的优先级
-
宏任务和微任务有哪些
-
React hook是什么?
-
React的Render函数是干什么的,JSX最后会return出来的那些标签是什么对象,是干嘛用的
-
虚拟dom
-
js常用的数组方法,有什么区别
-
数组遍历用那些方法,有什么区别
-
map当作动词用的时候把他翻译成什么意思
-
原型链的概念
-
什么是构造函数
-
怎么让一个函数都继承另一个函数
-
构造函数的原型指向什么
-
闭包的概念,什么样的代码会产生闭包
-
this的指向是在什么时候确定的?
-
递归的概念和定义,有什么应用场景
-
深拷贝原理
-
推荐用哪个遍历对象
-
快速排序的原理
-
正则表达式相关的 开头,结尾、.、怎么匹配.、匹配数字
-
进程和线程的概念,堆和栈的概念,一个进程里有几个堆几个栈,一个线程对应几个栈
-
js是单线程的,它是怎么实现异步操作的
-
rem 是什么,怎么算出来的
-
定位,相对定位偏离走了之后原本的空间会受到影响吗?absolute是根据哪个进行定位的
-
浮动会造成什么问题,怎么消除
-
在搜索栏输入地址后会有什么过程,解析html时有一个js文件浏览器会怎么处理,js文件是什么时候执行的
原生js获取dom元素
document.getElementById(): 通过元素的 ID 获取元素
document.getElementsByTagName(): 通过元素的标签名称获取元素
document.getElementsByClassName(): 通过元素的类名称获取元素
document.querySelector(): 通过 CSS 选择器获取元素
document.querySelectorAll(): 通过 CSS 选择器获取所有匹配的元素
给dom元素添加 attribute
可以使用 setAttribute() 方法给 DOM 元素添加属性:
var img = document.getElementById("myImg");
img.setAttribute("src", "image.jpg");
怎么删除dom元素
使用 removeChild() 或 remove() 方法删除 DOM 元素。
- removeChild():删除父元素中的子元素
var parent = document.getElementById("parent");
var child = document.getElementById("child");
parent.removeChild(child);
- remove():(只支持现代浏览器)
var element = document.getElementById("element");
element.remove();
怎么用原生js监听click事件
可以使用addEventListener
方法监听click事件,代码示例如下:
document.querySelector("element").addEventListener("click", function(){
// do something here
});
移动端touch事件有哪些,有什么功能
- touchstart:当手指接触屏幕时触发
- touchmove:当手指在屏幕上滑动时连续触发
- touchend:当手指离开屏幕时触发
- touchcancel:当系统取消touch事件时触发
怎么知道点击的横纵坐标
可以在监听的事件回调函数中使用event.clientX
和event.clientY
属性获取点击的横纵坐标。
element.addEventListener("click", function(event){
console.log("Click x: " + event.clientX);
console.log("Click y: " + event.clientY);
});
事件冒泡的概念,父子组件中事件触发的顺序?怎么阻止冒泡?
事件冒泡是指当一个元素上的事件被触发后,该事件会向上冒泡至父元素,一直冒泡到最外层的元素,如果在父元素上也有与该事件相关的事件监听器,则该监听器也会被触发。
在父子组件中,先发生在子组件上的事件,再发生在父组件上的事件。
阻止事件冒泡的方法是在事件回调函数中使用event.stopPropagation()
方法。
event.preventDefault()
也是常用的事件处理方法。
它的作用是阻止事件的默认行为,例如在表单元素上触发submit事件时,默认行为是提交表单,调用event.preventDefault()可以阻止表单提交。
vue响应式
Vue的响应式系统是Vue的核心功能之一,它使得Vue能够实时监测和响应数据的变化。
Vue响应式系统的工作原理是通过Object.defineProperty()
方法重新定义对象的属性,使得对象的每个属性都具有getter和setter方法,当数据改变时会触发setter方法,从而执行相关的更新操作。
在Vue中,可以使用data选项声明组件的响应式数据,然后在模板中使用{{}}插值表达式绑定数据,当数据改变时,Vue会自动更新相关的视图。
vue依赖收集在什么时候发生? 每次都发生吗? 父子组件都会更新吗
Vue的依赖收集是在数据劫持的过程中发生的。
具体来说,当数据被访问(即读取)时,如果当前存在一个正在计算的计算属性或渲染函数,则将该计算属性或渲染函数添加到该数据的依赖列表中,以便在该数据发生变化时,能够通知相关的计算属性或渲染函数进行更新。
依赖收集的发生并不是每次数据被访问时都会发生,而是在第一次访问时触发,将依赖关系建立起来。当数据变化时,会遍历依赖列表,通知相关的计算属性或渲染函数进行更新。
父子组件都会进行依赖收集和更新,但是更新的顺序和具体实现有关,不一定是所有的父组件都会在所有的子组件更新之前更新。通常情况下,父组件会在子组件之前更新,但也可以通过一些特殊的方式来改变更新的顺序。
for循环1000次,组件会更新多少次
在Vue中,一个组件在重新渲染时,它所依赖的响应式数据发生变化才会更新,否则不会更新。所以,如果在一个组件中进行了一个循环1000次的操作,但并没有对该组件所依赖的响应式数据进行修改,那么该组件不会进行任何更新。
如果该循环操作中对响应式数据进行了修改,那么组件会更新多少次取决于具体的修改操作。例如,如果在循环中进行了1000次数据修改操作,每次修改操作都会引起组件重新渲染,那么该组件会更新1000次。但是,如果这1000次操作只修改了一次响应式数据,那么组件只会更新一次。
需要注意的是,父子组件的更新也遵循以上原则,即只有当响应式数据发生变化时才会更新。当一个父组件更新时,它所有的子组件也会更新,但是如果子组件所依赖的响应式数据没有发生变化,那么子组件不会重新渲染。
框架是怎么知道什么时候生成虚拟dom
组件的数据发生变化时,会触发组件的重新渲染。在重新渲染组件时,Vue.js 会通过模板编译生成新的虚拟 DOM 树,并与旧的虚拟 DOM 树进行比较,以找出需要更新的节点。这个比较的过程称为 Virtual DOM 的 diff 算法。
在组件实例化时,Vue.js 会通过解析组件的模板生成初始的虚拟 DOM 树,然后将其渲染到页面上。之后,在组件的生命周期中,当组件的状态发生改变时,Vue.js 会重新生成虚拟 DOM 树,并与旧的虚拟 DOM 树进行比较,以确定需要更新的部分。生成新的虚拟 DOM 树的过程是由 Vue.js 的渲染函数(render function)完成的。渲染函数接收组件的状态作为参数,返回一个新的虚拟 DOM 树。每次组件状态变化时,Vue.js 会重新调用渲染函数生成新的虚拟 DOM 树。
this.$nextTinck()是干什么的?
它的作用是在下次 DOM 更新循环结束之后执行延迟回调函数,通常用于在视图更新后操作 DOM
它会在当前代码执行栈执行完毕后,DOM 更新循环队列开始之前执行,因此能保证回调函数中的 DOM 操作在当前的视图更新周期中得以执行
js事件循环
JavaScript 事件循环是一种执行模型,用于处理代码执行过程中的异步操作和回调函数。在 JavaScript 中,所有的代码都运行在单线程中,因此需要使用事件循环来处理异步操作。
事件循环的核心是一个无限循环,每一轮循环被称为一个“tick”。每个“tick”包含了以下步骤:
- 处理当前执行栈中的任务,直到执行栈为空。
- 从事件队列中取出最早加入队列的事件回调函数。
- 将该事件回调函数放入执行栈中,执行回调函数。
- 回到步骤1,处理执行栈中的任务,直到执行栈为空。
在事件循环中,可以将异步任务(如 setTimeout、Promise)放入事件队列中等待执行。当异步任务的条件满足时,将其对应的回调函数添加到事件队列中。在下一轮事件循环中,回调函数会被取出并执行。
js常见的异步操作有哪些,网络请求算吗?
-
回调函数:将异步操作作为函数的参数传递,并在操作完成时调用该函数。
-
Promise:使用 Promise 对象包装异步操作,可以链式调用 then() 和 catch() 方法处理操作成功或失败的情况。
-
async/await:使用 async 和 await 关键字,可以让异步代码看起来像同步代码,使其更易读、更易维护。
-
定时器:使用 setTimeout() 和 setInterval() 函数设置异步定时器。
-
事件监听器:通过事件监听器监听异步事件并处理事件触发时的操作。
-
网络请求:使用 XMLHttpRequest 或 fetch() 函数向服务器发送异步请求。
这些任务的优先级
???
宏任务和微任务有哪些
常见的宏任务有:
- setTimeout、setInterval 等计时器任务;
- DOM 事件(例如 click、load 等事件);
- I/O 任务(例如读取文件和写入文件);
- 请求动画帧(requestAnimationFrame)。
常见的微任务有:
- Promise 中的 then()、catch()、finally();
- MutationObserver 监听器;
- process.nextTick (Node.js 环境中)。
React hook是什么?
React Hook 是 React 16.8 引入的一项新特性,可以让你在无需编写类组件的情况下使用 state ,以及在函数组件中复用状态逻辑。通过 Hook,React 中的函数组件可以有状态、生命周期方法和其他特性。Hooks 可以让你从组件中提取状态和副作用的逻辑,使我们的代码更加简洁、易于理解和复用。
Hooks 有以下几种类型:
-
State Hook:useState(): 用于在函数组件中添加状态。
-
Effect Hook:useEffect(): 用于在函数组件中执行副作用操作(如网络请求、事件监听等)。
-
Context Hook:useContext(): 让你在函数组件中使用 Context。
-
Ref Hook:useRef(): 用于在函数组件中创建一个引用。
-
Callback Hook:useCallback(): 用于在函数组件中缓存一个回调函数。
-
Memo Hook:useMemo(): 让你可以在函数组件中缓存一个变量的值。
除了 React 内置的 Hook,第三方库也可以通过自定义 Hook 的方式来扩充这种能力。
React的Render函数是干什么的,JSX最后会return出来的那些标签是什么对象,是干嘛用的
在 React 中,render 函数是用来描述如何构建 UI 的。它通常返回一个虚拟 DOM,描述了如何将组件渲染到页面上。
JSX最终会被编译为React元素,它是一个描述UI的普通JavaScript对象。React元素包含一个type属性,表示元素的类型,可以是HTML标签名,也可以是React组件;以及一些props属性,表示元素的属性,包括事件处理函数、数据等。React元素是React构建UI的基础,它通过ReactDOM.render()方法渲染到DOM上,形成最终的UI界面。
什么是虚拟dom?
它是用 JavaScript 对象来描述真实的 DOM 元素树,通过对比新旧虚拟 DOM 的差异,最终只对需要更新的部分进行真实 DOM 的更新操作,从而提高渲染效率。
js常用的数组方法,有什么区别
- push():在数组的末尾添加一个或多个元素,返回新数组的长度。
- pop():删除数组末尾的元素,并返回该元素的值。
- shift():删除数组第一个元素,并返回该元素的值,数组的长度减 1。
- unshift():在数组的开头添加一个或多个元素,返回新数组的长度。
- concat():连接两个或更多的数组,并返回新的数组。
- slice():从数组中选取一个子集,返回一个新数组。
- splice():删除或替换数组中的元素,返回被删除元素的数组。
- sort():对数组进行排序。
- reverse():颠倒数组中元素的顺序。
- forEach():遍历数组中的每个元素,没有返回值。
- map():将数组中的每个元素应用于给定函数,返回一个新的数组。
- filter():将数组中的每个元素传递给给定函数,并返回一个新数组,其中包含所有计算结果为真的元素。
- reduce():将数组中的每个元素累加到一个累加器中,返回一个累加器的值。
修改原数组的方法:这些方法会直接修改原始数组,包括 push()、pop()、shift()、unshift()、splice()、sort()、reverse()。
不修改原数组的方法:这些方法会返回一个新的数组或其他数据类型,而不会修改原数组,包括 concat()、slice()、join()、toString()、indexOf()、lastIndexOf()、includes()、some()、every()、filter()、map()、reduce()、reduceRight()、find()、findIndex()、flat()等。
数组遍历用那些方法,有什么区别
-
forEach:遍历数组的每个元素,执行回调函数。不会创建新数组,也不会修改原数组。
-
map:遍历数组的每个元素,执行回调函数并返回处理后的结果,返回一个新的数组。
-
filter:遍历数组的每个元素,执行回调函数并根据返回值判断是否保留该元素,返回一个新的数组。
-
reduce:遍历数组的每个元素,执行回调函数,并返回一个累计值。可以用来计算数组元素的总和、平均值等,也可以用来将二维数组转换为一维数组。
-
some:遍历数组的每个元素,执行回调函数并根据返回值判断是否存在符合条件的元素,如果存在则返回 true,否则返回 false。
-
every:遍历数组的每个元素,执行回调函数并根据返回值判断是否所有元素都符合条件,如果都符合则返回 true,否则返回 false。
原型链的概念
原型链是 JavaScript 中的一个机制,用于实现对象之间的继承。每个对象都有一个内部属性 [[Prototype]],它指向其原型对象。如果在一个对象上调用一个方法或访问一个属性,而这个对象本身并没有这个方法或属性,JavaScript 引擎会沿着这个对象的原型链向上查找,直到找到该属性或方法为止,或者查找到原型链的顶端,即 Object.prototype。
什么是构造函数
构造函数是一种用于创建对象的特殊函数。它使用 new
运算符来创建一个对象,这个对象通常被称为该构造函数的实例。构造函数在对象初始化时可以接受参数,可以通过 this 关键字将属性和方法添加到实例对象上。
怎么让一个函数继承另一个函数
方式一:原型链继承
可以通过将子类的原型对象指向父类的实例对象来实现原型链继承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
const child = new Child('Tom', 10);
child.sayHello(); // 输出: "Hello, Tom"
方式二:class类继承
使用class进行继承,可以通过extends关键字来实现
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog('Rufus');
dog.speak(); // 输出 "Rufus barks."
构造函数的原型指向什么
构造函数的原型指向的是一个对象,这个对象是通过Object.prototype创建出来的,它包含了一些通用的方法和属性,比如toString()和valueOf()方法等。
每个通过构造函数创建出来的对象都可以访问到这个原型对象中的方法和属性。如果在对象中访问一个属性或方法,它会首先在对象本身中查找,如果没有找到,则会沿着原型链一直向上查找,直到找到该属性或方法或者到达原型链的顶端Object.prototype。
闭包的概念,什么样的代码会产生闭包
闭包是指一个函数可以访问并使用其词法作用域之外的变量的能力。在 JavaScript 中,闭包是由函数以及创建该函数的词法环境组合而成的。
具体来说,当一个函数访问了它外部定义的变量时,就创建了一个闭包。这个闭包包含了这个函数以及它所访问的变量。由于 JavaScript 中的函数都是对象,因此闭包也可以看作是一个对象,它拥有一个方法和一个引用到词法环境的链接。
以下是一个产生闭包的示例代码:
function outerFunction() {
const outerVariable = "I am outside!";
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closure = outerFunction();
closure(); // 输出 "I am outside!"
this的指向是在什么时候确定的?
JavaScript 中的 this 关键字在运行时绑定,即在函数执行时才能确定 this 的值,它的值取决于函数的调用方式。具体来说,this 的指向取决于函数的执行上下文,也就是函数被调用时所处的环境。
递归的概念和定义,有什么应用场景
递归是一种算法或函数调用机制,其中一个函数通过调用自身来解决问题。简而言之,递归是通过反复调用自身来解决问题的过程。
递归的应用场景有很多,包括但不限于:
- 解决数学问题,如计算斐波那契数列。
- 解决计算机科学中的问题,如遍历数据结构(如树和图)。
- 解决问题,如字符串匹配和搜索等。
- 实现某些算法,如分治法和回溯法等。
递归可以使代码更加简洁和易于理解,但需要注意避免死循环和栈溢出等问题。
深拷贝原理
深拷贝是指在内存中完全复制一个对象,新对象和原对象在内存中地址不同,修改新对象不会影响原对象。
深拷贝和浅拷贝的区别在于,浅拷贝只是复制了对象的引用,新对象和原对象在内存中地址相同,修改新对象会影响原对象。
实现深拷贝的方法有很多种,常见的有递归拷贝和JSON序列化。递归拷贝是指对于对象或数组,递归遍历每一个属性或元素,并将其拷贝到新的对象或数组中,最终返回一个新的对象或数组。JSON序列化则是将对象或数组转换为JSON字符串,再将其解析为新的对象或数组。需要注意的是,使用JSON序列化进行深拷贝时,只有对象属性值为原始类型数据或无循环引用的对象才能被成功拷贝。
推荐用哪个方法遍历对象
在 JavaScript 中,可以使用 for...in 循环和 Object.keys() 方法来遍历对象。这两种方法都可以遍历对象的属性,但是 for...in 循环会遍历对象的原型链上的属性,而 Object.keys() 方法只会遍历对象自身的属性。
如果需要遍历对象的所有属性,包括原型链上的属性,可以使用 for...in 循环;如果只需要遍历对象自身的属性,可以使用 Object.keys() 方法。
需要注意的是,for...in 循环的顺序是不确定的,而 Object.keys() 方法返回的属性名数组的顺序是按照属性插入的顺序排列的。
快速排序的原理
它采用了分治策略的思想,通过不断地分割数组并进行比较,最终将数组排列成有序序列。其基本原理如下:
-
选取一个基准元素,将待排序数组分为左右两个子数组;
-
将比基准元素小的数都放到左子数组中,比基准元素大的数都放到右子数组中;
-
对左右子数组分别进行递归排序。
具体实现时,一般选取数组的第一个元素作为基准元素,将其与数组中其他元素进行比较,并进行交换,使得左子数组的元素都比基准元素小,右子数组的元素都比基准元素大。然后对左右子数组分别进行递归排序,直到子数组的长度为1或0,排序完成。
快速排序的时间复杂度为 ,空间复杂度为 ,是一种常用的排序算法,常被用于程序实现中。
正则表达式相关的 开头,结尾、.、怎么匹配.、匹配数字
- ^ :表示匹配输入字符串的开头。
- $ :表示匹配输入字符串的结尾。
- . :表示匹配除了换行符以外的任意字符。
- . :用来匹配实际的 . 字符。
- \d :匹配数字字符,等价于 [0-9]。
进程和线程的概念,堆和栈的概念,一个进程里有几个堆几个栈,一个线程对应几个栈
进程是计算机中正在运行的一个程序,它具有一定的独立性,包括独立的内存空间、文件句柄、环境变量等,它可以由一个或多个线程组成。
线程是进程的基本执行单元,它是CPU调度的最小单位。一个进程可以包含多个线程,这些线程共享该进程的资源,如内存、文件句柄等。每个线程有自己的堆栈,用于存储局部变量和调用栈等信息。
堆和栈都是内存空间,堆用于存储动态分配的数据,由程序员手动控制分配和释放;栈用于存储局部变量、函数调用等信息,由系统自动分配和释放。
一个进程一般有一个堆和一个栈,每个线程也有一个栈。所以一个进程中的多个线程共享进程的堆,但每个线程有自己的栈,用于存储函数调用和局部变量等信息。
js是单线程的,它是怎么实现异步操作的
虽然 JavaScript 是单线程的,但是它可以通过异步操作来实现非阻塞的操作,这是通过事件循环机制实现的。
当代码执行到异步操作(如网络请求)时,它会被放入事件队列中,等待异步操作结束后被执行。在执行完当前的同步任务后,JavaScript 引擎会检查事件队列是否有待执行的任务,如果有则按照先进先出的顺序执行事件队列中的任务。
这种机制保证了 JavaScript 在执行异步操作时不会阻塞主线程,从而保证了页面的流畅性和用户体验。
rem 是什么,怎么算出来的
rem是一种相对于根元素(即标签)字体大小的单位。通常用于响应式设计中,能够根据屏幕大小自适应调整字体大小,实现网页的自适应。它的值可以根据根元素的字体大小相对调整,常用于设置网页元素的宽度、高度、内外边距等等。
计算rem值的公式为:元素的像素值 / 根元素的字体大小
相对定位偏离走了之后原本的空间会受到影响吗?absolute是根据哪个进行定位的
相对定位是相对于元素自身的初始位置进行定位,它对原本的空间不会产生影响。元素进行相对定位后,它所占据的空间大小和位置都没有改变,只是其在文档流中的位置发生了变化。因此,其它元素的位置和空间都不会受到影响。
absolute 定位是相对于最近的非 static 定位祖先元素进行定位的。如果不存在非 static 定位的祖先元素,则相对于最初的包含块(通常是 body 元素)进行定位。在绝大多数情况下,我们会通过设置父元素的 position 属性为 relative 或 absolute 来创建一个定位上下文。
浮动会造成什么问题,怎么消除
浮动(float)元素会脱离文档流,并且可以让其他元素环绕在其周围,这可能会引起一些问题,如下列举几个:
- 容器高度坍塌:当容器的子元素都浮动时,容器会变成空高度,导致无法撑开容器,这称为“高度坍塌”问题。
- 元素重叠:当多个浮动元素在同一个位置时,它们可能会重叠在一起,导致样式混乱。
- 布局错乱:浮动元素可能会让容器内的元素排列错乱,特别是在容器内元素高度不同时。
为了解决这些问题,可以使用以下方法消除浮动:
- 父元素使用 clear 属性:在浮动元素下方添加一个空元素,并使用 clear 属性清除浮动。
- 使用 BFC:使用 BFC(块级格式上下文)可以让容器形成一个独立的渲染区域,避免浮动元素影响到其他元素。
- 使用 Flex 布局:使用 Flex 布局可以避免浮动元素导致的布局问题。
在搜索栏输入地址后会有什么过程? 解析html时有一个js文件浏览器会怎么处理,js文件是什么时候执行的
在搜索栏输入地址后,会先进行域名解析,将域名解析成对应的 IP 地址。然后,浏览器会通过 TCP 协议与该 IP 地址建立连接,并向服务器发送 HTTP 请求。服务器收到请求后,会根据请求的内容返回相应的数据,通常是 HTML、CSS、JavaScript 等文件。浏览器接收到响应后,会解析 HTML 文件,并下载其中引用的其他资源,比如图片、字体、脚本等,然后将这些资源整合在一起,渲染成一个完整的页面。
当浏览器解析 HTML 时,如果遇到 script 标签,就会将该标签所引用的 JS 文件下载到本地,并在后台进行解析。解析完成后,如果 script 标签带有 defer 或 async 属性,浏览器就会按照相应的规则延迟或立即执行 JS 代码,否则会阻塞 HTML 的解析和渲染,直到 JS 代码执行完成。
其中,defer 属性表示该 JS 文件可以延迟执行,等到 HTML 解析完成后再执行,而 async 属性表示该 JS 文件可以异步加载和执行,不会阻塞 HTML 的解析和渲染,但 JS 执行完成前可能会出现页面渲染不完整的情况。
JavaScript文件是在HTML文档解析期间被下载和解析的,当HTML解析器遇到script标签时,它将停止对HTML的解析,将JavaScript文件下载并解析执行完毕后,再继续HTML的解析。如果是外部引入的JavaScript文件,浏览器会根据src属性指定的路径下载文件。如果是内嵌的JavaScript代码,则直接执行其中的代码。如果JavaScript代码位于标签中,则在HTML文档解析时下载和执行;如果位于标签中,则在DOM树构建完毕后下载和执行。
#你觉得今年春招回暖了吗##前端##软件开发2023笔面经##社招面试##春招#根据真实面试经历盘点面试题目,总结面试经验,分类总结面试题目答案