本系列主要整理博主2023牛客网秋招经纬恒润前端面经总结。
console.log(3 > 2 > 1);
console.log(1 < 2 < 3);
我们可以看一下它的输出结果:
我们可以分析一下,在js中0 == false和1 ==true是对的
当我们看3>2>1时,我们分成两个部分来看,3>2结果时true,也就等价于1,而1>1,结果当然是false
同理:
1<2<3,也分成两个部分,1<2,结果为true,1<3结果自然就是true了。
再看3<2<1,我们可以知道3<2结果是false,即0<1,结果必然是true
console.log(3 < 2 < 1); //true
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。
面向过程:简单来说面向过程,自顶向下,逐步细化!面向过程,就是按照我们分析好了的步骤,按部就班的依次执行就行了!所以当我们用面向过程的思想去编程或解决问题时,首先一定要把详细的实现过程弄清楚。一旦过程设计清楚,代码的实现简直轻而易举。
1、打开洗衣机
2、放入衣服
3、放入洗衣液
4、关上洗衣机
面向对象:所谓的面向对象,就是在编程的时候尽可能的去模拟真实的现实世界,按照现实世界中的逻辑去处理一个问题,分析问题中参与其中的有哪些实体,这些实体应该有什么属性和方法,我们如何通过调用这些实体的属性和方法去解决问题。
1、洗衣机.打开洗衣机
2、人.放衣服
3、人.放洗衣液
4、洗衣机.关上洗衣机
函数式编程:
1、函数式编程的显著特征-不可变|无副作用|引用透明
2、函数式编程的目标 - 模块化
3、将函数整合起来 - 高阶函数(Higher-order Functions)
总结:
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
不足:不易维护、不易复用、不易扩展
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
缺点:因为需要创建大量的类,性能不高,不适合对性能要求很苛刻的地方。
函数式编程
优点:变量不可变,引用透明,天生适合并发。表达方式更加符合人类日常生活中的语法,代码可读性更强。实现同样的功能函数式编程所需要的代码比面向对象编程要少很多,代码更加简洁明晰。函数式编程广泛运用于科学研究中,因为在科研中对于代码的工程化要求比较低,写起来更加简单,所以使用函数式编程开发的速度比用面向对象要高很多,如果是对开发速度要求较高但是对运行资源要求较低同时对速度要求较低的场景下使用函数式会更加高效。
缺点:由于所有的数据都是不可变的,所以所有的变量在程序运行期间都是一直存在的,非常占用运行资源。同时由于函数式的先天性设计导致性能一直不够。虽然现代的函数式编程语言使用了很多技巧比如惰性计算等来优化运行速度,但是始终无法与面向对象的程序相比,当然面向对象程序的速度也不够快。函数式编程虽然已经诞生了很多年,但是至今为止在工程上想要大规模使用函数式编程仍然有很多待解决的问题,尤其是对于规模比较大的工程而言。如果对函数式编程的理解不够深刻就会导致跟面相对象一样晦涩难懂的局面。
小结
函数式编程和面向对象编程各有利弊,一个语法更加自由,一个健壮性更好。作为程序员应该对两种编程方式都有所了解,不管是哪种方式,只要能够很好的解决当前的问题就是正确的方式,毕竟对于软件工程来说解决问题是最主要的,用的工具反而没有那么重要,就像对程序员来说语言不重要,重要的是解决问题的思想。
现在这两者的发展趋势是相互借鉴的,许多以面向对象作为基础的语言例如Java等都在新的版本中添加了对函数式编程的支持,而函数式编程则借鉴了一些在面向对象语言里用的一些编译技巧使得程序运行更快。
对函数式编程、面向对象和面向过程三者的理解
面试官科普:render层面、main层面、window
层面、浏览器层面
深拷贝与浅拷贝
实现深拷贝的几种方式:
1 递归的方法
function deepClone1(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if(obj && typeof obj === 'object'){
for(let key in obj){
if(obj[key] && typeof obj[key] === 'object'){
objClone[key] = deepClone1(obj[key]); //循环递归直到属性不为一个object为止
}else{
objClone[key] = obj[key];
}
}
}
return objClone;
}
let person1 = {
name:'园丁',
job:{
salary:50000,
address:'高新',
id:{
idCard:5003888
}
}
};
let person2 = deepClone2(person1);
console.log(person1);
console.log(person2);
let arr1 = [
1,
2,
[3,4],
5
];
let arr2 = deepClone1(arr1);
console.log(arr1);
console.log(arr2);
console.log(arr1 === arr2); //false
console.log(person1 === person2); //false
说明深拷贝得到的不是同一个对象
2.使用json对象的方法
// 通过json对象实现深拷贝
// JSON.stringify() 【从一个对象中解析出字符串】
// JSON.parse() 【从一个字符串中解析出对象】
function deepClone2(obj){
let _obj = JSON.stringify(obj);
console.log(_obj);
let objClone = JSON.parse(_obj);
console.log(objClone);
return objClone;
}
let person3= deepClone2(person1);
3.Object.assign()方法
// 这个方法去复制 对象只有一级属性的时候此方法为深复制
// 在有二级属性的时候 为浅复制二级属性
let person4 = Object.assign({},person1);
console.log(person4);
4.jq 的extend方法
let arr3 = $.extend(true,[],arr1);
console.log(arr3);
实现浅拷贝的几种方式:
(1) Object.assign()
var obj1 = {a: 1, b: 2};
var obj2 = Object.assign({}, obj1);
(2) 解构赋值
var obj1 = {a: 1, b: 2};
var obj2 = {...obj1};
浅拷贝实现原理:
function shallowCopy(obj) {
var target = {};
for (let i in obj) {
if (obj.hasOwnproperty(i)) {
target[i] = obj[i];
}
}
return target;
}
1、hash ——即地址栏URL中的#符号(此hsah 不是密码学里的散列运算)。
比如这个URL:http://www.abc.com/#/hello, hash 的值为#/hello。它的特点在于:hash 虽然出现URL中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。
2、history ——利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法。(需要特定浏览器支持)
这两个方法应用于浏览器的历史记录站,在当前已有的back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的URL,但你浏览器不会立即向后端发送请求。
history模式,会出现404 的情况,需要后台配置。
导航的意思就是路由的改变,在项目中我们routes里面会有很多个路由,我们在路由间进行切换的时候就是导航。我们可以在路由切换的过程中设置对于的路由导航守卫。对这个切换的行为做一个限制。比如说访问localhost:8080可以正常的访问首页,如果现在注册一个全局的前置守卫,通过的方法式在router的实例上,有一个beforeEach的方法:
全局的前置守卫:
const router = createRouter({ ... })
router.beforeEach((to, from) => {
// ...
// 返回 false 以取消导航
return false
})
这里面有三个参数
路由独享的守卫:
比如说在我们的用户路由中有过一个beforeEnter属性(只在进入路由时触发),这个属性呢,它可以是函数,他也有三个参数,分别是to,from,next;当next为next(false)时页面不显示
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
它还可以应用在组件里面,在组件对象里面有一个特殊的钩子叫做beforeRouteEnter()函数,同样接收上面三个参数,next(false)同样页面不显示。
离开某个组件的路由:
有一个函数叫做beforeRouteLeave()同样三个参数
完整的导航解析流程
Vue.js 基础总结-常用指令用法
在 Vue 中能监听数据变化的核销原因是利用 Object.defineProperty 方法在对象类型的数据的描述符属性上定义 get 和 set 属性,其属性值分别是 getter 和 setter 函数,当读取对象类型的数据时会触发 getter 函数,当修改对象类型的数据时会触发 setter 函数,这样就可以对对象类型的数据进行监听。又因为 Object.defineProperty 方法对数组类型的数据不起作用,则通过劫持数组实例方法的方式来监听数组类型的数据。另外 Object.defineProperty 方法在 IE8 及以下浏览器中不兼容,这也是为什么 Vue.js 不能兼容 IE8 及以下浏览器的原因。
在初始化数据时调用 observe 函数开始对数据进行监听,在 Observer 构造函数对数组类型和对象类型的数据用不同的逻辑进行监听,在 defineReactive 函数中利用 Object.defineProperty 方法把对象变成响应式对象实现了监听,再利用 observe 函数、Observer 构造函数、defineReactive 函数,相互调用形成一个递归调用,保证了一个对象无论多么复杂,其子属性都能被深度遍历监听到。
响应式原理
双向数据绑定的v-model的实现原理:(实际上是语法糖)
<template>
<div class="hello">
<!--双向数据绑定的v-model的实现原理-->
<!--两个指令的结合: v-bind,v-on -->
<!-- Input事件, 当输入框的内容发生改变,触发事件,拿到事件对象-->
<input type= "text" v-bind:value="message" v-on:input="valueChange" ></input>
<input type="text" v-model="massage">
<p>{{message}}</p>
</div>
</template>
<scrlpt>
export default {
name: "HelloWorld",
data(){
return{
message:'你好'
}
},
methods:{
valueChange: function(e){
console.1og(e);
this.message=e.target.value
}
}
};
</script>
双向数据绑定的核心方法是:Object.defineProperty,来实现对属性的劫持,达到监听数据的目的。
在官方文档中明确指出v-for和v-if不建议一起使用。 原因:v-for比v-if优先级高,所以使用的话,每次v-for都会执行v-if,造成不必要的计算,影响性能,尤其是当之需要渲染很小一部分的时候。 如上述情况,即使有100个user中只有一个需要使用v-if,也需要整个循环数组,这在性能上是极大的浪费。
解决方案:
1.在循环外部可以在外层嵌套已成template(页面不生成dom节点)、在外层v-if判断、然后在进行使用v-for循环
2、如果条件出现在循环内部、可以通过计算属性computed提前过滤掉那些不需要展示的数据
虚拟DOM节点, 它通过 JS 的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点。
在Javascript对象中,虚拟DOM 表现为一个 Object对象。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性。
为什么需要虚拟dom?
当你在一次操作时,需要更新10个DOM节点,浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程.
而通过VNode,同样更新10个DOM节点,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,避免大量的无谓计算
var、let、const三者区别
ES6 Set 和 Map 数据结构
箭头函数与普通函数的区别
例子:某一天要出门办事,但是我还要买菜做饭送到老婆单位,这时我就可以把买菜做饭的事情交给保姆,我会告诉她:
你先去超市买菜。
用超市买回来的菜做饭。
将做好的饭菜送到老婆单位。
送到单位后打电话告诉我。
上面三步都是需要消耗时间的,我们可以理解为三个异步任务。利用 Promise 的写法来书写这个操作:
function 买菜(resolve,reject) {
setTimeout(function(){
resolve(['西红柿'、'鸡蛋'、'油菜']);
},3000)
}
function 做饭(resolve, reject){
setTimeout(function(){
//对做好的饭进行下一步处理。
resolve ({
主食: '米饭',
菜: ['西红柿炒鸡蛋'、'清炒油菜']
})
},3000)
}
function 送饭(resolve,reject){
//对送饭的结果进行下一步处理
resolve('老婆的么么哒');
}
function 电话通知我(){
//电话通知我后的下一步处理
给保姆加100块钱奖金;
}
好了,现在我整理好了四个任务,这时我需要告诉保姆,让他按照这个任务列表去做。这个过程是必不可少的,因为如果不告诉保姆,保姆不知道需要做这些事情
// 告诉保姆帮我做几件连贯的事情,先去超市买菜
new Promise(买菜)
//用买好的菜做饭
.then((买好的菜)=>{
return new Promise(做饭);
})
//把做好的饭送到老婆公司
.then((做好的饭)=>{
return new Promise(送饭);
})
//送完饭后打电话通知我
.then((送饭结果)=>{
电话通知我();
})
async和await
类是用于创建对象的模板。
我们使用 class 关键字来创建一个类,类体在一对大括号 {} 中,我们可以在大括号 {} 中定义类成员的位置,如方法或构造函数。
每个类中包含了一个特殊的方法 constructor(),它是类的构造函数,这种方法用于创建和初始化一个由 class 创建的对象。
创建一个类的语法格式如下:
class ClassName {
constructor() { ... }
}
实例:
class Runoob {
constructor(name, url) {
this.name = name;
this.url = url;
}
}
CSS:
盒子水平垂直居中的五种方式
Flex 布局语法
Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
flex 属性是 flex-grow、flex-shrink 和 flex-basis 属性的简写属性。
注意:如果元素不是弹性盒模型对象的子元素,则 flex 属性不起作用。
flex-grow 属性用于设置或检索弹性盒子的扩展比率。
注意:如果元素不是弹性盒对象的元素,则 flex-grow 属性不起作用。
flex-grow: number|initial|inherit;
实现css两栏布局、右侧自适应、三栏布局中间自适应
BFC是什么,如何触发,能解决什么问题?
谈谈你对BFC的理解?
兴业数金面试之隐藏元素
计网:
tcp/ip协议三次握手、四次挥手之http报文
浏览器缓存机制
进程与线程
数组去重
数组&链表
JavaScript | TypeScript |
---|---|
支持动态网页内容 | 为帮助项目解决代码复杂性而创建的JavaScript超集 |
解释性语言,因此只有在运行时才会发现错误 | 在编译期间可以检测和修复错误 |
弱类型,无法选择静态类型 | 强类型,支持静态和动态类型 |
可以直接在浏览器中使用 | 将代码转换为 JS 以实现浏览器兼容性 |
不支持模块、泛型和接口 | 支持模块、泛型和接口 |
不支持可选参数 | 可选参数可以添加到函数中 |
使用数字和字符串作为接口 | 数字和字符串是对象 |
大量社区支持,包括大量文档 | 社区支持正在增长,不像以前那么强大 |
不支持原型设计 | 原型设计是一个可行的选择 |
不需要事先的脚本知识 | 学习和编码需要时间,需要脚本知识 |
无需设置构建环境 | 对于静态类型定义,需要适当地构建设置(npm 包) |
你真的知道 JavaScript 和 Typescript 之间的区别吗?
Three.js是一个跨浏览器的脚本,使用JavaScript函数库或API来在网页浏览器中创建和展示动画的三维计算机图形。 Three.js使用WebGL。 源代码托管在GitHub。Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多、使用最广泛的三维引擎,本文将简要介绍three.js的基础使用知识。
three.js的三要素是Scene(场景),Camera(相机)和Renderer(渲染器),缺一不可。
SVG 是使用 XML 来描述二维图形和绘图程序的语言。
canvas简介:web上的动画都是Flash。比如动画广告、游戏等等,基本上都是Flash实现的。Flash是有缺点的,比如需要安装Adobe Flash Player,漏洞多,重量比较大。卡顿和不流畅等等。HTML5提出了一个新的canvas标签,彻底颠覆了Flas 的主导地位。无论是广告、游戏都可以使用canvas实现了,Canvas是一个轻量级的画布,我们使用Canvas进行JavaScript的编程,不需要增加额外的插件,性能也很好,不卡顿,在手机中也很流畅
canvas动画的原理:清屏-更新-渲染,因为canvas绘图完成,就被像素化了,所以不能通过style.left方法进行修改,而是必须要重新绘制
Vue生命周期函数
Vue中的几种传值方式
webpack
前端性能优化 - 首屏渲染优化实现及其必要性
关于首屏优化,我做了哪些
说说作用域和闭包
水平垂直居中
水平居中:
垂直居中:
行高:height:100px;line-height:100px;
display:table-cell
这次彻底搞懂Vuex!
这么多状态码大体分为五类,分别是以1到5开头,下面我们来说下这五类的大体含义:
链接:https://juejin.cn/post/7110782288979296264
1、Cookie
cookie 是存储在本地的数据,本身非常小,它的大小限制为4KB左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的。
2、LocalStorage
仅在客户端保存(即浏览器),不参与和服务器的通信;没有时间限制,即使浏览器关闭,数据依然存在;
3、sessionStorage与localStorage的接口类似,但保存数据的生命周期与localStorage不同,做过后端的同学都知道Session这个词,翻译过来就是会话。而sessionStorage是前端的一个概念。它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但是关闭后,sessionStorage中的数据就会被清空。
cookie、session、sessionStorage、localStorage区别
let和const、数组和对象的解构赋值、模板字符串、对象的简化写法、箭头函数、函数参数可以设
置默认值、rest参数、扩展运算符、Symbol和BigInt、Promise、set。
ES6 常用知识点总结
GET
GET方法请求一个指定资源的表示形式,使用GET的请求应该只被用于获取数据
POST
POST方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用
本质上都是TCP链接,并无差别
但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中会体现出一些区别
区别如下:
法一:
function sumFunc(num, start) {
num += start
start++;
if (start > 100) {
console.log(num);
return num
}
else {
sumFunc(num, start)
}
}
sumFunc(0, 1);
法2:
function num(n) {
if (n == 1) return 1;
return num(n - 1) + n;
}
let sum = num(100);
console.log(sum, "sum")