每天掌握10道面试题,轻轻松松去面试(Yes, that's right, I'm kidding)!!!
一、4.12
1、说一说cookie sessionStorage localStorage 是什么,有什么区别?
Cookie、sessionStorage 和 localStorage 都是在浏览器端存储数据的方式,它们的主要区别在于存储的数据范围、有效期和访问权限等方面。
-
Cookie
Cookie 是一种在浏览器和 Web 服务器之间传递的小数据片段,它通常用于存储用户的身份验证令牌、用户偏好设置、购物车信息等。Cookie 的最大缺点是它们的大小受限,一般只能存储少量数据(通常不超过 4KB),并且每次请求都会将 Cookie 发送到服务器,因此可能会影响性能。
Cookie 的优点是它们可以设置过期时间,从而控制数据的有效期,并且可以在浏览器和服务器之间共享。Cookie 还可以设置跨域属性,允许在多个域名之间共享数据。
-
sessionStorage
sessionStorage 是 HTML5 提供的一种会话存储机制,它可以在浏览器关闭后仍然保留数据。sessionStorage 中存储的数据只在同一浏览器窗口或标签页中有效,如果用户打开多个窗口或标签页,那么每个窗口或标签页都有自己的 sessionStorage。
sessionStorage 可以存储较大量的数据(通常在 5MB 左右),并且数据只在同一会话期间有效。sessionStorage 的主要用途是存储临时数据,例如表单数据、用户操作记录等。
-
localStorage
localStorage 也是 HTML5 提供的一种本地存储机制,它与 sessionStorage 的区别在于数据的有效期,localStorage 中存储的数据可以跨越多个浏览器窗口或标签页,并且在浏览器关闭后仍然保留。
localStorage 与 sessionStorage 一样,可以存储较大量的数据,通常在 5MB 左右。localStorage 的主要用途是存储用户偏好设置、主题样式等持久性数据。
总的来说,Cookie、sessionStorage 和 localStorage 都是在浏览器端存储数据的方式,它们的主要区别在于存储的数据范围、有效期和访问权限等方面。需要根据实际需求选择合适的存储方式。
2、说一说Vue2.0双向绑定的原理与缺陷?
1、Vue 2.0 的双向绑定原理是通过使用数据劫持和发布-订阅模式来实现的。
具体来说,当 Vue 将数据对象传递给组件实例化时,Vue 会遍历对象的每个属性,并使用 Object.defineProperty() 方法将其转换为 getter 和 setter。这样,当属性被读取或修改时,Vue 可以拦截并触发相应的更新。
在模板中,Vue 通过使用 v-model 指令来实现双向绑定。当用户在表单控件中输入数据时,Vue 会自动更新组件数据对象中对应的属性值。反之亦然,当组件数据对象中的属性值被修改时,Vue 会自动更新对应的表单控件。
尽管 Vue 2.0 的双向绑定机制在使用上非常方便,但它也存在一些缺陷。其中最大的问题是性能问题。由于 Vue 2.0 使用了数据劫持机制,对于大型数据集合或频繁的数据更新操作,这种机制可能会带来显著的性能损失。此外,Vue 2.0 的双向绑定机制也增加了代码的复杂性,使得调试和维护变得更加困难。
2、Vue2.0 的双向绑定是通过数据劫持和发布订阅模式实现的,具体的实现原理如下:
- 在 Vue 初始化时,会对数据对象进行递归遍历,把每一个属性转换成 getter/setter,并且在数据对象上定义一个观察者 Watcher 对象;
- 当数据发生变化时,setter 会被触发,setter 会通知所有观察者 Watcher 对象,观察者 Watcher 对象就会依次执行 update 方法,更新视图;
- 在模板编译时,如果遇到 v-model 指令,会在相应的元素上添加一个事件监听器,当输入框的值发生变化时,会触发这个事件监听器,通过调用 setter 方法更新数据。
Vue2.0 双向绑定的缺陷主要有以下几点:
- 性能问题:每个属性都要添加 getter 和 setter,会导致一定的性能损失,尤其是在处理大量数据时;
- 内存泄漏:由于观察者 Watcher 对象和数据对象之间的强引用,如果不及时销毁观察者 Watcher 对象,就会导致内存泄漏;
- 无法监听数组和对象的变化:由于 Vue2.0 只能通过数据劫持监听属性的变化,无法监听数组和对象的变化,需要通过额外的方法进行处理;
- 深层嵌套数据监听问题:由于 Vue2.0 只能监听到对象属性的变化,无法监听到深层嵌套数据的变化,需要通过递归或者使用第三方库进行处理。
3、v-if和v-show的区别
v-if和v-show是Vue.js中两个常用的指令,它们都可以用来控制DOM元素的显示和隐藏,但是它们的实现方式有所不同,具体如下:
-
v-if指令是对DOM元素进行条件渲染,只有在指定条件为真时才会渲染DOM元素,并在条件为假时销毁DOM元素。这意味着如果条件为假,DOM元素不会被渲染到页面上,也不会占据任何页面资源。因此,v-if适用于不经常改变的条件性显示内容。
-
v-show指令是基于CSS的display属性来控制DOM元素的显示和隐藏。当条件为真时,该元素会被显示出来,当条件为假时,该元素会被隐藏。这意味着如果条件为假,DOM元素仍然存在于页面上,只是被设置为不可见状态。因此,v-show适用于频繁切换的显示内容。
总的来说,如果需要频繁切换元素的显示和隐藏状态,应该使用v-show指令,因为它只是简单地切换CSS的display属性,可以避免频繁的DOM操作。而如果元素的显示和隐藏状态不太经常改变,或者需要在条件为假时完全清除DOM元素,应该使用v-if指令。
4、说一说提高前端性能优化手段
-
减少HTTP请求:减少页面中的HTTP请求可以大大提高页面的加载速度。可以通过合并CSS和JavaScript文件、使用CSS Sprites和图像压缩等技术来减少HTTP请求。
-
压缩文件:压缩CSS、JavaScript和HTML文件可以减少文件大小,提高页面加载速度。可以使用Gzip或Deflate等压缩算法来压缩文件。
-
使用缓存:使用浏览器缓存可以减少页面的加载时间。可以使用HTTP缓存策略来控制缓存,例如设置Expires头、Cache-Control头等。
-
延迟加载:将非关键资源(如图片、广告等)的加载延迟到页面其它内容加载完成后再进行,可以提高页面的加载速度。可以使用LazyLoad等插件来实现延迟加载。
-
最小化DOM操作:DOM操作是非常耗费性能的操作,应该尽量减少DOM操作的次数和复杂度。可以使用文档片段(DocumentFragment)来减少DOM操作次数,使用事件委托(Event Delegation)来减少DOM操作复杂度。
-
使用CDN:使用CDN可以加快资源的加载速度,提高页面的响应速度。可以使用公共CDN,如Google、百度、360等,也可以使用自己的CDN。
-
优化图片:图片是页面中占用带宽最大的资源,应该尽量减小图片的大小。可以使用图像压缩和格式转换等技术来优化图片。
-
使用异步加载:使用异步加载可以将页面的加载和执行分离,提高页面的响应速度。可以使用异步加载的JavaScript库、defer属性和async属性等技术来实现异步加载。
-
代码优化:优化JavaScript和CSS代码可以减少文件大小,提高页面加载速度。可以使用压缩工具、代码检查工具、代码精简工具等来优化代码。
综上所述,前端性能优化是一个综合性的问题,需要从多个方面进行优化。通过上述手段的应用,可以显著提高页面的加载速度和响应速度,从而提升用户的体验。
5、mvvm是什么
MVVM是一种前端架构模式,它是Model-View-ViewModel的缩写。MVVM模式将前端应用程序分为三个部分:模型(Model)、视图(View)和视图模型(ViewModel),并通过数据绑定技术将这三个部分连接在一起。
在MVVM模式中,模型表示应用程序的数据和业务逻辑,视图表示应用程序的用户界面,而视图模型则是连接模型和视图的桥梁,它负责处理视图的事件和数据,将模型数据绑定到视图上,并将用户输入反映到模型中。视图模型通常是由JavaScript编写的,它使用观察者模式来监听模型数据的变化,并通过数据绑定技术将变化传递到视图上,从而实现视图和模型之间的解耦。
MVVM模式的一个重要特点是数据双向绑定,即当模型中的数据发生变化时,会自动更新视图中的数据,而当用户在视图中输入数据时,会自动更新模型中的数据,从而实现了数据的同步更新,减少了手动操作的复杂性。
常见的MVVM框架包括AngularJS、Vue.js、KnockoutJS等。这些框架提供了丰富的工具和API,帮助开发者更轻松地实现MVVM模式,提高前端应用程序的开发效率和质量。
6、axios面试题
-
什么是Axios?
Axios是一个基于Promise的HTTP客户端,用于浏览器和Node.js平台。它可以发送异步请求并处理响应数据,支持拦截器、取消请求、自动转换JSON数据等功能。 -
Axios和其他HTTP客户端库的区别是什么?
Axios相比其他HTTP客户端库,具有更简单、易用的API和更好的性能。它还可以在浏览器和Node.js平台上运行,支持拦截器、自动转换JSON数据等功能。 -
如何在Axios中设置请求头和响应头?
可以使用config.headers属性来设置请求头,例如:
axios.get(url, { headers: { 'Authorization': 'Bearer ' + token } })
可以使用response.headers属性来获取响应头,例如:
axios.get(url) .then(response => { console.log(response.headers); });
- 如何实现Axios的请求超时?
可以使用config.timeout属性来设置请求超时,例如:
axios.get(url, { timeout: 5000 // 5秒超时 })
- 如何在Axios中实现请求拦截器和响应拦截器?
可以使用axios.interceptors.request.use()方法来实现请求拦截器,例如:
axios.interceptors.request.use(config => { // 在请求发送之前做些什么 return config; }, error => { // 对请求错误做些什么 return Promise.reject(error); });
可以使用axios.interceptors.response.use()方法来实现响应拦截器,例如:
axios.interceptors.response.use(response => { // 对响应数据做些什么 return response; }, error => { // 对响应错误做些什么 return Promise.reject(error); });
7、
8、
9、
10、
二、4.13
2.1、== 和 === 有什么区别
在大多数编程语言中,"==" 和 "===" 都是用于比较两个值是否相等的运算符,但它们之间有一些区别。
"==" 运算符执行弱类型比较,它会尝试将两个操作数转换为相同的类型,然后再进行比较。例如,在 JavaScript 中,"==" 运算符将数字字符串转换为数字,而将数字转换为数字字符串,然后将它们进行比较。这意味着如果两个值的类型不同,但它们的值相等,"==" 运算符也会返回 true。
"===" 运算符执行强类型比较,它会比较两个操作数的值和类型是否完全相同。例如,在 JavaScript 中,"===" 运算符将不会尝试将数字字符串转换为数字,而是直接比较它们的类型和值。这意味着如果两个值的类型不同,"===" 运算符会返回 false,即使它们的值相等。
因此,"===" 运算符比 "==" 运算符更严格,更准确地比较两个值。在编程中,应该根据需要选择使用哪种运算符,以确保比较得到正确的结果。
以下是一个使用 JavaScript 的例子,展示了 "==" 和 "===" 运算符的区别:
let num = 5; let strNum = "5"; console.log(num == strNum); // true,因为 "==" 运算符会将 strNum 转换为数字类型,然后比较它们的值 console.log(num === strNum); // false,因为 "===" 运算符会比较 num 和 strNum 的类型和值,类型不同,所以返回 false
2.2、什么是网络存储?有什么区别
网络存储指的是将数据存储在网络上的一种技术。这种技术可以让用户通过网络访问和共享存储在网络中的数据,而无需在本地计算机上存储数据。
网络存储有多种形式,包括云存储、网络硬盘、NAS(网络附加存储)等。这些网络存储形式都有一个共同的特点,就是它们可以通过网络连接访问和共享存储在其中的数据。
相比于传统的本地存储方式,网络存储有以下区别:
-
可以实现远程访问和共享:网络存储可以通过互联网实现数据的远程访问和共享,使得用户可以随时随地访问和共享存储在网络存储中的数据。
-
可以实现数据备份和容灾:网络存储可以将数据存储在多个地方,从而实现数据备份和容灾,保障数据的安全性和可靠性。
-
可以实现扩展性和灵活性:网络存储可以根据需要进行扩展和升级,从而满足数据存储的需求。
-
需要网络连接:网络存储需要网络连接才能访问和共享数据,因此对网络的稳定性和速度有一定的要求。
总的来说,网络存储可以提供更加便捷、安全、可靠和灵活的数据存储方式,但也需要注意网络的稳定性和速度等问题。
2.3、JavaScript的回调函数是什么
在 JavaScript 中,回调函数是指一个函数被作为参数传递给另一个函数,并在另一个函数执行完毕后被调用的函数。回调函数通常用于异步编程,例如处理定时器、事件处理函数、Ajax 请求等。
回调函数的使用可以避免阻塞代码的执行,因为它可以在另一个函数执行的同时进行其他的操作,并在需要时被调用。在 JavaScript 中,回调函数通常被定义为匿名函数或者命名函数。
以下是一个使用回调函数的例子,这个例子使用了 setTimeout 函数来模拟异步操作,并在异步操作完成后调用回调函数:
function doSomethingAsync(callback) { setTimeout(function() { console.log("异步操作完成!"); callback(); }, 1000); } function callback() { console.log("回调函数被调用了!"); } doSomethingAsync(callback);
在上面的例子中,doSomethingAsync 函数接受一个回调函数作为参数,并在异步操作完成后调用回调函数。在调用 doSomethingAsync 函数时,我们将 callback 函数作为参数传递给它。
当异步操作完成后,setTimeout 函数会调用匿名函数,并在其中调用回调函数。因此,在执行 doSomethingAsync 函数时,我们会看到 "异步操作完成!" 和 "回调函数被调用了!" 这两个日志消息。
2.4、为什么要使用promises
在 JavaScript 中,Promises 是一种用于异步编程的解决方案,它可以更好地处理异步操作,提高代码的可读性和可维护性。以下是一些使用 Promises 的好处:
-
避免回调地狱:在传统的回调函数中,如果需要执行多个异步操作,代码会出现多层嵌套的回调函数,这被称为回调地狱。使用 Promises 可以避免回调地狱,使代码更加简洁和易于理解。
-
更好的错误处理:在传统的回调函数中,错误处理比较麻烦,容易出现错误处理不到位的情况。而在 Promises 中,可以通过 catch 方法捕获错误,并进行统一的处理。
-
支持链式调用:Promises 可以支持链式调用,使得代码更加简洁和易于理解。例如,可以通过 then 方法在多个异步操作之间传递数据。
-
更好的可读性:使用 Promises 可以使代码更加易于理解和可读性更高,因为 Promises 中的代码按照顺序排列,而不是嵌套在多个回调函数中。
总之,Promises 可以帮助开发人员更好地处理异步操作,提高代码的可读性和可维护性。虽然 Promises 的学习曲线可能比较陡峭,但是一旦掌握了 Promises 的使用方法,就可以更加轻松地处理异步操作。
2.5、JavaScript中的回调函数是什么
在 JavaScript 中,回调函数(Callback Function)是一种特殊的函数,它作为参数传递给另一个函数,并在该函数执行完成后被调用。回调函数通常被用来处理异步操作,例如读取文件、网络请求或定时器等。
以下是一个简单的回调函数的示例:
function fetchData(callback) { // 模拟异步操作 setTimeout(function() { const data = { id: 1, name: 'John' }; callback(data); }, 1000); } function processData(data) { console.log(data); } fetchData(processData);
在上述代码中,fetchData
函数模拟了一个异步操作,并接受一个回调函数作为参数。当异步操作完成后,fetchData
函数会调用回调函数,并将数据作为参数传递给它。在这个例子中,我们将 processData
函数作为回调函数传递给 fetchData
函数,当异步操作完成后,processData
函数会被调用,并将数据打印到控制台中。
回调函数是 JavaScript 中一种非常常见的编程模式,在处理异步操作时非常有用。同时,回调函数也有一些缺点,例如嵌套回调(Callback Hell)和难以处理错误等问题。因此,在现代 JavaScript 中,我们通常使用 Promise、async/await 等更高级的技术来处理异步操作。
2.6、typescript和JavaScript有何不同
TypeScript 和 JavaScript 是两种不同的编程语言,尽管它们非常相似,但它们之间有一些重要的区别:
-
类型系统:TypeScript 是一种带有静态类型检查的编程语言,而 JavaScript 是一种动态类型语言。在 TypeScript 中,可以使用类型注解来指定变量、函数和参数的类型,并且编译器会在编译时检查类型。这可以帮助开发人员在编写代码时更早地发现错误,并提高代码的可维护性和可读性。
-
扩展性:TypeScript 是 JavaScript 的超集,它可以使用 JavaScript 的所有功能,还可以添加新的功能,例如类、接口、枚举等。这使得 TypeScript 可以更好地支持大型应用程序的开发,并提供更好的代码组织和重用。
-
编译:TypeScript 需要先进行编译,将 TypeScript 代码转换为 JavaScript 代码,然后才能在浏览器或 Node.js 环境中运行。这可以帮助开发人员在编写代码时捕获错误,并将代码转换为可在任何浏览器或 Node.js 环境中运行的标准 JavaScript 代码。
-
生态系统:尽管 TypeScript 是一种相对较新的编程语言,但它已经有了强大的生态系统,并且有许多优秀的开源库和工具可供使用。TypeScript 支持 JavaScript 的所有库和工具,并且还有大量的 TypeScript 特定库和工具可供选择。
总之,TypeScript 和 JavaScript 之间的主要区别在于类型系统、扩展性、编译和生态系统。尽管在使用上有所不同,但它们都是非常有用的编程语言,可以用于开发各种类型的应用程序。
2.7、如何在TypeScript中定义和导出模块
在 TypeScript 中,可以使用关键字 export
来定义和导出模块。以下是一些示例:
- 导出单个变量或函数
// 定义 export const myVariable = 123; export function myFunction() { console.log("Hello, world!"); } // 导入 import { myVariable, myFunction } from "./myModule";
- 导出一个对象
// 定义 export const myObject = { name: "John", age: 30, }; // 导入 import { myObject } from "./myModule";
- 导出一个类
// 定义 export class MyClass { greet() { console.log("Hello, world!"); } } // 导入 import { MyClass } from "./myModule";
- 导出一个默认项
// 定义 export default function() { console.log("Hello, world!"); } // 导入 import myFunction from "./myModule";
在上述示例中,我们使用 export
关键字来定义和导出模块,然后使用 import
关键字来导入模块。需要注意的是,当我们导出一个默认项时,可以使用 import
关键字来导入模块,而不需要使用大括号。
在 TypeScript 中,模块可以是单独的文件,也可以是命名空间。对于单独的文件,每个文件都是一个模块。对于命名空间,可以使用 namespace
关键字来定义,例如:
// 定义 namespace MyNamespace { export const myVariable = 123; } // 导入 import { MyNamespace } from "./myModule";
总之,使用 export
和 import
关键字可以帮助我们在 TypeScript 中定义和导出模块,使代码更加模块化和可读性更高。
2.8、说一说vue-router实现懒加载的方法
Vue Router 支持使用懒加载(Lazy Loading)来延迟加载路由组件,从而提高应用程序的性能。懒加载可以使得只有在用户访问到某个路由时才会加载相应的组件,而不是在应用程序启动时就加载所有的路由组件。
以下是实现懒加载的两种方法:
- 使用动态导入
Vue Router 支持使用动态导入来实现懒加载。在路由配置中,可以将组件配置为一个返回 import()
函数的函数,这个函数会在路由被访问时调用,并动态加载组件。例如:
const Foo = () => import('./Foo.vue') const Bar = () => import('./Bar.vue') const router = new VueRouter({ routes: [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] })
在上述代码中,Foo
和 Bar
组件都是使用 import()
函数来动态加载的。
- 使用异步组件
Vue Router 还支持使用异步组件来实现懒加载。异步组件是指组件的定义是一个返回 Promise 的函数,这个函数会在组件被渲染时调用,并动态加载组件。例如:
const Foo = () => ({ component: import('./Foo.vue'), loading: LoadingComponent, error: ErrorComponent, delay: 200, timeout: 10000 }) const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] })
在上述代码中,Foo
组件被定义为一个返回 Promise 的函数,同时还提供了 loading
、error
、delay
和 timeout
等选项,用于指定加载组件时的 loading 状态、错误处理、延迟和超时等。这些选项都是可选的,可以根据具体需要进行配置。
总之,无论是使用动态导入还是异步组件,Vue Router 都提供了很好的支持,可以帮助我们实现懒加载,提高应用程序的性能。
2.9、说一说get请求和post请求的区别
在 HTTP 协议中,GET 和 POST 是两种最常见的请求方法。它们之间的主要区别如下:
-
GET 请求会将请求参数追加到 URL 中,而 POST 请求会将请求参数放在请求体中。因此,在 URL 中可以看到 GET 请求的请求参数,而 POST 请求的请求参数则不会出现在 URL 中。
-
GET 请求提交的数据量有限,一般不超过 2KB。而 POST 请求提交的数据量可以很大,一般不受限制。因此,在传输大量数据时,应该使用 POST 请求。
-
GET 请求是幂等的,也就是说,多次请求同一个 URL,得到的结果是相同的。而 POST 请求是非幂等的,不同的请求可能会返回不同的结果。
-
GET 请求不应用于修改数据的操作,因为它会将数据暴露在 URL 中,容易被攻击者窃取。而 POST 请求可以用于修改数据,因为它将数据放在请求体中,相对安全。
-
GET 请求可以被缓存,而 POST 请求不能。因为 GET 请求的参数是包含在 URL 中的,可以被浏览器缓存。而 POST 请求的参数是包含在请求体中的,不能被缓存。
总之,GET 和 POST 请求各有优缺点,应根据具体的需求来选择合适的请求方法。通常情况下,GET 请求用于查询数据,而 POST 请求用于修改数据。
2.10、 Computed和Watch的区别
对于Computed:
●它支持缓存,只有依赖的数据发生了变化,才会重新计算
●不支持异步,当Computed中有异步操作时,无法监听数据的变化
computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data声明过,或者
父组件传递过来的props中的数据进行计算的。
●如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,-般会使用computed
如果computed属性的属性值是函数,那么默认使用get方法,函数的返回值就是属性的属性值;在computed
中,属性有一个get方法和一一个set方法,当数据发生变化时,会调用set方法。
对于Watch:
●它不支持缓存,数据变化时,它就会触发相应的操作
●支持异步监听
监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值
●当一个属性发生变化时,就需要执行相应的操作
. 监听数据必须是data中声明的或者父组件传递过来的props中的数据, 当发生变化时,会触发其他操作,函数
有两个的参数:
。immediate: 组件加载立即触发回调函数
。deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注
意的是,deep无法监听到数组和对象内部的变化。
当想要执行异步或者昂贵的操作以响应不断的变化时,就需要使用watch。