疫情期间,一个前端妹子的面试小记(含答案)(1),CSS颜色基本样式

currentTarget是事件处理程序注册的元素

  • document.ready和window.onload区别
    document.ready是dom树加载后执行,而window.onload是整个页面资源加载完后执行,所以document.ready比window.onload先执行

  • 事件流
    DOM2事件流分为三个部分:事件捕获、处于目标、事件冒泡。

事件冒泡是指事件从执行的元素开始往上层遍历执行

事件捕获是指事件从根元素开始从外向里执行

Title click Me

点击按钮后,事件冒泡的执行顺序是:button->body->html->document

事件捕获的执行顺序则相反:document->html->body->button

  • doctype作用,严格模式和混合模式的区别
    <!doctype>声明位于文档的最前面,在html之前显示。用于告诉浏览器的解析器,用什么文档类型规范来解析文档。严格模式默认用浏览器支持的最高版本解析,混合模式以宽松的向后兼容的方式解析,doctype不存在或格式不正确时会让文档以混杂模式呈现

水平垂直居中
//方法一
display:flex;
justify-content:center;
align-items:center;

//方法二
display:table;
vertical-align:center;

//方法三:适用于已知宽高且父元素定位不为static
postion:absolute;
width:100px;
height:100px;
top:50%;
left:50%;
margin:-50px 0 0 -50px;

//方法四
position:absolute;
top:50%;
left:50%;
transform:translateY(-50%) translateX(-50%);

//方法五:适用于行内元素
display:inline-block;
width:100px;
height:100px;
text-align:center;
line-height:100px;

//方法六:适用于块级元素
display:block;
height:100px;
margin:0 auto;
line-height:100px;

回流和重绘区别
回流:当渲染树中元素尺寸、结构或者某些属性发生变化时,浏览器重新渲染部分或全部页面的情况叫回流。下列元素改变引发回流:

  • getBoundingClientRect()

  • scrollTo()

  • scrollIntoView()或者scrollIntoViewIfneeded

  • clientTop、clientLeft、clientWidth、clientHeight

  • offsetTop、offsetLeft、offsetWidth、offsetHeight

  • scrollTop、scrollLeft、scrollWidth、scrollHeight

  • getComputedStyle()

重绘:当页面中元素样式变化不会改变它在文档流中的位置时,即不会使元素的几何属性发生变化,浏览器会将新样式赋给它并重新绘制页面(比如color、backgroundColor)

频繁回流和重绘会引起性能问题

避免方法:

  • 减少table布局使用

  • 减少css表达式的使用(如calc())

  • 减少DOM操作,用documentFragment代替

  • 将元素设为display:none;操作结束后把它显示回来,因为display:none不会引发回流重绘

  • 避免频繁读取会引发回流重绘的元素,如果需要最好是缓存起来

  • 对复杂动画元素使用绝对定位,使它脱离文档流

  • 减少使用行内样式

js部分

  • setTimeout、setInterval区别
    两者都是定时器,设定一个150ms后执行的定时器不代表150ms后定时器会执行,它表示代码在150ms内会被加入队列,如果这个时间点队列没有其他逻辑在执行,表面上看代码在精确时间执行了。在队列中有其他逻辑时,代码等待时间会超过150ms

setTimeout 只执行一次

setInterval 执行多次,属于重复定时器

  • 防抖节流
    节流:多次触发事件时,一段时间内保证只调用一次。以动画为例,人眼中一秒播放超过24张图片就会形成动画,假设有100张图片,我们一秒播放100张过于浪费,一秒播放24张就够了。

防抖:持续触发事件后,时间段内没有再触发事件,才调用一次。以坐电梯为例,电梯10s运行一次。如果快要运行时进来一个人,则重新计时。

//节流
function throttle(fn,delay) {
let timer=null
return function () {
if(!timer){
timer=setTimeout(()=>{
fn.call(this,arguments)
timer=null
},delay)
}
}
}
//防抖
function debounce(fn,delay) {
let timer=null
return function () {
if(timer){
clearTimeout(timer)
}
timer=setTimeout(()=>{
fn.call(this,arguments)
},delay)
}
}

深浅拷贝
浅拷贝:

concat()

Object.assign()

slice()

手写

function shallowCopy(obj){
if(typeof obj===‘function’&& obj!==null){
let cloneObj=Array.isArray(obj)?[]:{}
for(let prop in obj){
if(obj.hasOwnProperty(prop)){
cloneObj[prop]=obj[prop]
}
}
return cloneObj
}
else{
return obj
}
}

JSON.stringfy(JSON.parse())

上面的方法不能解决循环引用,也不能显示函数或undefined

手写深拷贝

var deepClone=(obj,map=new WeakMap())=>{
if(map.get(obj)){
return obj
}

let newObj;
if(typeof obj===‘object’&& obj!==null){
map.set(obj,true)
newObj=Array.isArray(obj)?[]:{};
for(let item in obj){
if(obj.hasOwnProperty(item)){
newObj[item]=deepClone(obj[item])
}
}
return newObj;
}
else {
return obj;
}
};

继承
我觉得没啥好说的,红宝书里介绍的挺详细了,不过es6的class多了个extends

//原型链继承
function Parent(){
this.property=true;
}
Parent.prototype.getValue=function(){
return this.property;
}
function Son(){
this.subProperty=false;
}
Son.prototype=new Parent();
let instance=new Son();

原型链继承继承了原型的属性和方法,但是有几个缺点:

原型链中包括引用类型的值时,会被所有实例共享

不能实现子类向超类的构造函数中添加属性

由此产生了借用构造函数继承,解决了原型链继承的缺点,它自身又有缺点:不能实现函数复用

//借用构造函数继承
function Parent(){
this.property=true;
}
function Son(){
Parent.call(this);
}
复制代码//组合继承
function Parent(){
this.property=true;
this.colors=[‘red’,‘purple’,‘orange’]
}
Parent.prototype.getPro=function(){
return this.property;
}
function Son(property,name){
Parent.call(this,property);
this.name=name;
}
Son.prototype=new Parent()

组合继承避免了原型链和借用构造函数的缺陷,是最常用的继承之一

//原型继承
var a = {
friends : [“yuki”,“sakura”]
};
var b = Object.create(a);
b.friends.push(“ruby”);
var c = Object.create(a);
c.friends.push(“lemon”);
alert(a.friends);//“yuki,sakura,ruby,lemon”

原型继承缺点跟原型链继承一样,也是引用类型的属性会被所有实例共享

//寄生式继承,可以类比设计模式的工厂模式
function createAnother(obj){
var clone = object(obj);
clone.sayHi = function(){
console.log(“hello”);
};
return clone;
}

寄生式继承不能做到函数复用

//寄生组合式继承
function Parent(name){
this.name=name;
this.colors=[‘red’,‘white’,‘gray’]
}
Parent.prototype.getName=function(name){
this.name=name
}
function Son(name,age){
Parent.call(this,name);//第二次调用Parent()
this.age=age;
}
Son.prototype=new Parent()//第一次调用Parent()
Son.prototype.constructor=Son;
Son.prototype.getAge=function(){
return this.age;
}

寄生组合式继承避免了在子实例上创建多余的属性,又能保持原型链不变,还能正常使用instanceof()和isPrototypeOf(),是最理想的继承方式。

es6方法的继承:通过extends实现

class Parent(){
constructor(){}
}
class Son extends Parent(){
constructor(){
super()
}
}

  • 了解的es6新特性
    说的越多越好,比如Promise,箭头函数、数组扩展:includes()、find()、findIndex()…,Symbol、Map、Set… 我就不罗嗦了,看阮一峰的ES6就好了

  • es6的class的es5的类有什么区别
    1.es6 class内部定义的方法都是不可枚举的
    2.es6 class必须用new调用
    3.es6 class不存在变量提升
    4.es6 class默认使用严格模式
    5.es6 class子类必须在父类的构造函数中调用super(),才有this对象;而es5是先有子类的this,再调用父类的方法应用再在this上面

数组去重

//方法一 使用ES6的Set
function filterArr(arr) {
return new Set(arr)
}
//方法二:filter+indexOf()判断,如果数字不是第一次出现则被过滤
function filterArr2(arr){
let newArr=arr.filter((item,index)=>{
return arr.indexOf(item)===index
})
console.log(newArr)
}
//方法三:双重for循环
function filterArr3(arr){
let isRepeat,newArr=[];
for(let i=0;i<arr.length;i++){
isRepeat=false
for(let j=i+1;j<arr.length;j++){
if(arr[i]===arr[j]){
isRepeat=true
break
}
}
if(!isRepeat){
newArr.push(arr[i])
}
}
return newArr
}
//方法四:哈希表
function filterArr4(arr){
let seen={}
return arr.filter(function (item) {
return seen.hasOwnProperty(item)?false:(seen[item]=true)
});
}
//方法五:sort排序,相同的数字会排在相邻n个位置
function filterArr5(arr){
let lastArr=[]
const newArr=arr.sort((a,b)=>{
return a-b
})
for(let i=0;i<newArr.length;i++){
if(newArr[i]!==newArr[i+1]){
lastArr.push(newArr[i])
}
}
return lastArr
}

this
this绑定函数的执行上下文,谁调用它,它就指向谁。分为默认绑定、显式绑定、隐式绑定、apply/call/bind绑定、new绑定和箭头函数绑定

默认绑定:严格模式下this指向undefined,非严格模式this指向window

call、apply、bind都可以改变this的指向,但是apply接收参数数组,call接收的是参数列表 bind接收的是参数列表,但是apply和call调用就执行,bind需要手动执行

箭头函数绑定:箭头函数的this是父作用域的this,不是调用时的this,其他方法的this是动态的,而箭头函数的this是静态的

window.name=‘a’
const obj={
name:‘b’,
age:22,
getName:()=>{
console.log(this)
console.log(this.name)
},
getAge:function(){
setTimeout(()=>{
console.log(this.age)
})
}
}
obj.getName();//window a
obj.getAge();//22

优先级:箭头函数>new绑定>显示绑定/apply/bind/call>隐式绑定>默认绑定

箭头函数和普通函数区别
箭头函数没有prototype,所以箭头函数本身没有this

箭头函数的this指向在定义的时候继承自外层第一个普通函数的this

箭头函数没有arguments,普通函数有

使用new调用箭头函数会报错

不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

new的原理
var obj={};
obj.proto=F.prototype;
F.call(obj);

创建一个空对象

this变量引用该对象,同时还继承了这个函数的原型

属性和方法被加入到引用的对象里

新创建的对象由this引用,最后隐式返回this

浏览器事件循环和node事件循环
浏览器事件循环:

同步任务在主线程执行,在主线程外还有个任务队列用于存放异步任务

主线程的同步任务执行完毕,异步任务入栈,进入主线程执行

上述的两个步骤循环,形成eventloop事件循环 浏览器的事件循环又跟宏任务和微任务有关,两者都属于异步任务。

js异步有一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入任务队列,再执行微任务,将微任务放入任务队列,他俩进入的不是同一个任务队列。往外读取的时候先从微任务里拿这个回调函数,然后再从宏任务的任务队列上拿宏任务的回调函数

宏任务:

script

定时器 setTimeout setInterval setImmediate

微任务:

promise

process.nextTick()

MutationObserver

node事件循环:

timer阶段

I/O 异常回调阶段

空闲预备阶段

poll阶段

check阶段

关闭事件的回调阶段

  • 手写系列
    远程面试没考手写,但我觉得还是要会

  • 手写promise
    我的是简易版本

function myPromise(executor) {
let self=this;
self.status=‘pending’;
self.value=undefined;
self.reason=undefined;

function resolve(value) {
if(self.status===‘pending’){
self.value=value
self.status=“resolved”
}
}
function reject(reason) {
if(self.status===‘pending’){
self.reason=reason
self.status=status
}
}
try{
executor(resolve,reject)
}
catch (e) {
reject(e)
}
}

手写bind
Function.prototype.myBind=function(context,…args){
const fn=this
args=args?args:[]
return function newFn(…newFnArgs) {
if(this instanceof newFn){
return new fn(…args,…newFnArgs)
}
return fn.apply(context,[…args,…newFnArgs])
}
}

手写call、apply
Function.prototype.myCall=function(context,…args){
context=context||window
args=args?args:[]
const key=Symbol()
context[key]=this
const result=contextkey//通过隐式绑定的方式调用函数
delete context[key]//删除添加的属性
return result//返回函数调用的返回值
}

Function.prototype.myApply=function(context,args){
context=context||window
args=args||[]
const key=Symbol()
context[key]=this
const result=contextkey
delete context[key]
return result
}

  • vue部分
    vue的双向绑定原理
    vue的双向绑定是通过数据劫持和发布者-订阅者模式实现的,数据劫持又是通过Object.defineProperty()实现的

Object.defineProperty(data,‘a’,{
enumerable:true,//是否可枚举
writable:true,//是否可写
configurable:true,//是否可配置
get(){
return this.a//读取data对象的a属性时,触发get方法
},
set(val){
this.a=val;//修改data对象的a属性时,触发set方法
}
})

mvvm的数据变化更新视图,是通过Object.defineProperty()实现的;视图更新变化数据,是通过事件监听实现的。

发布者-订阅者的实现过程:

  1. 实现一个监听器Observer,劫持并监听所有属性,如果有变化,就通知订阅者
  2. 实现一个订阅者Watcher,收到属性的变化通知并执行响应的函数,从而更新视图
  3. 实现一个解析器Compiler,可以扫描并解析每个节点的相关指令,初始化模板数据和对应的订阅器

vue的指令有哪些
v-if 用于条件渲染
v-show 用于条件渲染,两者的区别请参考下一个问题
v-for 用于列表渲染
v-on 监听事件
v-bind 动态绑定
v-html 渲染html元素
v-model 数据双向绑定

v-if和v-show区别
v-if 是惰性的,只有条件为真时才会切换,为假时什么也不做。确保切换过程中的事件监听器和子组件适当的被销毁和重建,适用于运行条件很少改变的场景。v-show 不管条件是否为真,总是会被渲染,适用于频繁切换的场景

v-for和v-if为什么不能放于同一级
v-for优先级高于v-if,放于同级可能会重复渲染两次v-if,建议把v-for放于v-if所在的外层元素

nextTick
原理:eventloop事件循环

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

v-for中key的原理
key 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。不指定key时,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试
就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。

vue的生命周期
1.beforeCreate
初始化界面前
2.created
初始化界面后,拿到data,props,methods、computed和watch
3.beforeMount
渲染dom前
4.mounted
渲染dom后,拿到$el
5.beforeUpdate
更新前
6.updated
更新后,拿到更新后的dom
7.beforeDestroy
卸载组件前
8.destroyed
卸载组件后
9.activated
被 keep-alive 缓存的组件激活时调用
10.deactivated
被 keep-alive 缓存的组件停用时调用
11. errorCaptured
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例
以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

父子组件通信
父传子:prop

子传父:$emit

父子通信:

  • eventbus
  • vuex
  • ref结合$parent或 $children

跨层级较多:provide/inject

computed和watch区别
computed用于计算属性,只有它依赖的值改变才会触发,且这个值有缓存

watch用于监听一个属性的变化,属性变化就会触发

vnode的diff算法原理
虚拟dom是对真实dom的一种映射,新旧Vnode比较同层级的节点,然后根据两者的差异只更新有差异的部分,生成新的视图,而不是对树进行逐层搜素遍历,因此时间复杂度是O(n)。虚拟dom可以减少页面的回流和重绘,提升性能

webpack相关
webpack运行流程
分为初始化、编译、输出三个阶段.

  • 初始化:

从配置文件和shell文件读取、合并参数;

加载plugin

实例化compiler

  • 编译

从entry发出,针对每个module串行调用对应loader翻译文件内容

找到module依赖的module,递归进行编译处理

  • 输出:

把编译后module组合成chunk

把chunk转换成文件,输出到文件系统

  • webpack性能优化
    优化可以从两个方面考虑,一个是优化开发体验,一个是优化输出质量。

  • 优化开发体验

缩小文件搜索范围。涉及到webpack如何处理导入文件,不再赘述,不会的可以自行搜索。由于loader对文件转换操作很耗时,应该尽量减少loader处理的文件,可以使用include命中需要处理的文件,缩小命中范围。

使用DllPlugin,提升构建速度

使用happyPack开启多线程

使用UglifyJs压缩代码(只支持es5),uglifyes支持es6,两个插件不能同时开启。使用ParalellUgifyPlugin开启多个子进程压缩,既支持UglifyJs又支持uglifyes

使用自动刷新:监听到代码改变后,自动编译构建可运行代码并刷新页面

开启模块热替换:在不刷新网页的同时实现实时预览
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

正值金三银四招聘旺季,很多小伙伴都询问我有没有前端方面的面试题,特地整理出来赠送给大家!

资料领取方式:点击这里前往免费获取

PO等大厂,18年进入阿里一直到现在。**

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-BxVK3k4C-1712379657897)]

[外链图片转存中…(img-iFnfoEKc-1712379657897)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-yCgwme22-1712379657898)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

正值金三银四招聘旺季,很多小伙伴都询问我有没有前端方面的面试题,特地整理出来赠送给大家!

资料领取方式:点击这里前往免费获取

前端资料图.PNG

  • 14
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值