万得前端一面面经
A面
有哪些设计模式,如何使用
设计模式是解决特定问题的通用解决方案,可以提高代码的可维护性、可重用性和可扩展性。常见的设计模式包括:
-
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。
-
工厂模式(Factory Pattern):定义一个创建对象的接口,但是由子类决定实例化的类是哪一个。
-
观察者模式(Observer Pattern):定义对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。
-
装饰者模式(Decorator Pattern):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰者模式比生成子类更灵活。
-
策略模式(Strategy Pattern):定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。
-
适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
-
模板方法模式(Template Method Pattern):定义一个操作中的算法的骨架,将一些步骤延迟到子类中实现。
-
代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问。
-
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象。
等等。
要使用设计模式,首先要了解各种设计模式的特点、适用场景和实现方式。然后根据实际需求选择合适的设计模式进行实现。在编码过程中,遵循设计模式可以使代码更加清晰、灵活、可维护,并且有助于降低代码的耦合性。
隐藏元素的方式
要隐藏元素,可以使用 CSS 中的 display 属性或 visibility 属性,或者使用 JavaScript 来动态操作元素的样式或属性。
使用 CSS 隐藏元素:
-
display: none;:完全隐藏元素,并且不占据页面空间。
.hidden-element { display: none; } -
visibility: hidden;:元素隐藏,但仍占据页面空间。
.hidden-element { visibility: hidden; } -
opacity: 0;:元素透明度设置为 0,元素仍然占据页面空间。
.hidden-element { opacity: 0; } -
position: absolute;:将元素绝对定位到页面之外,元素仍然存在但看不见。
.hidden-element { position: absolute; left: -9999px; }
使用 JavaScript 隐藏元素:
-
修改 style 属性:
document.getElementById("elementId").style.display = "none"; -
添加/移除类:
document.getElementById("elementId").classList.add("hidden"); document.getElementById("elementId").classList.remove("hidden"); -
设置元素属性:
document.getElementById("elementId").setAttribute("hidden", true); document.getElementById("elementId").removeAttribute("hidden");
这些方法可以根据具体的需求选择合适的隐藏方式。
css实现居中
要在 CSS 中实现元素居中,可以使用以下方法之一:
水平居中
-
使用 margin 属性:适用于块级元素和行内块级元素。
.center-horizontal { margin: 0 auto; /* 左右外边距设为 auto */ display: block; /* 让元素变为块级元素 */ } -
使用 Flexbox:适用于容器内的子元素。
.container { display: flex; justify-content: center; /* 水平居中 */ }
垂直居中
-
使用 line-height 属性:适用于单行内联元素。
.center-vertical { line-height: 100px; /* 与父容器高度相同 */ } -
使用 Flexbox:适用于容器内的子元素。
.container { display: flex; align-items: center; /* 垂直居中 */ } -
使用 Grid 布局:适用于容器内的子元素。
.container { display: grid; place-items: center; /* 将子元素水平垂直居中 */ }
水平垂直居中
-
使用 Flexbox:适用于容器内的子元素。
.container { display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ } -
使用 Grid 布局:适用于容器内的子元素。
.container { display: grid; place-items: center; /* 将子元素水平垂直居中 */ }
根据实际情况和需求,选择合适的方法来实现元素的居中。
websocket和http有什么区别
WebSocket 和 HTTP 是两种不同的网络通信协议,它们有以下区别:
-
连接方式:
- HTTP:采用请求-响应模式,客户端发送请求,服务器端返回响应后即断开连接。
- WebSocket:建立持久连接,客户端和服务器端可以双向通信,双方可以随时发送数据。
-
通信效率:
- HTTP:每次通信都需要建立连接、发送请求、等待响应,通信效率较低。
- WebSocket:建立一次连接后可以持续通信,数据传输效率高,适用于实时通信场景。
-
数据格式:
- HTTP:通常使用 JSON、XML 等格式传输数据。
- WebSocket:可以自定义数据格式,通常使用二进制格式传输数据。
-
使用场景:
- HTTP:适用于客户端发起的请求-响应场景,如网页浏览、数据请求等。
- WebSocket:适用于实时通信场景,如在线聊天、实时游戏、实时监控等。
-
协议类型:
- HTTP:属于应用层协议。
- WebSocket:属于应用层协议,基于 TCP 协议实现。
总的来说,HTTP 协议适用于请求-响应式的场景,通信效率较低;而 WebSocket 协议适用于实时通信场景,可以建立持久连接,通信效率较高。
谈项目难点
B面
vue组件间通信
Vue 组件间通信有多种方式,常见的包括:
- Props / Events:父子组件之间通信的常用方式。通过 Props 将数据从父组件传递给子组件,通过 Events($emit)在子组件中触发事件来通知父组件。
<!-- Parent.vue -->
<template>
<Child :message="message" @update:message="updateMessage" />
</template>
<script>
export default {
data() {
return {
message: 'Hello from parent'
};
},
methods: {
updateMessage(newMessage) {
this.message = newMessage;
}
}
};
</script>
<!-- Child.vue -->
<template>
<div>
<p>{{ message }}</p>
<button @click="sendMessage">Send Message</button>
</div>
</template>
<script>
export default {
props: ['message'],
methods: {
sendMessage() {
this.$emit('update:message', 'New message from child');
}
}
};
</script>
-
Vuex:用于管理应用中的状态。Vuex 可以在任何组件中访问共享状态,因此可以在不同组件之间实现通信。
-
Event Bus:通过创建一个全局的事件总线实例来实现组件之间的通信。可以使用 Vue 实例作为事件总线,或者创建一个专门的 Vue 实例来作为事件总线。
// EventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
// ComponentA.vue
import { EventBus } from './EventBus';
export default {
methods: {
sendMessage() {
EventBus.$emit('message', 'Hello from Component A');
}
}
};
// ComponentB.vue
import { EventBus } from './EventBus';
export default {
created() {
EventBus.$on('message', message => {
console.log(message); // "Hello from Component A"
});
}
};
- Provide / Inject:用于祖先组件向后代组件注入数据。祖先组件通过 provide 提供数据,后代组件通过 inject 注入数据。
<!-- Parent.vue -->
<template>
<div>
<p>Parent Component</p>
<Child />
</div>
</template>
<script>
import { provide } from 'vue';
import Child from './Child.vue';
export default {
components: {
Child
},
setup() {
provide('message', 'Hello from parent');
}
};
</script>
<!-- Child.vue -->
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const message = inject('message');
return { message };
}
};
</script>
这些是常用的 Vue 组件间通信方式,选择合适的方式取决于具体的场景和需求。
vue3和react相比有哪些异同点
Vue 3 和 React 是两个流行的前端框架,它们在某些方面有相似之处,但也存在一些显著的异同点:
-
语言和生态:
- Vue 3 使用了 TypeScript,提供了更好的类型检查和开发体验,但也可以选择使用 JavaScript 进行开发。
- React 主要使用 JavaScript,但也可以与 TypeScript 结合使用。
- Vue 3 提供了一整套生态系统,包括 Vue Router、Vuex 等,而 React 需要借助其他库来实现类似的功能,如 React Router、Redux 等。
-
组件化:
- Vue 3 和 React 都采用了组件化的开发方式,将界面拆分为独立的组件进行开发和复用。
- Vue 3 的组件化更加直观和简单,使用了单文件组件(SFC)的方式,将 HTML、CSS、JavaScript 集中在一个文件中,易于维护和阅读。
- React 的组件化更加灵活,通过 JavaScript 的函数或类来定义组件,支持更多的编程范式,如函数式组件和类组件。
-
响应式原理:
- Vue 3 使用了 Proxy 和 ES6 的响应式 API 来实现数据的响应式,通过监听对象的变化来更新视图。
- React 使用了虚拟 DOM 和 setState 来实现数据的响应式,当数据发生变化时,重新渲染组件并更新视图。
-
状态管理:
- Vue 3 推荐使用 Vuex 进行状态管理,提供了一种集中式存储管理应用中所有组件的状态的方式。
- React 推荐使用 Context API、Redux 等库进行状态管理,可以根据项目需求选择合适的状态管理工具。
-
模板语法:
- Vue 3 使用了模板语法来编写组件模板,通过指令(Directives)来操作 DOM。
- React 使用了 JSX 语法,将 HTML 结构和 JavaScript 代码混合在一起,通过组件的 render 方法返回虚拟 DOM。
-
渲染性能:
- Vue 3 在渲染性能上有一定优势,由于使用了更加精细的依赖追踪和更新策略,可以减少不必要的重新渲染。
- React 通过虚拟 DOM 的 diff 算法和批量更新机制来优化渲染性能,但相比 Vue 3 在某些情况下可能会有些许性能损失。
总的来说,Vue 3 和 React 都是优秀的前端框架,选择哪个取决于个人偏好、项目需求以及团队的技术栈和经验。
es6有哪些新特性
ES6(ECMAScript 2015)引入了许多新特性,以改进 JavaScript 的语法和功能。以下是一些 ES6 的主要新特性:
-
let 和 const 声明:引入了块级作用域的 let 和 const 关键字,用于声明变量和常量,取代了传统的 var 关键字。
-
箭头函数:提供了一种更简洁的函数声明语法,使用箭头 (=>) 定义函数,可以减少代码量和改变 this 的指向。
-
模板字符串:使用反引号(`)定义字符串模板,可以在其中插入变量或表达式,简化字符串拼接和格式化操作。
-
解构赋值:可以从数组或对象中提取值并赋给变量,使得代码更简洁易读。
-
默认参数:允许在函数声明中为参数设置默认值,简化了函数调用时的代码。
-
展开运算符:使用三个点(...)可以将数组、对象或字符串展开为单独的元素,方便进行合并、拷贝或解构操作。
-
对象字面量增强:提供了更方便的对象声明语法,支持简写属性和方法、计算属性名等。
-
class 和继承:引入了类(class)和 extends 关键字,提供了更接近传统面向对象语言的类和继承机制。
-
模块化:定义了模块化的导入和导出语法,可以更轻松地将代码分割为多个文件进行管理和复用。
-
迭代器和生成器:引入了可迭代对象和迭代器的概念,以及生成器函数的语法,用于简化数据集合的遍历和处理。
-
Promise 对象:提供了一种更优雅的异步编程方式,用于处理异步操作和解决回调地狱问题。
-
Symbol 数据类型:引入了一种新的原始数据类型 Symbol,用于创建唯一的标识符,可以防止属性名冲突。
-
Proxy 和 Reflect 对象:提供了更强大的元编程能力,可以拦截并自定义对象的操作行为。
-
Map 和 Set 数据结构:引入了新的数据结构 Map 和 Set,分别用于存储键值对和唯一值的集合,提供了更灵活的数据操作方式。
-
TypedArray 和 ArrayBuffer:引入了一系列新的数据类型和对象,用于处理二进制数据和内存的直接操作。
这些新特性使得 JavaScript 更加强大、灵活和易于维护,提升了开发效率和代码质量。
vue生命周期,关于create阶段具体做了什么
在 Vue 的生命周期中,created 钩子函数是在 Vue 实例被创建后调用的,此时实例已经完成了数据观测(data observer)和事件配置(event watchers),但是尚未挂载到 DOM 上。在 created 钩子中,可以进行一些初始化的工作,例如:
-
数据初始化:可以在这个阶段对数据进行初始化,包括从服务器获取数据、设置默认值等。
-
事件监听:可以在这个阶段进行事件的监听,例如监听用户的交互事件、定时器等。
-
访问数据和方法:可以在这个阶段访问到实例中的数据和方法,但是注意此时还未挂载到 DOM 上,因此无法操作 DOM。
-
异步请求:可以在这个阶段发起异步请求,例如通过 Ajax 获取远程数据。
-
插件初始化:可以在这个阶段初始化一些 Vue 插件,例如路由器、状态管理器等。
总的来说,created 钩子函数适合进行一些实例的初始化操作,但是需要注意的是,在这个阶段无法确保子组件已经被创建和挂载,因此如果需要与子组件进行交互,可能需要在后续的生命周期阶段进行处理。
谈项目难点
对于闭包的理解
闭包(Closure)是指在函数内部创建另一个函数时,内部函数可以访问外部函数的变量,即使外部函数已经执行完毕并且不在内存中存在,这种机制称为闭包。
具体来说,闭包在 JavaScript 中的实现是通过函数作用域和词法作用域的特性实现的。当内部函数引用了外部函数的变量时,JavaScript 引擎会将外部函数的执行环境保存在内部函数的[[Scope]]属性中,形成了一个闭包。这个[[Scope]]链包含了内部函数创建时的所有父级作用域,使得内部函数可以访问外部函数的变量和参数,即使外部函数执行完毕后,这些变量依然可以被内部函数引用。
闭包的特性使得 JavaScript 可以实现许多高级的编程模式和技术,例如模块化、封装、私有变量等。同时,闭包也要谨慎使用,因为它会导致内存泄漏问题,如果闭包引用了外部函数的大量变量并且没有释放,可能会导致内存占用过高。
总的来说,闭包是 JavaScript 中一个重要且强大的特性,能够帮助我们编写更加灵活和强大的代码,但也需要谨慎使用以避免潜在的问题。
讲讲输出结果(大致是这样):
for(var i = 0; i < 5; i++){
setTimeout(()=>{
console.log(i);
},0);
}
想让它从0到4输出,该怎么改(没搞懂问的什么,答的是每个循环里进行一个console.log,听到面试官无奈的笑了,后来发现原来想问的是作用域的问题,把var改成let就好啦)
在原始代码中,使用了 var 关键字声明变量 i,这会导致 i 变量具有函数作用域而不是块级作用域。因此,在 setTimeout 中访问 i 时,实际上是访问的是同一个变量 i,而此时 i 的值已经是循环结束后的值,因此会输出 5 个 5。
为了让代码输出从 0 到 4,可以将 var 关键字改为 let,这样 i 将具有块级作用域,每次循环会创建一个新的 i 变量,从而使得每个 setTimeout 函数中访问的 i 都是独立的,对应着循环当前的值。修改后的代码如下:
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
这样修改后,代码将按预期输出从 0 到 4 的数字。
作者:KeyError44
链接:www.nowcoder.com/feed/main/d…
来源:牛客网