解决国内不能访问github的问题
问题
最近访问GitHub总是不稳定,经常连不上, 出各种错误(OpenSSL SSL_read: Connection was reset, errno 10054, Connection refused …)。
原因大概是GitHub的IP经常变化(一天可能变几次),我们本地的DNS解析有问题,解决办法就是查询github的IP,手工写到我们的hosts文件中。
查询GitHub最新IP
- 访问 https://github.com.ipaddress.com/
- IP Address 即为IP地址

修改hosts文件
- 目录:C:\Windows\System32\drivers\etc
- 打开
hosts文件 - 结尾增加一行
140.82.112.4 github.com(IP 用你查询到的最新的IP) - 搞定,测试一下
说明,
hosts文件, 可以给github.com添加多条IP记录,这样当我们访问github时,会一次尝试每个IP是否可以访问,如果都不行,会去Internet上做DNS解析。
可以这样配置
1 | 140.82.112.4 github.com |
(算法) - 不使用递归,实现斐波那契数列
https://jackniu81.github.io/2021/04/18/Algorithm-Fibonacci-numbers/
1. 斐波那契数列
斐波那契数列:0, 1, 1, 2, 3, 5, 8, 13 … …
通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。
1 | F(0) = 0,F(1) = 1 |
敏捷开发时,我们估算Story Point,通常就是使用斐波那契数列。
2. 解题思路
2.1. 递归
F(n) = F(n - 1) + F(n - 2), 很简单,就不谈了。
2.2. 动态规划
动态规划的思想是,记录中间计算结果,计算后面相时,根据前面保存的结果直接计算,避免重复计算。
3. Javascript 实现
1 | /** |
还可以进一步优化,当前使用数组记录以及计算过的F(n),由于F(n) = F(n - 1) + F(n - 2),实际只需要记录最近的2个值即可,不需要使用数组记录全部数据。
2021 .NET/dotnet Core/C# 面试题及参考答案
1. NET/dotnet core/.NET framework 的关系?
先看 2 张图
最新的.NET 5 的示意图
之前的(dotnet core3.x 时代)的示意图
总结如下:
- 2016 年开始,分开了.NET Framework 和.NET Core,后者开源、面向跨平台应用;
- 2019 年,.NET Framework 和.NET Core 被.NET5 完成了统一。未来只会有一个.NET,可以支持 Windows、Linux、macOS、iOS、Android、tvOS、watchOS 和 WebAssembly,等等
- .Net standard library 是之前.NET Framework 和.NET Core 公共的部分;
2. corefx, coreclr 都是什么?
dotnet 组织包含了.NET Core 的核心代码, 包括 coreclr 和 corefx 等.
- coreclr 是运行时代码,
- corefx 是各种基础类库的代码. CoreFX 主要包含数个公共库,例如 System.Collections, System.IO, System.Xml 等。
3. asp.net core
3.1. Kestrel
Kestrel 是 asp.net core 引入的跨平台的网络服务器,是默认配置。可以没有 iis, apache 和 nginx 就可以单独运行。
Kestrel 基于 libuv 实现;(node.js 也是基于 libuv)
3.2. asp.net core 主要的特性
- 依赖注入。
- 日志系统架构。
- 引入了一个跨平台的网络服务器,kestrel。可以没有 iis, apache 和 nginx 就可以单独运行。
- 可以使用命令行创建应用。
- 使用 AppSettings.json 来配置工程。
- 使用 start up 来注册服务。
- 更好的支持异步编程。
- 支持 web socket 和 signal IR。
- 对于跨网站的请求的预防和保护机制。
3.3. startup class 的作用?
Startup class 是 dot net core 应用的入口。所有的 dot net core 应用必须有这个 class。这个类用来配置应用。这个类的调用是在 program main 函数里面进行配置的。类的名字可以自己定义。
3.4. Startup class 的 config service 方法有什么作用?
在这个方法里我们可以添加一些 service 进入依赖注入容器。
3.5. startup class 的 configure 方法有什么作用?
这个方法来定义整个应用如何响应 HTTP 请求。它有几个比较重要的参数,application builder,Hosting environment, logo factory, 在这里我们可以配置一些中间件用来处理路径,验证和 session 等等。
3.6. 什么是 meta packages?
Meta packages 是指包含所有 ASP dot net code 依赖的一个包。叫做 Microsoft.AspNetCore.All
3.7. 什么是中间件?
中间件在这里是指注入到应用中处理请求和响应的组件。asp.net core 的中间件模型也是洋葱模型(和 node.js d Koa 一样)
3.8. application builder 的 use 和 run 方法有什么区别?
这两个方法都在 start up class 的 configure 方法里面调用。都是用来向应用请求管道里面添加中间件的。Use 方法可以调用下一个中间件的添加,而 run 不会。
3.9. dot net core 管道(pipeline)里面的 map 拓展有什么作用?
可以针对不同的路径添加不同的中间件。
1 | public void Configure(IApplicationBuilder app) |
3.10. 如何在 dot net core 中激活 session 功能?
- 首先要添加 session 包.
- 其次要在 config service 方法里面添加 session。
- 然后又在 configure 方法里面调用 usesession。
3.11. 如何在 controller 中注入 service?
依赖注入的思想:
- 在 config services 方法中配置这个 service。
- 在 controller 的构造函数中,添加这个依赖注入。
3.12. dot net core 里面的路径是如何处理的?
路径处理是用来为进入的请求寻找处理函数的机制。所有的路径在函数运行开始时进行注册。
主要有两种路径处理方式:
- 常规路径处理就是用 MapRoute 的方式设定调用路径
- 属性路径处理是指在调用函数的上方设定一个路径属性。
3.13. application builder 的 use 和 run 方法有什么区别?
这两个方法都在 start up class 的 configure 方法里面调用。都是用来向应用请求管道里面添加中间件的。Use 方法可以调用下一个中间件的添加,而 run 不会。
3.14. 依赖注入后的服务生命周期?
在 dot net core 中,我们不需要关心如何释放这些服务, 因为系统会帮我们释放掉。有三种服务的生命周期。
- 单实例服务, 通过 add singleton 方法来添加。在注册时即创建服务, 在随后的请求中都使用这一个服务。
- 短暂服务, 通过 add transient 方法来添加。是一种轻量级的服务,用于无状态服务的操作。
- 作用域服务,一个新的请求会创建一个服务实例。使用 add scoped 方法来添加。
3.15. 什么么是 Razor 页面?
是 dot net core 中支持 ASP 网页表格的一种开发模型。@page 作为页面的起始标志。可以看做是 asp.net core 使用的模板引擎。
3.16. 如何在 Razor 页面中实现数据模型绑定?
使用 bindproperty 属性。
3.17. Server.Transfer 和 Response.Redirect 的区别是什么
- Server.Transfer 仅是服务器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;
- Response.Redirect 则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。
3.18. API,什么时候用 Get/Post/Put/Delete
- GET: 查询
- POST :创建对象
- PUT: 修改对象
- DELETE:删除
4. C#
4.1. 一个单例(Singleton)类
单线程下的简单实现:
1 | public MySingleton |
线程安全的加锁双检查
1 | public class MySingleton |
4.2. abstract class 和 interface 有什么区别?
相同点:都不能被直接实例化,都可以通过继承实现其抽象方法。
不同点:
- 接口支持多继承;抽象类不能实现多继承。
- 接口只能定义行为;抽象类既可以定义行为,还可能提供实现。
- 接口只包含方法(Method)、属性(Property)、索引器(Index)、事件(Event)的签名,但不能定义字段和包含实现的方法;
- 抽象类可以定义字段、属性、包含有实现的方法。
4.3. 是否可以继承 String 类?
String 类是 sealed 类故不可以继承。
4.4. StringBuilder 和 String 的区别?
String 在进行运算时(如赋值、拼接等)会产生一个新的实例,而 StringBuilder 则不会。所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用 StringBuilder,不要使用 String。
但是简单的字符拼接,String 还是首选。(100?次以内用 String,以上用 StringBuilder)
4.5. 什么情况下会用到虚方法?它与接口有什么不同?
- 子类重新定义父类的某一个方法时,必须把父类的方法定义为 virtual
- 在定义接口中不能有方法体,虚方法可以。
- 实现时,子类可以不重新定义虚方法,但如果一个类继承接口,那必须实现这个接口。
4.6. string、String;int、Int32;Boolean、bool 的区别
String、Int32、Boolean 等都属于.Net 中定义的类,而 string、int、bool 相当于 C#中对这些类定义的别名。CTS。
4.7. private、 protected、 public、 internal 修饰符的访问权限。
- private : 私有成员, 在类的内部才可以访问。
- protected : 保护成员,该类内部和继承类中可以访问。
- public : 公共成员,完全公开,没有访问限制。
- internal: 当前程序集内可以访问。
4.8. C#中的委托是什么?事件是不是一种委托?事件和委托的关系。
委托可以把一个方法作为参数代入另一个方法,可以理解为指向一个函数的指针。
换个说法来解释,委托就是函数指针,事件就是保存多个函数指针的数组。
事件和委托的区别如下:
- 事件只能在方法的外部进行声明,而委在方法的外部和内部都可以声明。
- 事件只能在类的内部触发,不能在类的外部触发。而委托在类的内部和外都都可以触发。
- 委托一般用于回调,而事件用于外部接口。例如在观察者模式中,在被观察者中可以声明一个事件作为外部观察者注册的接口。
4.9. override 与重载(overload)的区别
- 重载是方法的名称相同。参数或参数类型不同,进行多次重载以适应不同的需要。重载(overload)是面向过程的概念。
- Override 是进行基类中函数的重写。Override 是面向对象的概念
4.10. 什么是装箱(boxing)和拆箱(unboxing)
装箱:从值类型接口转换到引用类型。
拆箱:从引用类型转换到值类型。
——–END——–
参考:
https://www.webtrainingroom.com/interview/asp-net-core-interview-questions-answers > https://www.talkingdotnet.com/asp-net-core-interview-questions/
—————- END —————-
2021 Vue.js 面试题汇总及答案
- 1. 基本问题
- 1.1. Vue.js 的特点
- 1.2. Vue.js 双向绑定的原理
- 1.3. Vue.js 3.0 放弃defineProperty, 使用Proxy的原因
- 1.4. Vue 2 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?
- 1.5. Computed和Watch的区别
- 1.6. Computed 和 Methods 的区别
- 1.7. 虚拟DOM,diff算法
- 1.8. 为何需要Virtual DOM?
- 1.9. 过滤器 (Filter)
- 1.10. 常见的事件修饰符及其作用
- 1.11. v-show指令和v-if指令的区别是什么?
- 1.12. v-model 是如何实现的,语法糖实际是什么
- 1.13. data为什么是一个函数而不是对象
- 1.14. Vue template 到 render 的过程
- 1.15. Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?
- 1.16. axios是什么
- 1.17. sass是什么?如何在vue中安装和使用?
- 1.18. Vue.js页面闪烁
- 1.19. 如何解决数据层级结构太深的问题
- 1.20. 在 Vue. js开发环境下调用API接口,如何避免跨域
- 1.21. 批量异步更新策略
- 1.22. vue 的 nextTick 方法的实现原理
- 1.23. Vue 组件 data 为什么必须是函数 ?
- 1.24. v-if和v-for一起使用的弊端及解决办法
- 1.25. vue常用指令
- 1.26. 组件传值方式有哪些
- 1.27. vue-loader是什么?使用它的用途有哪些?
- 2. 组件 Component
- 3. Vuex
- 4. Router
1. 基本问题
1.1. Vue.js 的特点
- 易用: 简单,易学,上手快
- 灵活: (渐进式)不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩。
- 高效: 20kB min+gzip 运行大小;超快虚拟 DOM;最省心的优化
- 双向绑定:开发效率高
- 基于组件的代码共享
- Web项目工程化,增加可读性、可维护性
1.2. Vue.js 双向绑定的原理
Vue.js 2.0 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
Vue.js 3.0, 放弃了Object.defineProperty ,使用更快的ES6原生 Proxy (访问对象拦截器, 也称代理器)
步骤:
需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器(dep)里面添加自己 ②自身必须有一个update()方法 ③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
1.3. Vue.js 3.0 放弃defineProperty, 使用Proxy的原因
Object.defineProperty缺陷
- 监控到数组下标的变化时,开销很大。所以Vue.js放弃了下标变化的检测;
- Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象。Object.defineProperty需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。
- Object.defineProperty对新增属性需要手动进行Observe。vue2时需要使用 vm.$set 才能保证新增的属性也是响应式
- Proxy支持13种拦截操作,这是defineProperty所不具有的
- Proxy 作为新标准,长远来看,JS引擎会继续优化 Proxy,但 getter 和 setter 基本不会再有针对性优化
1.4. Vue 2 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?
视图并未刷新。这是因为在Vue实例创建时,新属性并未声明,因此就没有被Vue转换为响应式的属性,自然就不会触发视图的更新,这时就需要使用Vue的全局 api $set():this.$set(this.obj, 'new_property', 'new_value')
1.5. Computed和Watch的区别
- computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的 属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
- watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每 当监听的数据变化时都会执行回调进行后续操作。
运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率, 并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
- 多个因素影响一个显示,用Computed;一个因素的变化影响多个其他因素、显示,用Watch;
1.6. Computed 和 Methods 的区别
- computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值对于 method ,只要发生重新渲染,
- method 调用总会执行该函数
1.7. 虚拟DOM,diff算法
(1)让我们不用直接操作DOM元素,只操作数据便可以重新渲染页面
(2)虚拟dom是为了解决浏览器性能问题而被设计出来的
当操作数据时,将改变的dom元素缓存起来,都计算完后再通过比较映射到真实的dom树上
(3)diff算法比较新旧虚拟dom。如果节点类型相同,则比较数据,修改数据;如果节点不同,直接干掉节点及所有子节点,插入新的节点;如果给每个节点都设置了唯一的key,就可以准确的找到需要改变的内容,否则就会出现修改一个地方导致其他地方都改变的情况。比如A-B-C-D, 我要插入新节点A-B-M-C-D,实际上改变的了C和D。但是设置了key,就可以准确的找到B C并插入
1.8. 为何需要Virtual DOM?
- 具备跨平台的优势
- 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。
- 提升渲染性能
1.9. 过滤器 (Filter)
在Vue中使用filters来过滤(格式化)数据,filters不会修改数据,而是过滤(格式化)数据,改变用户看到的输出(计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示。
使用场景: 比如需要处理时间、数字等的的显示格式;
1.10. 常见的事件修饰符及其作用
.stop:等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;.prevent:等同于 JavaScript 中的 event.preventDefault() ,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);.capture:当元素发生冒泡时,先触发带有该修饰符的元素。若有多个该修饰符,则由外而内触发。如 div1中嵌套div2中嵌套div3.capture中嵌套div4,那么执行顺序为:div3=》div4=》div2=》div1.self:只会触发自己范围内的事件,不包含子元素;.once:只会触发一次。
1.11. v-show指令和v-if指令的区别是什么?
v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;而v-if会控制这个 DOM 节点的存在与否。当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
1.12. v-model 是如何实现的,语法糖实际是什么
- 作用在表单元素上
v-model="message"等同于v-bind:value="message" v-on:input="message=$event.target.value" - 作用在组件上, 本质是一个父子组件通信的语法糖,通过prop和$.emit实现, 等同于
:value="message" @input=" $emit('input', $event.target.value)"
1.13. data为什么是一个函数而不是对象
JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。
而在Vue中,我们更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。
所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当我们每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。
1.14. Vue template 到 render 的过程
- 调用parse方法将template转化为ast(抽象语法树, abstract syntax tree)
- 对静态节点做优化。如果为静态节点,他们生成的DOM永远不会改变,这对运行时模板更新起到了极大的优化作用。
- 生成渲染函数. 渲染的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名,子节点,文本等等)
1.15. Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?
不会立即同步执行重新渲染。
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。
然后,在下一个的事件循环”tick”中,Vue 刷新队列并执行实际(已去重的)工作。
1.16. axios是什么
易用、简洁且高效的http库, 支持node端和浏览器端,支持Promise,支持拦截器等高级配置。
1.17. sass是什么?如何在vue中安装和使用?
sass是一种CSS预编译语言安装和使用步骤如下。
- 用npm安装加载程序( sass-loader、 css-loader等加载程序)。
- 在 webpack.config.js中配置sass加载程序。
1.18. Vue.js页面闪烁
Vue. js提供了一个v-cloak指令,该指令一直保持在元素上,直到关联实例结束编译。当和CSS一起使用时,这个指令可以隐藏未编译的标签,直到实例编译结束。用法如下。
1 | [v-cloak]{ |
1.19. 如何解决数据层级结构太深的问题
在开发业务时,经常会岀现异步获取数据的情况,有时数据层次比较深,如以下代码: span 'v-text="a.b.c.d"></span>, 可以使用vm.$set手动定义一层数据: vm.$set("demo",a.b.c.d)
1.20. 在 Vue. js开发环境下调用API接口,如何避免跨域
config/ index.js内对 proxyTable项配置代理。
1.21. 批量异步更新策略
Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
换句话说,只要观察到数据变化,就会自动开启一个队列,并缓冲在同一个事件循环中发生的所以数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和 DOM 操作。
1.22. vue 的 nextTick 方法的实现原理
- vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行
- microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕
1.23. Vue 组件 data 为什么必须是函数 ?
因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染。
所以一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
1.24. v-if和v-for一起使用的弊端及解决办法
由于v-for的优先级比v-if高,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。
解决办法:
- 在v-for的外层或内层包裹一个元素来使用v-if
- 用computed处理
1.25. vue常用指令
- v-model 多用于表单元素实现双向数据绑定(同angular中的ng-model)
- v-bind 动态绑定 作用: 及时对页面的数据进行更改
- v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面
- v-for 格式: v-for=”字段名 in(of) 数组json” 循环数组或json(同angular中的ng-repeat)
- v-show 显示内容 (同angular中的ng-show)
- v-hide 隐藏内容(同angular中的ng-hide)
- v-if 显示与隐藏 (dom元素的删除添加 同angular中的ng-if 默认值为false)
- v-else-if 必须和v-if连用
- v-else 必须和v-if连用 不能单独使用 否则报错 模板编译错误
- v-text 解析文本
- v-html 解析html标签
- v-bind:class 三种绑定方法 1、对象型 ‘{red:isred}’ 2、三元型 ‘isred?”red”:”blue”‘ 3、数组型 ‘[{red:”isred”},{blue:”isblue”}]’
- v-once 进入页面时 只渲染一次 不在进行渲染
- v-cloak 防止闪烁
- v-pre 把标签内部的元素原位输出
1.26. 组件传值方式有哪些
- 父传子:子组件通过props[‘xx’] 来接收父组件传递的属性 xx 的值
- 子传父:子组件通过 this.$emit(‘fnName’,value) 来传递,父组件通过接收 fnName 事件方法来接收回调
- 其他方式:通过创建一个bus,进行传值
- 使用Vuex
1.27. vue-loader是什么?使用它的用途有哪些?
2. 组件 Component
2.1. vue中如何编写可复用的组件 (编写组件的原则)
- 以组件功能命名
- 只负责ui的展示和交互动画,不要在组件里与服务器打交道(获取异步数据等)
- 可复用组件不会因组件使用的位置、场景而变化。尽量减少对外部条件的依赖。
2.2. 如何让CSS只在当前组件中起作用?
在每一个Vue.js组件中都可以定义各自的CSS、 JavaScript代码。如果希望组件内写的CSS只对当前组件起作用,只需要在Style标签添加Scoped属性,即。
2.3. keep-alive是什么?
如果需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。
两个重要属性,include 缓存组件名称,exclude 不需要缓存的组件名称。
2.4. 如何在 Vue. js动态插入图片
对“src”属性插值将导致404请求错误。应使用 v-bind:src (简写:src)格式代替。
2.5. 父子组件的生命周期顺序
- 加载渲染过程:
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted - 子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
- 父组件更新过程:父beforeUpdate->父updated
- 销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
3. Vuex
3.1. vuex的核心概念
- state => 基本数据
- getters => 从基本数据派生的数据
- mutations => 修改数据,同步
- actions => 修改数据,异步 (Action 提交的是 mutation,而不是直接变更状态)
- modules => 模块化Vuex
3.2. vuex是什么?怎么使用?哪种功能场景使用它?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理器,采用集中式存储管理应用的所有组件的状态,主要是为了多页面、多组件之间的通信。
Vuex有5个重要的属性,分别是 State、Getter、Mutation、Action、Module,由 view 层发起一个 Action 给 Mutation,在 Mutation 中修改状态,返回新的状态,通过 Getter暴露给 view层的组件或者页面,页面监测到状态改变于是更新页面。如果你的项目很简单,最好不要使用 Vuex,对于大型项目,Vuex 能够更好的帮助我们管理组件外部的状态,一般可以运用在购物车、登录状态、播放等场景中。
3.3. 多个组件之间如何拆分各自的state,每块小的组件有自己的状态,它们之间还有一些公共的状态需要维护,如何思考这块
- 公共的数据部分可以提升至和他们最近的父组件,由父组件派发
- 公共数据可以放到vuex中统一管理,各组件分别获取
4. Router
4.1. vue-router路由的两种模式
vue-router中默认使用的是hash模式
- hash模式, 带#。如:http://localhost:8080/#/pageA。改变hash,浏览器本身不会有任何请求服务器动作的,但是页面状态和url已经关联起来了。
- history模式,不带#, 如:http://localhost:8080/ 正常的而路径,并没有#。基于HTML5的 pushState、replaceState实现
4.2. vue-router如何定义嵌套路由
通过children 数组:
1 | const router = new VueRouter({ |
4.3. vue-router有哪几种导航钩子?
- 全局导航钩子:router.beforeEach(to,from,next)
- 组件内的钩子beforeRouteEnter (to, from, next) beforeRouteUpdate (to, from, next) beforeRouteLeave (to, from, next)
- 单独路由独享组件 beforeEnter: (to, from, next)
4.4. $route和$router的区别
- $route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
- $router是“路由实例”对象包括了路由的跳转方法,钩子函数等
4.5. 路由之间跳转的方式
- 声明式(标签跳转)
- 编程式( js跳转)
4.6. active-class是哪个组件的属性
vue-router 模块 的router-link组件
React 入门(2)- 路由 React Router
环境:
- React 17.x
- react-router-dom: 5.2
1. 概述
React 本身是一个极简的类库,并且遵循渐进式原则,所以是不带路由(Router)功能的。对于稍复杂些的应用,没有路由显然是不可以的,所以就需要引入路由(React Router)库。
React Router 从5.0开始只分为Core,Web 和Native3种。 从5.0开始,彻底将原本的针对网页使用的react-router的npm package命名改为了react-router-dom。也就是说,针对web应用,用react-router-dom吧。(本文暂不涉及Native)
BrowserRouter与HashRouter的区别
React Router 有2中模式,BrowserRouter与HashRouter,下面我们来看一下区别
- url表现形式不一样,看是否包含#
- BrowserRouter没有,例如:http://localhost:3000/about
- HashRouter的路径包含#,例如:http://localhost:3000/#/about
- 底层原理不一样
- BrowserRouter使用的是HTML5的history API(window.history.pushState, popState)
- HashRouter使用的是URL的哈希值。
- 刷新后对路由state参数的影响
- BrowserRouter没有任何影响,因为state保存在history对象中。
- HashRouter刷新后会导致路由state参数的丢失。
安装和使用
安装:
npm i react-router-dom
angular绑定(脏检查)方面的性能优化技巧
- 1. 为什么要优化
- 2. 编程习惯方面
- 3. ChangeDetectionStrategy.OnPush 进行性能提升
- 4. 利用ngzone-runOutsideAngular优化
- 5. 手动控制脏检查 ChangeDetectorRef
- 6. 总结
环境:
- Angular: 8+
1. 为什么要优化
双向绑定是一柄双刃剑,提高开发效率的同时,也牺牲了性能。当然,随着硬件性能的提升,Angular自身性能的提升,对于一般(中小)复杂度的应用,性能问题可以忽略不计。但是对于特殊场景,或复杂页面来说,我们就需要单独的处理数据绑定问题,否则就会有卡顿的现象,影响用户体验。
2. 编程习惯方面
平时的一些小技巧,小习惯,都可以改善Angular绑定方面的性能。
2.1. NgForOf,加入trackBy提升性能
trackBy定义如何跟踪可迭代项的更改的函数。在迭代器中添加、移动或删除条目时,指令必须重新渲染适当的 DOM 节点。为了最大程度地减少 DOM 中的搅动,仅重新渲染已更改的节点。
默认情况下,变更检测器假定对象实例标识可迭代对象。提供此函数后,指令将使用调用此函数的结果来标识项节点,而不是对象本身的标识。
2.2. Angular数据绑定的三种方式
1 | <div> |
- 直接绑定: 大多数情况下,这都是性能最好的方式。
- 绑定方法调用的结果:在每个脏值检测过程中,classes方程都要被调用一遍。如果没有特殊需求,应尽量避免这种使用方式。
- pipe方式: 它和绑定function类似,每次脏值检测classPipe都会被调用。不过Angular给pipe做了优化,加了缓存,如果item和上次相等,则直接返回结果。
1 | <li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li> |
2.3. 除非需要,都是用单向绑定,减少监控值的个数
对于一般数据来说,都是只需要展示给用户,不需要修改。那么对于这部分数据,使用单向绑定即可(ts->html).
如:
1 | <!-- 也称插值绑定 --> |
3. ChangeDetectionStrategy.OnPush 进行性能提升
对于一些很复杂的页面,上面的小技巧就不够用了,不过Angular也是考虑到这些了,提供了不少方法。
Angular 对比 AngularJS 在变化检测上由原来的双向检测(父->子,子->父)变为了单向(父->子)。所以每一次变化检测都会确定性地收敛。
Angular定义一个组件时,可以传入一个变化检测配置项为
1 | changeDetection: ChangeDetectionStrategy.OnPush | ChangeDetectionStrategy.Default; |
onpush策略只判断输入的引用(如果是object)是否改变,来判断是否进行脏检查。因此,我们可以使用onpush策略来减少变化检测的开销。
4. 利用ngzone-runOutsideAngular优化
Angular依赖NgZone来监听异步操作,并从根部执行变化检测。换句话说,我们代码中的每一个 addEventListener都会触发脏检查。但是如果我们非常明确,有些addEventListener要执行的东西,不会(或者说可以忽略)影响数据结果,不想然他触发脏检查。比如监测scroll,监测鼠标事件等。
针对这种情况, 我们可以使用zone提供的runOutsideAngular,让这些事件不触发脏检查。
1 | this.zone.runOutsideAngular(() => { |
5. 手动控制脏检查 ChangeDetectorRef
Angular的ChangeDetectorRef实例上提供了可以绑定或解绑某个组件脏检查的方法。
1 | class ChangeDetectorRef { |
6. 总结
- 平时的一些小习惯,都可以提高angular的性能;
- 针对复杂应用,或者当出现卡顿时,我们也是有办法的!
CSS面试题汇总
CSS实现水平垂直居中
- margin: auto;实现绝对定位元素的居中
.center-vertical{
width: 100px;
height: 100px;
background: orange;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
- CSS3.0弹性布局
html,
body {
width: 100%;
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center; /* 定义body的元素水平居中 */
}
.content {
width: 300px;
height: 300px;
background: orange;
}
行内元素有哪些?块级元素有哪些?
- 行内元素有:
a b span img input select strong - 块级元素有:
div ul ol li dl dt dd h1 h2 h3 h4…p - 常见的空元素:
<br> <hr> <img> <input> <link> <meta>
CSS3有哪些新特性?
- 新增各种CSS选择器 (: not(.input):)
- 圆角 (border-radius:8px)
- 多列布局 (multi-column layout)
- 阴影和反射 (Shadow\Reflect)
- 文字特效 (text-shadow)
- 文字渲染 (Text-decoration)
- 线性渐变 (gradient)
- 旋转 (transform)
- 缩放,定位,倾斜,动画,多背景
—————- END —————-
======================
前端(Fontend)面试题汇总
1. http 1.1/2.0 新增加的功能
HTTP1.1
- 缓存处理,在 HTTP1.0 中主要使用 header 来控制缓存策略。
- 带宽优化及网络连接的使用,HTTP1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分, 并且支持断点续传。
- 错误通知的管理,在 HTTP1.1 中新增了 24 个错误状态响应码,如 409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- Host 头处理,在 HTTP1.0 中认为每台服务器都绑定一个唯一的 IP 地址,因此,请求消息中的 URL 并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个 IP 地址。HTTP1.1 的请求消息和响应消息都应支持 Host 头域,且请求消息中如果没有 Host 头域会报告一个错误(400 Bad Request)。
- 长连接,HTTP 1.1 支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个 TCP 连接上可以传送多个 HTTP 请求和响应. (Connection: keep-alive)
HTTP2.0 大幅度的提高了 web 性能,进一步减少了网络的延迟。实现低延迟高吞吐量。
- 二进制分帧 (二进制格式编码)
- 首部压缩 (header)
- 多路复用 (HTTP2.0 可以在共享 TCP 连接的基础上同时发送请求和响应,避免 HTTP 旧版本的队头阻塞问题)
- 请求优先级
- 服务器推送 (服务端根据客户端的请求,提前返回多个响应,推送额外的资源给客户端。)
2. 用户输入 URL 到浏览器显现给用户页面经过了什么过程
- 用户输入 URL,浏览器获取到 URL
- 检查缓存,如果有有效缓存,直接加载
- 浏览器(应用层)进行 DNS 解析(直接输入 IP 地址既跳过该步骤)
- 根据解析出的 IP 地址+端口,浏览器(应用层)发起 HTTP 请求
- 请求到达传输层,tcp 协议为传输报文提供可靠的字节流传输服务,它通过三次握手等手段来保证传输过程中的安全可靠。通过对大块数据的分割成一个个报文段的方式提供给大量数据的便携传输。
- 服务器端收到发送方的 HTTP 请求之后,进行请求文件资源(如 HTML 页面)的寻找并响应报文
- 客户端收到返回内容后,处理 html
- 解析 html,生成 dom 树
- CSS 解析,为 DOM 中元素加入样式
- javascript 解析处理,更改 dom 树
- 进行页面渲染
- 重排、重绘
3. HTML5 的新特性
- 新的内容标签:header nav content footer article 1. aside
- 更好的单元格体系:
- 音频、视频 API:video radio
- 画布(Canvas) API
- 地理(Geolocation) API
- 网页存储(Web storage) 1. API:localStorage,sessionStorage
- 拖拽释放(Drag and drop) API
4. 对浏览器内核的理解
主要分成两部分:渲染引擎(layout engine 或 Rendering Engine)和 JS 引擎。
- 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
- JS 引擎则:解析和执行 javascript 来实现网页的动态效果。最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。
5. cookies,sessionStorage 和 localStorage
cookie 是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。
cookie 数据始终在同源的 http 请求中携带(即使不需要),记会在浏览器和服务器间来回传递。
sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存。
存储大小:
cookie 数据大小不能超过 4k。
sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到 5M 或更大。
有期时间:
localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
sessionStorage 数据在当前浏览器窗口关闭后自动删除。
cookie 设置的 cookie 过期时间之前一直有效,即使窗口或浏览器关闭
6. 如何实现浏览器内多个标签页之间的通
WebSocket、SharedWorker;也可以调用 localstorge、cookies 等本地存储方式;
7. script标签中的async、defer属性的作用
- Async: 可选属性。表示应该立即下载脚本,但不应妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本。
- Defer: 可选属性。标识脚本可以延迟到文档完全被解析和显示之后再执行。
2021 javascript 面试题汇总
1. this 都可以是哪些内容
对 C#、Java 等语言,this 就是当前对象,但是 javascript 不是,简单来说:
- 全局 this 是 window;
- 函数 this 是调用者;
- 构造函数的 this 是 new 之后的新对象,
- call 和 apply bind 的 this 第一个参数
2. 原型链, prototype
- 函数对象都包含 prototype 属性(函数的原型对象),其作用就是让该函数所实例化的对象们都可以找到公用的属性和方法;
- constructor 属性的含义就是指向该对象的构造函数
__proto__和 constructor 属性是对象所独有的;prototype 属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和 constructor 属性。- JavaScript 的每个对象都继承另一个父级对象,父级对象称为原型 (prototype) 对象。
- 每一个实例对象都有一个私有属性proto指向其构造函数的原型对象 prototype;该原型对象也会作为实例对象有一个私有属性proto,层层向上直到一个对象的原型对象值为 null。
- 当访问一个对象的属性或方法时,js 引擎会先查找该对象本身是否包含,如果没有,会去该对象的proto属性所指向的原型对象上找,如果没有,会继续向上一层找,直到某个对象的proto值为 null,这就是原型链。
- 每个构造函数都有一个 prototype 属性,指向另外一个对象,说明整个对象所有的属性和方法都会被构造函数所拥有。
3. new 操作符都做了什么
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一。
new Object()举例:
- 创建一个新对象
- 把新对象的原型(
__proto__)指向构造函数的 prototype - 把构造函数里的 this 指向新对象
- 返回这个新对象
4. Javascript 的内存回收机制
Javascript 内嵌了垃圾收集器,用来跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。垃圾收集器会按照固定的时间间隔,或代码执行中预定的收集时间,周期性地执行这一操作。
垃圾收集器必须跟踪哪个变量有用哪个变量无用,对于不再有用的变量打上标记,以备将来收回其所占用的内存。用于标识无用变量的策略通常有标记清除和引用计数两种。不同浏览器采用的策略不完全一致。
5. 内存泄漏的主要原因
- 缓存
- 队列消费不及时
- 作用域未释放
6. Javascript 性能优化
- 针对 js 方面的前端性能优化,可以 1)使用 ansyc/defer 加载;2)加载时,放到
</body>之前;3) 合并 JS 文件,并进行最小化处理;4)缓存;5)使用 CDN 网络;6)js 的 HTTP 压缩 - 删除未使用的功能性代码以及与之相关的代码, 多余的依赖库,被滥用的 npm 包。
- 减少作用域链上的查找次数(减少循环中的活动)。比如 for 循环,把 Array.length 赋值给一个变量,而不是每次比较都直接使用 length 属性;对页面元素进行操作时,取出复制给一个变量,每次操作都对这个变量进行操作,而不是每次操作都再重复取一下这个元素;
- 闭包导致的内存泄露。闭包可以保证函数内的变量安全,可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会被自动清除。我们需要手动销毁内存中的变量。(赋值为 null);
- 尽量少用全局变量,尽量使用局部变量。
- 减少不必要的变量
- 类型转换:把数字转换成字符串使用 number + “” 。 性能对比: (“” + ) > String() > .toString() > new String()
- 对字符串进行循环操作,譬如替换、查找,应使用正则表达式。因为本身 JavaScript 的循环速度就比较慢,而正则表达式的操作是用 C 写成的语言的 API,性能很好。
- 浮点数转换成整型使用 Math.floor()或者 Math.round()。parseInt()是用于将字符串转换成数字,Math 是内部对象,所以 Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。
- 使用 classname 代替大量的内联样式修改。
- 循环遍历,尽量少用 for in, 虽然代码易读,但性能很差。 尤其是遍历属性数量未知的情况下,少用。
7. ES6/ES7/ES8/… 新增的功能
7.1. ES6 (2015)
- 类
- 模块化
- 箭头函数
- 函数参数默认值
- 模板字符串
- 解构赋值
- 延展操作符
- 对象属性简写
- Promise
- Let与Const
7.2. ES7(2016)
- 数组includes()方法,用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。
- **指数运算符: a ** b指数运算符,它与 Math.pow(a, b)相同。
7.3. ES8(2017)
- async/await
- Object.values()
- Object.entries()
- String padding: padStart()和padEnd(),填充字符串达到当前长度
- 函数参数列表结尾允许逗号
- Object.getOwnPropertyDescriptors()
- ShareArrayBuffer和Atomics对象,用于从共享内存位置读取和写入
7.4. ES9(2018)
- 异步迭代
- Promise.finally()
- Rest/Spread 属性: ES2015引入了Rest参数和扩展运算符。三个点(…)仅用于数组。Rest参数语法允许我们将一个不定数量的参数表示为一个数组。
- 正则表达式命名捕获组
- 正则表达式反向断言
7.5. ES10新特性(2019)
- 行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
- 更加友好的 JSON.stringify
- 新增了Array的flat()方法和flatMap()方法
- 新增了String的trimStart()方法和trimEnd()方法
- Object.fromEntries()
- Symbol.prototype.description
- String.prototype.matchAll
- Function.prototype.toString()现在返回精确字符,包括空格和注释
- 简化try {} catch {},修改 catch 绑定
- 新的基本数据类型BigInt
- globalThis
- import()
- Legacy RegEx
- 私有的实例方法和访问器
7.6. ES11(2020)
- Nullish coalescing Operator(空值处理)
- import() 按需导入
7.7. ES12(2021)
- string增加replaceAll
- Promise.any, 收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise
- WeakRefs, 使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)
- 数字分隔符
- 逻辑运算符和赋值表达式
8. === VS ==
1 | 100 == "100"; // true |
很有意思吧, 看看这两个,是等效的:
1 | obj == null; |
9. 闭包
在 JavaScript 中,实现外部作用域访问内部作用域中变量的方法叫做闭包(closure)。这得益于高阶函数的特性:函数可以作为参数或者者返回值;
当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数,就是一个函数去访问了另外一个函数的中的变量的函数。
闭包作用:局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。延伸变量的作用范围。
闭包特点:占用更多内存;不容易被释放
闭包用法:变量既想反复使用,又想避免全局污染如何使用?
- 定义外层函数,封装被保护的局部变量。
- 定义内层函数,执行对外部函数变量的操作。
- 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
e.g.
1 | function outerMethod() { |
10. js 里的作用域是什么样子的?
大多数语言里边都是块作作用域,以{}进行限定,js 里边不是.js 里边叫函数作用域,就是一个变量在全函数里有效.比如有个变量 p1 在函数最后一行定义,第一行也有效,但是值是 undefined.
11. let 和 var 的区别
- var 声明变量可以重复声明,而 let 不可以重复声明,属于 TDZ 暂时性死区问题
- 作用域不同,var 是函数作用域,而 let 是块级作用域;
- var 可以在声明的上面访问变量,而 let 不存在变量提升;
12. 对 async 和 await 的理解
- async…await 是基于 promise 的 generator 语法糖,是用来解决异步的,它用来等待 promise 的执行结果,常规函数使用 await 没有效果;
- async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数
- 当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句;
13. promised 的三种状态
promise 有三种状态:pending/reslove/reject 。pending 就是未决,resolve 可以理解为成功,reject 可以理解为拒绝。
14. async/await 的优点
1)方便级联调用: 即调用依次发生的场景;
2)同步代码编写方式: Promise 使用 then 函数进行链式调用,一直点点点,是一种从左向右的横向写法;async/await 从上到下,顺序执行,就像写同步代码一样,更符合代码编写习惯;
3)多个参数传递: Promise 的 then 函数只能传递一个参数,虽然可以通过包装成对象来传递多个参数,但是会导致传递冗余信息,频繁的解析又重新组合参数,比较麻烦;async/await 没有这个限制,可以当做普通的局部变量来处理,用 let 或者 const 定义的块级变量想怎么用就怎么用,想定义几个就定义几个,完全没有限制,也没有冗余工作;
4)同步代码和异步代码可以一起编写: 使用 Promise 的时候最好将同步代码和异步代码放在不同的 then 节点中,这样结构更加清晰;async/await 整个书写习惯都是同步的,不需要纠结同步和异步的区别,当然,异步过程需要包装成一个 Promise 对象放在 await 关键字后面;
5)基于协程: Promise 是根据函数式编程的范式,对异步过程进行了一层封装,async/await 基于协程的机制,是真正的“保存上下文,控制权切换……控制权恢复,取回上下文”这种机制,是对异步过程更精确的一种描述;
6)async/await 是对 Promise 的优化: async/await 是基于 Promise 的,是进一步的一种优化,不过在写代码时,Promise 本身的 API 出现得很少,很接近同步代码的写法;
15. async/await 的写法
1 | async function testAsync() { |
16. apply、call 和 bind 的区别
这三者都是用来改变函数的 this 对象的指向的(也就是说要改变函数运行时的 context 即上下文),第一个参数都是 this 要指向的对象。
不同之处:
- call 接受连续参数,apply 接受数组参数。
- bind 功能和 call apply 差不多,区别在于 bind 返回的是一个改变 this 指向的新函数,这个函数不是立即执行函数(call apply 两个立即执行!)
17. 深拷贝的原理
操作的是值,取出对象的值,放到一个新的{},修改时不会影响到原数据;要完成对象的深拷贝需要使用递归遍历所有对象的属性进行赋值,也可以使用 JSON.stringfy 和 JSON.parse 操作。
18. set 和 Map 的区别
- Set 是一种类似数组的集合类型,它与数组不同的是,不允许存在重复数据;常用操作方法有:add,delete,has,clear 等;遍历使用 forEach;
- Map 是一种类似对象的集合类型,它与对象不同的是,key 可以接受对象类型,常用的操作方法有:set,get,has,delete 等;遍历使用 forEach
19. JS 实现双向绑定 (vue/angular 双向绑定的原理)
vue.js 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
Object.defineProperty( )内还包含一对儿 getter 和 setter 函数,它们被称作这个对象的访问器属性,这两个函数都不是必须的,只是在读取访问对象属性时,会调用 getter 函数,这个函数负责返回有效值,在写入对象属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据。
Angular, 采用脏检查的方式。基于 zone.js,重写 setTimeout, httpRequest 等方法,触发脏检查, 更新 dom 节点。
20. var 和 let 的区别
- 作用域不同, var 是函数作用域,let 是块作用域。
- 在函数中声明了 var,整个函数内都是有效的,比如说在 for 循环内定义的一个 var 变量,实际上其在 for 循环以外也是可以访问的
- 而 let 由于是块作用域,所以如果在块作用域内定义的变量,比如说在 for 循环内,在其外面是不可被访问的,所以 for 循环推荐用 let
- let 不能在定义之前访问该变量,但是 var 可以。 (var 可以提升变量作用域)
- let不能被重新定义,但是var是可以的。(重新定义var,执行时会忽略)
21. Javascript 如何实现继承?
1、构造继承
2、原型继承: Child.prototype = new Parent();
3、实例继承
4、拷贝继承
22. 变量提升
js 引擎首先在读取 js 代码时默认执行 2 个步骤:
- 解释(通篇扫描所有 js 代码,然后把所有声明(变量申明、函数声明)提升到对应作用域顶端)
- 执行(执行逻辑操作)
1 | // 例1. |
23. 函数提升
首先要知道函数定义有两种方式的,一种是函数定义表达式,一种是函数声明语句。
1 | // 函数定义表达式 |
1 | // 提升测试 |
重点:
- 函数提升针对是函数声明语句而言,其次变量提升只提升变量名而函数提升会提升整个函数体
- 优先级,函数提升会在变量提升的上面
24. 作用域
1 | // 例1 块级作用域, 不适合变量提升 |
25. 实现一个随机排序的程序
1 | function randomSort() { |
26. 实现一个函数,传入一个字符串,得到使用次数最多的字符,以及数量
1 | function getMostUsedChar(sourceString) { |
参考: