前端八股文第二篇
11.事件循环
事件循环(Event Loop)是 JavaScript 运行时中的一种机制,用于处理异步操作和事件驱动的编程。在浏览器环境中,事件循环是指浏览器通过事件队列(Event Queue)来管理和调度异步任务的执行顺序。
事件循环的基本原理如下:
-
执行同步任务:首先执行当前执行栈(Execution Stack)中的所有同步任务,直到执行栈为空。
-
处理异步任务:检查异步任务队列,如果有异步任务需要执行,则将其移出队列,放入执行栈中执行。异步任务可以包括定时器回调.事件回调.Promise 的回调等。
-
等待新的任务:一旦当前执行栈中的任务执行完毕,事件循环会检查是否有新的任务需要执行。如果有,就重复执行上述步骤;如果没有,则继续等待新的任务。
事件循环保证了 JavaScript 运行时的单线程特性,同时也确保了异步任务的执行顺序和正确性。JavaScript 运行时通过事件队列来存储和调度异步任务,通过事件循环来不断地检查队列中是否有待执行的任务,并按照顺序执行它们,从而实现了异步编程。
12.箭头函数
箭头函数(Arrow Functions)是 ES6 新增的一种函数声明方式,它提供了一种更简洁的语法形式来定义函数。
基本语法形式如下:
// 无参数箭头函数
const func = () => {
// 函数体
};
// 有参数箭头函数
const funcWithParam = (param1, param2) => {
// 函数体
};
// 简写形式:如果只有一个参数,可以省略参数括号
const funcWithSingleParam = param => {
// 函数体
};
// 如果函数体只有一条语句,可以省略花括号和 return 关键字
const singleStatementFunc = () => console.log("Hello");
// 箭头函数可以与解构赋值结合使用
const sum = (a, b) => a + b;
箭头函数具有以下特点:
-
简洁的语法:相比传统的函数声明方式,箭头函数的语法更为简洁明了,尤其适合用于回调函数或者只包含单一表达式的函数。
-
绑定
this
:箭头函数没有自己的this
,它会捕获所在上下文的this
值。这意味着箭头函数内部的this
始终指向词法作用域中的this
,而不是在运行时动态绑定。 -
不能作为构造函数:箭头函数不能使用
new
关键字来实例化对象,因为它没有自己的this
绑定,也没有prototype
。 -
不能使用 arguments 对象:箭头函数没有
arguments
对象,但可以使用剩余参数(rest parameters)来替代。
总的来说,箭头函数简化了函数的声明方式,并且通过固定的 this
绑定规则,避免了 this
指向的不确定性,使得代码更加可读和可维护。
13.react中组件传值
在 React 中,组件之间传值可以通过不同的方式实现:
- 父传子:
- 父组件通过在子组件上添加属性(props)来传递数据。
- 子组件通过
this.props
访问传递的值。
示例代码:
// ParentComponent.js
import React from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends React.Component {
render() {
return (
<div>
<ChildComponent message="Hello from ParentComponent" />
</div>
);
}
}
// ChildComponent.js
import React from 'react';
class ChildComponent extends React.Component {
render() {
return (
<div>
<p>{this.props.message}</p>
</div>
);
}
}
- 子传父:
- 子组件通过回调函数的方式,将数据传递给父组件。
- 父组件定义一个回调函数,并将其作为 props 传递给子组件。
- 子组件在需要传递数据时调用该回调函数。
示例代码:
// ParentComponent.js
import React from 'react';
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleData = this.handleData.bind(this);
}
handleData(data) {
this.setState({ message: data });
}
render() {
return (
<div>
<ChildComponent onData={this.handleData} />
<p>Message from child: {this.state.message}</p>
</div>
);
}
}
// ChildComponent.js
import React from 'react';
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onData('Hello from ChildComponent');
}
render() {
return (
<div>
<button onClick={this.handleClick}>Send Message to Parent</button>
</div>
);
}
}
- 兄弟组件互传:
- 如果兄弟组件之间需要相互传递数据,通常可以通过它们共同的父组件作为中介来实现。
- 将需要共享的数据或者状态提升到它们的共同父组件中,并通过 props 将数据传递给需要的兄弟组件。
示例代码:
// ParentComponent.js
import React from 'react';
import FirstSibling from './FirstSibling';
import SecondSibling from './SecondSibling';
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
sharedData: ''
};
this.handleData = this.handleData.bind(this);
}
handleData(data) {
this.setState({ sharedData: data });
}
render() {
return (
<div>
<FirstSibling onData={this.handleData} />
<SecondSibling sharedData={this.state.sharedData} />
</div>
);
}
}
// FirstSibling.js
import React from 'react';
class FirstSibling extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onData('Hello from FirstSibling');
}
render() {
return (
<div>
<button onClick={this.handleClick}>Send Message to SecondSibling</button>
</div>
);
}
}
// SecondSibling.js
import React from 'react';
class SecondSibling extends React.Component {
render() {
return (
<div>
<p>Message from FirstSibling: {this.props.sharedData}</p>
</div>
);
}
}
- Context 机制跨层级组件通信:
- Context 是 React 提供的一种跨层级传递数据的方式,可以避免 props 层层传递的繁琐。
- 使用
React.createContext()
创建一个 Context 对象,并通过Provider
和Consumer
分别提供数据和消费数据的能力。
示例代码:
// Context.js
import React from 'react';
const MyContext = React.createContext();
export const MyProvider = MyContext.Provider;
export const MyConsumer = MyContext.Consumer;
// ParentComponent.js
import React from 'react';
import { MyProvider } from './Context';
import ChildComponent from './ChildComponent';
class ParentComponent extends React.Component {
render() {
return (
<MyProvider value="Hello from ParentComponent">
<ChildComponent />
</MyProvider>
);
}
}
// ChildComponent.js
import React from 'react';
import { MyConsumer } from './Context';
class ChildComponent extends React.Component {
render() {
return (
<MyConsumer>
{value => <p>{value}</p>}
</MyConsumer>
);
}
}
这些是 React 中常见的组件传值方式,根据具体的应用场景和需求,选择合适的方式来实现组件之间的通信。
14.虚拟dom的理解
虚拟 DOM(Virtual DOM)是 React 中的一种优化技术,用于提高页面渲染性能和用户体验。它是一个内存中的虚拟树形结构,对应着真实 DOM 中的节点,但不直接操作浏览器中的 DOM。
虚拟 DOM 的基本原理是:
-
组件状态变化触发重新渲染:当组件的状态(state)发生变化时,React 会调用组件的
render()
方法重新渲染组件。 -
生成虚拟 DOM:在重新渲染过程中,React 会生成新的虚拟 DOM 树,表示更新后的页面结构。
-
比较虚拟 DOM:React 会将更新前后的两棵虚拟 DOM 树进行比较,找出发生变化的部分。
-
计算更新:根据比较结果,React 会计算出使真实 DOM 发生变化的最小操作集合。
-
更新真实 DOM:React 会将计算出的更新操作应用到真实 DOM 上,使页面得到更新。
虚拟 DOM 的优势包括:
-
提高性能:通过在内存中操作虚拟 DOM,避免直接操作真实 DOM 导致的性能损耗。React 会将多次 DOM 修改操作合并成一次,减少了浏览器的重绘和重排次数,从而提高了页面渲染性能。
-
简化复杂性:虚拟 DOM 的存在使得开发者可以专注于组件的状态和结构,而不必过多关注底层的 DOM 操作细节,简化了开发复杂界面的难度。
-
跨平台兼容:虚拟 DOM 可以提供与平台无关的抽象,使得 React 可以同时运行在浏览器.移动端和服务器端等不同的环境中,实现了跨平台兼容。
总的来说,虚拟 DOM 是 React 的核心技术之一,它通过在内存中操作虚拟 DOM 树,有效地提高了页面渲染性能和开发效率。
15.单点登录
单点登录(Single Sign-On,简称 SSO)是一种身份认证和授权机制,允许用户通过一次登录,即可访问多个相关系统或服务,而无需在每个系统中重新进行身份验证。
基本原理如下:
-
用户认证:用户在访问系统 A 时,需要进行身份认证。如果用户尚未登录,则系统 A 将重定向用户到身份提供者(如认证服务器或身份提供商)进行登录认证。
-
颁发令牌:身份提供者验证用户的身份后,会颁发一个令牌(Token),其中包含了用户的身份信息和授权信息。这个令牌可以是基于标准的令牌协议,如 JSON Web Token(JWT)。
-
访问其他系统:用户在系统 A 登录成功后,系统 A 会保存用户的登录状态,并向用户返回一个会话凭证(Session Cookie)等。用户在访问其他相关系统 B.C 等时,这些系统会检查用户的会话状态。如果用户已经登录,系统 B.C 等会通过令牌认证机制向身份提供者验证令牌的有效性,从而确定用户的身份和授权信息,而无需用户再次登录。
优点:
- 简化用户体验:用户只需进行一次登录,即可访问多个相关系统,大大简化了用户的操作流程,提升了用户体验。
- 提高安全性:通过中心化的身份认证和授权机制,可以减少密码的传输和存储,提高了系统的安全性。
- 降低开发和维护成本:单点登录可以降低系统间的集成成本,减少了重复开发和维护的工作量。
常见的单点登录解决方案包括 OAuth.OpenID Connect(OIDC).SAML 等,各自有不同的特点和适用场景。企业级的单点登录解决方案还包括常见的身份提供商,如 Microsoft Azure AD.Okta.Auth0 等。
16.vuex
Vuex 是 Vue.js 的状态管理模式,用于管理 Vue.js 应用中的状态(state)。它提供了一种集中式的存储管理方案,用于统一管理应用中的数据,并实现数据在组件之间的共享和响应式更新。
Vuex 主要包含以下几个核心概念:
-
State(状态):即应用中的数据状态,保存在 Vuex 的状态树中。可以通过
this.$store.state
来访问。 -
Getter(获取器):用于从状态树中派生出一些新的状态,类似于计算属性。可以通过
this.$store.getters
来访问。 -
Mutation(变更):用于修改状态树中的数据,必须是同步函数。通过提交 mutation 来触发状态的变更,可以通过
this.$store.commit()
方法来提交。 -
Action(动作):用于提交异步操作,可以包含任意异步操作。通常用于提交 mutation,而不直接变更状态。可以通过
this.$store.dispatch()
方法来分发。 -
Module(模块):将 Vuex 的状态树分割成模块,每个模块拥有自己的 state.getters.mutations 和 actions。
Vuex 的工作流程如下:
-
组件通过触发 action 来提交异步操作,action 中可以进行一些异步操作后再提交 mutation。
-
Mutation 中定义的方法用于修改状态,是同步函数。
-
通过 Getter 可以从状态树中派生出一些新的状态,Getter 类似于计算属性。
-
组件通过
this.$store.state
来访问状态,通过this.$store.commit()
来提交 mutation,通过this.$store.dispatch()
来分发 action。
总的来说,Vuex 提供了一种集中式的状态管理机制,用于解决 Vue.js 应用中的组件通信和状态共享问题,是构建大型单页应用的理想选择。
17.vue的声明周期有哪些,data里面的数据初始化是在哪个阶段
Vue.js 的生命周期包括了创建、挂载、更新和销毁等多个阶段,具体的生命周期钩子函数如下:
-
创建阶段(Creation):
beforeCreate
:在实例初始化之后,数据观测 (data observation) 和 event/watcher 事件配置之前被调用。created
:在实例创建完成后被立即调用。在这一步,实例已完成了数据观测 (data observation),属性和方法的运算,watch/event 事件回调。但是挂载阶段还未开始,$el 属性目前尚不可用。
-
挂载阶段(Mounting):
beforeMount
:在挂载开始之前被调用:相关的 render 函数首次被调用。mounted
:el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子函数。在该钩子函数中,组件已经被创建,DOM 树中已经有了对应组件的元素,因此可以进行 DOM 操作。
-
更新阶段(Updating):
beforeUpdate
:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。可以在该钩子函数中进一步地更改状态,不会触发附加的重渲染过程。updated
:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。调用时,组件 DOM 已经更新,所以可以执行依赖于 DOM 的操作。
-
销毁阶段(Destroying):
beforeDestroy
:在实例销毁之前调用。在这一步,实例仍然完全可用。destroyed
:在实例销毁之后调用。该钩子函数被调用时,所有的指令和子实例都已经被销毁,所有的事件监听器被移除。
关于 data
中的数据初始化是在哪个阶段,数据初始化是在 Vue 实例创建的生命周期阶段中,即在 beforeCreate
和 created
阶段之间。此时的数据可以在组件的其他生命周期中访问和使用。
18.vue双向数据绑定的原理
Vue 的双向数据绑定原理主要基于以下两个核心概念:数据劫持和发布-订阅模式。
-
数据劫持:
- 当 Vue 实例被创建时,会对 data 中的数据进行劫持(或称为响应式化),即通过 Object.defineProperty() 方法将 data 中的每个属性转换为 getter 和 setter,这样当属性被访问或修改时,Vue 就能够监测到,并触发相应的更新操作。
-
发布-订阅模式:
- Vue 中采用了发布-订阅模式来实现数据的变化通知和更新。每个数据属性都维护了一个依赖收集器(Dep),在模板中使用到该属性的地方都会创建一个 Watcher 对象并添加到该属性的依赖收集器中。
- 当属性发生变化时,其对应的 setter 方法会被调用,触发依赖收集器中所有 Watcher 对象的更新操作。这些 Watcher 对象负责将变化通知到对应的视图,并执行视图的更新操作,实现了数据和视图的双向绑定。
综上所述,Vue 的双向数据绑定原理是通过数据劫持和发布-订阅模式实现的。数据劫持确保了数据的变化能够被监测到,而发布-订阅模式则确保了数据变化时能够及时通知到相关的视图进行更新,从而实现了数据和视图之间的双向绑定。
19.闭包
闭包(Closure)是指函数和函数内部能够访问到函数外部作用域的变量的组合。在 JavaScript 中,函数可以创建闭包。闭包由两部分组成:函数本身和函数能够访问的外部作用域(词法环境)。
闭包的特点包括:
-
函数内部可以访问外部作用域的变量:闭包可以访问包含它的外部函数中声明的变量,即使在外部函数执行完毕之后仍然可以访问这些变量。
-
变量的值被保护:外部函数的变量在闭包内被引用时,其值会被保护起来,不会被垃圾回收机制回收,直到闭包不再被使用时才会被释放。
-
延长了变量的生命周期:由于闭包中包含对外部变量的引用,这些变量的生命周期会延长,直到闭包不再被使用。
闭包在 JavaScript 中的应用非常广泛,常见的应用场景包括:
- 封装变量:可以使用闭包封装变量,避免全局变量的污染。
- 实现私有变量和方法:闭包可以实现类似于私有变量和方法的效果,对外部代码隐藏内部实现细节。
- 定时器和回调函数:在定时器和回调函数中经常会使用闭包来保持对外部变量的引用,以确保在函数执行时能够访问到正确的变量值。
总的来说,闭包是 JavaScript 中强大而灵活的特性,合理地运用闭包能够帮助我们编写更加优雅和健壮的代码。
20.https为什么比http更安全
HTTPS(HyperText Transfer Protocol Secure)是一种通过加密通道传输数据的安全 HTTP 协议,而 HTTP(HyperText Transfer Protocol)则是一种明文传输数据的协议。HTTPS 相对于 HTTP 更安全的主要原因包括以下几点:
-
数据加密传输:
- 在 HTTPS 中,传输的数据会经过 SSL/TLS 协议进行加密,可以有效防止中间人攻击和窃听,使得数据传输更加安全可靠。
-
身份验证:
- HTTPS 还可以通过证书来验证网站的身份,确保用户与服务器之间的通信是安全可信的,防止钓鱼网站等恶意攻击。
-
完整性保护:
- HTTPS 在传输过程中使用了数字证书对数据进行签名,可以验证数据的完整性,防止数据被篡改或修改。
-
信任机构认证:
- HTTPS 使用了公开密钥基础设施(PKI)来验证网站的身份,浏览器会对证书进行信任机构的验证,确保用户访问的网站是可信任的,防止了中间人攻击。
-
搜索引擎优化:
- 由于 HTTPS 可以提供更安全的用户体验,搜索引擎会更倾向于将使用 HTTPS 的网站排名更靠前,提高网站的可见性和流量。
综上所述,HTTPS 相比 HTTP 更安全的原因主要在于数据加密传输、身份验证、完整性保护、信任机构认证等方面,这些安全机制有效地保护了用户和网站之间的通信安全,提高了网络数据传输的安全性和可靠性。