美团春招面试
- js数据类型有哪些?基本数据类型和引用数据类型的区别?
- js作用域有哪些?
- 深拷贝和浅拷贝的区别?如何对数组进行深拷贝?
- 什么数据存在对象中,什么数据存在prototype中
- 如何判断A是不是B的实例
- this的指向?如何改变this的指向?
- 什么是原型链?原型链的终点?Function.prototype指向哪?
- 异步编程的方法
- 说一下promise
- promise如果后面有多个.then怎么传值
- async await怎么捕获异常?
- async await的底层原理?generator的原理?
- http状态码
- 协商缓存的过程?协商缓存具体存在哪里?
- 浏览器本地存储的方式?cookie,localStorage,sessionStorage区别?它们会把数据存在哪?受不受同源策略制约?
- cookie常用的属性有哪些?
- vue2和vue3的区别?
- Object.defineProperty如何监听数组?为什么无法获取数组的变化?
- vue为什么要用data包裹属性
- vue生命周期?组件间生命周期的顺序?
- 如何实现界面的切换?如何定义路由?
- vuex是做什么的?vue组件间通信的方式有哪些?React组件间通信的方式有哪些?
- git常用命令?回滚怎么写?
- webpack是干什么的?
- webpack能处理什么类型的文件?不能处理什么类型的文件?
- 算法题:移动零
js数据类型有哪些?基本数据类型和引用数据类型的区别?
JS数据类型可以分为基本数据类型和引用数据类型。
其中基本数据类型包括:字符串(string)、数字(number)、布尔值(boolean)、空(null)、未定义(undefined),symbol(符号) 而引用数据类型则包括:对象(Object)
区别在于存储方式和赋值方式。基本数据类型的值直接存储在变量内,存在栈中,而引用数据类型的值存储的是该对象的内存地址。存在堆中
js作用域有哪些?
全局作用域:指的是定义在代码块外部、函数外部或者是模块外部的变量、函数等,它们拥有全局作用域。
局部作用域:指的是定义在代码块、函数或者是模块内部的变量、函数等,它们拥有局部作用域。
在ES6规范下,还引入了块级作用域的概念。块级作用域可以用花括号包裹一段代码,在这段代码内部定义的变量仅在此代码块内部有效,超出此范围后便会失效,不会影响其他代码块中的同名变量。
深拷贝和浅拷贝的区别?如何对数组进行深拷贝?
浅拷贝:将对象的引用复制给一个新对象,新对象和原对象引用的是同一个对象,修改一个对象的属性会影响另一个对象的属性。常见的浅拷贝方法有Object.assign()、扩展运算符(...)等。
深拷贝:将对象完全复制一份,新对象和原对象是两个独立的对象,修改一个对象的属性不会影响另一个对象的属性。常见的深拷贝方法有递归拷贝、JSON.parse(JSON.stringify())等。
以下是使用递归拷贝实现深拷贝的示例代码:
function deepCopy(source) { if (typeof source !== 'object' || source === null) { return source; } const target = Array.isArray(source) ? [] : {}; for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = deepCopy(source[key]); } } return target; }
什么数据存在对象中,什么数据存在prototype中
只要是对象的独有属性和方法,就应该定义在对象本身上
而对于一些多个对象需要共享的属性和方法,可以将它们定义在对象的原型(prototype)对象上。这样,多个对象就可以共享这些属性和方法,无需在每个对象上都定义一遍
一般来说,满足以下两点之一的属性或方法,应该定义在对象的原型对象上:
- 多个对象需要共享它;
- 它需要被继承。
如何判断A是不是B的实例
可以使用instanceof运算符来判断一个对象是否为某个构造函数的实例obj instanceof Object
this的指向?
this是一个特殊的关键字,它指向函数当前被调用时的上下文对象。this的具体指向取决于函数的调用方式。
- 作为对象方法调用时,this指向该对象;
- 作为普通函数调用时,this指向全局对象(在浏览器中通常是window对象,在Node.js中是global对象);
- 作为构造函数调用时,this指向新创建的对象;
- 使用call或apply方法调用时,this指向指定的对象;
- 箭头函数的this指向当前代码所在的上下文。
如何改变this的指向?
- call方法:用于调用一个函数,并且指定函数体内this对象指向;使用call方法可以立即调用函数,同时指定函数内部的this指向特定对象。
function sayHello() { console.log(`Hello, my name is ${this.name}.`); } const obj = { name: 'jack' }; sayHello.call(obj); // Hello, my name is jack.
- apply方法:与call方法类似,用于调用一个函数,并且指定函数体内this对象指向。apply方法与call方法的区别只是传递参数的方式不同
- bind方法:用于创建一个新函数,并且指定新函数体内this对象指向。在调用bind方法后,它会返回一个新函数,再次调用新函数才会执行
function sayHello() { console.log(`Hello, my name is ${this.name}.`); } const obj = { name: 'jack' }; const newFunc = sayHello.bind(obj); newFunc(); // Hello, my name is jack.
什么是原型和原型链?原型链的终点?Function.prototype指向哪?
原型:每个函数都有一个 prototype(原型) 属性,这个属性是一个指针,指向一个对象,而这个对象包含某种特定类型的所有实例共享的属性和方法。
JavaScript 中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链
原型链是由原型对象组成的层次结构,它描述了JavaScript对象之间的继承关系。每个JavaScript对象的原型都指向它的继承原型,形成了一条链,即原型链。
原型链的终点是Object.prototype
。所有对象的原型链都是从Object.prototype
继承而来。如果在查找属性或方法时,最终到达了Object.prototype
,还是没有找到,则返回undefined。
Function.prototype
指向函数的原型对象。
什么是异步编程?异步编程的方法
异步编程是一种处理非阻塞I/O操作的技术,允许程序在某些操作执行的同时,执行其他操作,而不是等待这些操作执行完毕后才进行其他操作。
JavaScript中的异步编程可以通过回调函数、Promise、async/await、Generator函数等方式实现。
- 回调函数: 在函数调用的过程中传递一个函数作为参数,函数执行完毕后调用这个函数。回调函数虽然容易实现,但是嵌套过多会导致代码难以维护,产生回调地狱(Callback Hell)的问题。
- Promise: Promise是ES6中引入的一种异步编程方案。它是对回调函数的一种优化,可以解决回调地狱的问题。它的主要特点是提供了链式调用API,可以使用then()和catch()等方法让代码更加清晰和易于维护。
- async/await:async/await是一种基于Promise的语法糖,可以更好地解决回调地狱问题。async表示函数是异步的,await表示在等待异步操作完成后再继续执行。async/await提供了一种更加直观、流畅、易于理解的处理异步操作的方式。
- Generator函数+Promise:通过Generator函数的组合使用可以实现一种类似于async/await的效果。Generator函数通过yield关键字来暂停执行,然后通过next()方法继续执行。在Generator函数中,可以使用Promise来处理异步操作。
- 发布/订阅模式: 也称为观察者模式,通过定义一种一对多的依赖关系,当某个对象改变状态时,所有依赖它的对象都会得到通知并自动更新。
说一下promise
Promise 是异步编程的一种解决方案
承诺过一段时间会给你一个结果。promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
Promise是一种非常实用的异步编程技术,可以帮助我们更加优雅和高效地编写异步代码,避免多层回调函数嵌套的问题。
promise如果后面有多个.then怎么传值
Promise实例可以通过.then()方法链式调用多个回调函数,每个回调函数可以处理前一个回调函数的返回值,即通过return语句将值传递给下一个回调函数。
promise.then(function(value1) { // 处理 value1,并返回 value2 return value2; }).then(function(value2) { // 处理 value2,并返回 value3 return value3; }).then(function(value3) { // 处理 value3 });
Promise中的值是通过Promise的链式调用不断传递的,每个.then()方法的返回值可以作为下一个.then()方法执行的参数,从而实现值的传递和处理。
async await怎么捕获异常?
可以使用 try/catch
语句来捕捉异步操作中的异常
async function fetchData() { try { const response = await fetch('/api/data'); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } }
async await的底层原理?generator的原理?
async/await的底层原理是基于ES6中引入的Generator函数和Promise对象实现的。
Generator函数可以通过function*关键字定义,它返回一个可迭代对象,可以通过.next()方法一步一步执行函数中的代码,每次执行到yield语句时,函数暂停并返回一个yield表达式的值。可以通过.next()方法恢复函数执行并传递参数。例如:
http状态码
1xx:信息性状态码
- 100:Continue(继续):客户端应继续其请求
- 101:Switching Protocols(切换协议):服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求
2xx:成功状态码
- 200:OK(成功):请求已经成功处理
- 201:Created(已创建):请求已经被实现,并且创建了新的资源
- 202:Accepted(已接受):服务器已经接受了请求,但是尚未处理完成
- 204:No Content(没有内容):请求已经成功处理,但是没有返回消息体
4xx:客户端错误状态码
- 400:Bad Request(错误请求):客户端发送的请求无效
- 401:Unauthorized(未授权):客户端请求未被授权访问该资源
- 403:Forbidden(禁止访问):服务器拒绝响应客户端的请求
- 404:Not Found(未找到):请求的资源不存在
5xx:服务器错误状态码
- 500:Internal Server Error(内部服务器错误):服务器遇到了一个未曾预料的错误,无法完成客户的请求
- 502:Bad Gateway(坏的网关):服务器作为网关或代理角色,未能及时从上游服务器收到响应
- 503:Service Unavailable(服务不可用):服务器当前无法处理客户端的请求,一段时间后可能会恢复正常
什么是协商缓存?协商缓存的过程?协商缓存具体存在哪里?
协商缓存指的是客户端缓存中保存了一个资源的元数据; 当客户端发起请求时,会与服务器进行一次“协商”,如果服务器端判断该资源自上次请求后并未发生改变,服务器会返回 304 Not Modified,告诉客户端可以继续使用缓存中的资源,从而避免了不必要的数据传输,减轻了服务器的负担,提高了网页加载速度。
协商缓存的过程如下:
- 客户端第一次请求资源时,服务器返回资源并在响应头中添加一个 Last-Modified 标识。
- 客户端再次请求该资源时,客户端会在请求头中添加一个 If-Modified-Since 字段,该字段值为上次请求返回的 Last-Modified 值。
- 服务器端接收到请求后会根据 If-Modified-Since 字段和资源的最后修改时间进行比较,如果资源未被修改,则服务器返回 304 Not Modified 响应,客户端从缓存中获取该资源。
协商缓存存在客户端和服务器端,客户端通过浏览器缓存将资源保存在本地,避免再次发送请求;在服务器端,通过响应头的 Last-Modified 和 If-Modified-Since 字段进行通信,减少不必要的网络请求和数据传输。
cookie,localStorage,sessionStorage区别?它们会把数据存在哪?受不受同源策略制约?
- Cookie 可以存储一些小型数据,如用户偏好设置、登录状态、购物车、用户行为等信息。
- LocalStorage 可以存储大量的数据,数据可以长期保留,即使关闭了浏览器或重新启动电脑,数据仍旧存在。
- SessionStorage 只存在当前会话中,当关闭浏览器或者重新打开时,数据就会被清空。
cookie常用的属性有哪些?
cookie常用的属性有以下几种:
- name和value: 表示cookie的名字和值,这是cookie中最基本的属性。
- domain: 指定cookie所属的域名,例如".google.com", 表示该cookie属于所有以".google.com"结尾的网站。
- path: 指定cookie的路径,例如"/account",表示该cookie只属于网站中/account路径下的页面。
- expires和max-age: 用于设置cookie的过期时间。expires设置的是一个具体的过期时间点,例如"Thu, 01 Jan 1970 00:00:01 GMT"。而max-age则设置的是cookie生存周期的秒数,例如"3600",表示该cookie会在3600秒后过期。
- secure: 表示cookie只能在通过HTTPS或者其他安全协议加密的连接中传输。
- HttpOnly: 限制了cookie只能通过HTTP和HTTPS进行访问,不能通过JavaScript等客户端语言进行访问,从而提高cookie的安全性。
vue2和vue3的区别?
Vue3 引入了组合式 API,替代 Vue2 的选项式 API,它使得组件的逻辑更清晰、更易于测试和组合。
vue2 的响应性主要依赖 Object.defineProperty 进行实现,但是 Object.defineProperty 只能监听 指定对象的指定属性的 getter 行为和 setter 行为;我们在 data 中声明了一个对象 person ,但是在后期为 person 增加了新的属性,那么这个新的属性就会失去响应性。想要解决这个问题其实也非常的简单,可以通过 Vue.$set 方法来增加 指定对象指定属性的响应性。但是这样的一种方式,在 Vue 的自动响应性机制中是不合理。
在 Vue3 中,Vue 引入了反射和代理的概念,所谓反射指的是 Reflect,所谓代理指的是 Proxy。我们可以利用 Proxy 直接代理一个普通对象,得到一个 proxy 实例 的代理对象。在 vue3 中,这个过程通过 reactive 这个方法进行实现。
Object.defineProperty如何监听数组?为什么无法获取数组的变化?
Object.defineProperty 可以用于监听对象属性的变化,但是不能直接监听数组的变化。
如果要监听到数组的变化,可以使用 Array.prototype 提供的一些方法,比如 push、pop、splice 等; 使用 Vue 提供的 Vue.set 或 $set 方法来实现数组响应式。
因为 Object.defineProperty 在监听一个对象的变化时,实际上是监听到了对象的某个属性的变化。而对于数组来说,它的某些操作(如 push、pop、splice 等)实际上是修改了数组本身的属性,而不是修改了某个已存在的属性的值。
vue为什么要用data包裹属性
组件是一个可复用的实例,当你引用一个组件的时候,组件里的data是一个普通的对象,所有用到这个组件的都引用的同一个data,就会造成数据污染。
将data封装成函数后,在实例化组件的时候,我们只是调用了data函数生成的数据副本,避免了数据污染。
Vue 在初始化时会检测组件实例对象上所有声明的属性,将这些属性添加到 Vue 的响应式系统中。而在组件实例对象上直接声明属性,这些属性不会被添加到响应式系统中,也就不具有响应式的能力。
vue生命周期?组件间生命周期的顺序?
如何实现界面的切换?如何定义路由?
可以使用 <router-link>
标签或 router.push
方法来跳转到对应的页面。
引入vue-router
库来定义路由,包括路由的路径和对应的组件
vuex是做什么的?vue组件间通信的方式有哪些?React组件间通信的方式有哪些?
Vuex(Vue.js 中央状态管理模式)是一个专为 Vue.js 应用程序开发的状态管理库。它采用集中式存储管理全局共享状态,可以让我们更好地组织、维护和管理 Vue.js 应用程序中的状态。
Vuex 的核心概念分为以下几个部分:
- State:状态存储,即数据存储的地方。Vue 组件中的 data 存储的数据仅为组件自身的,Vuex 的 State 存储的数据全局共享。
- Getters:类似于 Vue 的计算属性,用于派生出一些状态,并且会缓存状态。
- Mutations:修改状态的方式,必须是同步函数。
- Actions:异步地、复杂的、或者需要多个 Mutation 触发的状态修改操作。
- Modules:将 Store 分割成各个模块,用于更好的管理项目中庞大、复杂的状态。
Vue 组件间通信有以下几种方式:
- 父子组件通信:父组件通过props将数据传递给子组件,子组件通过$emit触发自定义事件回传数据
- 子父组件通信:子组件通过this.parent访问父组件实例,通过emit触发自定义事件回传数据
- 兄弟组件通信:通过一个事件总线(Event Bus)或者Vuex来实现,Event Bus 就是通过一个新的 Vue 实例来实现数据传输
- 跨级组件通信:通过provide/inject来实现,provide可以在祖先级别中注册一个变量,inject可以在子孙级别中注入
React 组件间通信也有以下几种方式:
- 父子组件通信:父组件通过props将数据传递给子组件,子组件通过回调函数回传数据给父组件
- 子父组件通信:父组件通过props将回调函数传递给子组件,子组件通过调用父组件传递的回调函数回传数据
- 兄弟组件通信:通过一个共同的父组件传递数据,或者通过Context API共享状态
- 跨级组件通信:通过Context API来实现,在祖先组件中创建一个Context,子组件可以通过Consumer订阅Context中的数据
git常用命令?回滚怎么写?
git init :将当前目录初始化为Git仓库 git add <file> :将文件添加到本地暂存区 git commit -m "message" :将暂存区的文件提交到本地仓库,并添加操作说明message git push :将本地仓库内容推送到远程仓库 git pull :将远程仓库内容拉取到本地仓库 git clone :克隆远程仓库到本地
当需要回滚(即撤销)操作时,可以通过以下命令实现:
git reset:将文件从stage(即暂存区)中撤回到unstage(即工作区)
git reset --soft: 撤回commit,但不影响stage和工作区,可以使用git status查看变化 git reset --mixed: 撤回commit和stage,但不影响工作区,可以使用git status查看变化 git reset --hard: 撤回commit、stage和工作区,本地所有修改都会被删除,请谨慎使用
git revert:将指定commit的修改撤回(即创建一个新的commit来覆盖原有commit的修改)
git revert <commit-hash>: 撤回commit-hash对应的commit
webpack是干什么的?
Webpack是一个现代化的静态模块打包器,它将各种不同类型的文件(如js、css、图片等)视为一个模块,并将它们打包成最终的静态资源。Webpack的核心原理是将所有的资源都视为一个模块,并通过loader进行转换处理,最后将这些模块打包成一个或多个bundle。
Webpack的主要作用是优化前端资源加载,将多个文件打包成一个压缩文件,使得网页加载速度更快,用户体验更好。同时,Webpack还支持开发模式和生产模式的切换,支持开发过程中自动编译和热更新,以及支持代码分割和懒加载等高级特性,使得前端开发更加便捷和高效。
其他常用的插件有:
- html-webpack-plugin: 用于自动生成HTML文件并将其加入Webpack打包后的结果中,可以同时处理CSS和JS文件并自动添加<link>和<script>标签。
- css-loader: 用于将CSS文件转换为JavaScript模块,以便Webpack可以处理。
- style-loader: 将CSS以<style>标签插入到HTML文件中。
- babel-loader: 将ES6+的JavaScript代码转换为ES5的代码,以便Webpack可以处理。
webpack能处理什么类型的文件?不能处理什么类型的文件?
Webpack可以处理各种类型的文件,例如JavaScript、CSS、HTML、图片、字体等资源。Webpack通过使用loader来转译这些文件,将其转换为Webpack可以处理的模块。
Webpack可以处理大部分前端开发中遇到的文件类型。但是对于一些特殊的文件类型,例如PDF和Word文档等非常规的文件类型,Webpack可能并不能直接处理,需要借助其他工具或loader来进行支持。
算法题:移动零
可以使用双指针的方法解决,左指针指向已处理好的序列的尾部,右指针指向待处理序列的头部。遍历数组,当右指针指向的元素为0时,右指针向右移动1位,否则交换左右指针所指向的元素,左右指针同时向右移动1位。
function moveZeroes(nums) { let left = 0, right = 0; while (right < nums.length) { if (nums[right] !== 0) { // 交换左右指针所指向的元素 let temp = nums[left]; nums[left] = nums[right]; nums[right] = temp; // 左右指针同时向右移动1位 left++; } // 右指针向右移动1位 right++; } return nums; }#大家都开始春招面试了吗##春招##前端面试##面试答案##美团#
根据真实面试经历盘点面试题目,总结面试经验,分类总结面试题目答案