前端实习面试经验汇总(不定期更新)

第一波前端面试比想象中问的面比较小,主要集中在js,尤其是es6的一些特性上,还有就是项目经验,项目中展开的问题。可能是这些公司对实习生要求没那么高?这次没有问到网络相关的,比如tcp、http啥的,不过感觉以后不会这么简单。

有鱼(一面挂)

1.promise和settimeout

笔试,面试都有问到
主要是给一道带有promise和settimeout方法的程序题,手写输出的顺序,当时给我的是这道题

Promise.resolve().then(()=>{console.log('A');setTimeout(()=>{console.log('B')},0)});console.log('C');setTimeout(()=>{console.log('D')Promise.resolve().then(()=>{console.log('E');})},0)

answer : CADEB

现在看很简答的一道题,只要搞清楚事件循环和宏任务微任务的概念就很好懂,这里推荐
2分钟了解 JavaScript Event Loop | 面试必备
这个视频,很短很易懂。

2.深拷贝与浅拷贝

说实话这是我当时第一次接触的概念,我完全不知道这俩是啥意思。所以后来直接挂了

实际上就是

假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。

如何实现深拷贝呢?

  1. 递归赋值,即拷贝对象各个层级的属性,赋给一个全新的对象
function deepClone(obj){let objClone = Array.isArray(obj)?[]:{};if(obj && typeof obj==="object"){for(key in obj){if(obj.hasOwnProperty(key)){//判断ojb子元素是否为对象,如果是,递归复制if(obj[key]&&typeof obj[key] ==="object"){objClone[key] = deepClone(obj[key]);}else{//如果不是,简单复制objClone[key] = obj[key];}}}}return objClone;
}
  1. 利用JSON的stringfy和parse方法实现
function deepClone(obj){let _obj = JSON.stringify(obj),objClone = JSON.parse(_obj);return objClone
}
  1. JQ的extend方法
$.extend( [deep ], target, object1 [, objectN ] )

deep表示是否深拷贝,true为深拷贝,false为浅拷贝

target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。

object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。

let a=[0,1,[2,3],4],b=$.extend(true,[],a);
  1. lodash中的 _.cloneDeep(value)
    详情见该库

3.vue组件间中的通信方式

老生常谈的东西,包括但不仅限于props/emit、vuex、事件总线、parent\children(感觉不怎么用?)

总结:人生第一次面试,直接一个白给开局

简巨(一面挂)

1.如何给string加一个getlength方法,用于获取字符串中数字个数(原型链)

 String.prototype.getNumberCount = function()  {var reg = /\d/gvar res = this.match(reg)return res.length}

注:原型函数不能用箭头函数声明

原因:this指向问题
因为箭头函数的this是window,function的this是string本身

2.v-show和v-if的区别

相同点:v-show和v-if都能控制元素的显示和隐藏。

不同点:

  1. 实现本质方法不同
    v-show本质就是通过设置css中的display设置为none,控制隐藏
    v-if是动态的向DOM树内添加或者删除DOM元素
  2. 编译的区别
    v-show其实就是在控制css
    v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件
  3. 编译的条件
    v-show都会编译,初始值为false,只是将display设为none,但它也编译了
    v-if初始值为false,就不会编译了
  4. 性能
    v-show只编译一次,后面其实就是控制css,而v-if不停的销毁和创建,故v-show性能更好一点。
    注意点:因为v-show实际是操作display:" "或者none,当css本身有display:none时,v-show无法让显示
    总结:如果要频繁切换某节点时,使用v-show(无论true或者false初始都会进行渲染,此后通过css来控制显示隐藏,因此切换开销比较小,初始开销较大),如果不需要频繁切换某节点时,使用v-if(因为懒加载,初始为false时,不会渲染,但是因为它是通过添加和删除dom元素来控制显示和隐藏的,因此初始渲染开销较小,切换开销比较大)

3.闭包

闭包指的是有权访问另一个函数作用域中变量的函数,常见的方式就是在函数中创建函数,借此突破作用链域。

4.vue单元测试

单元测试(unit testing),是指对代码中的最小可测试单元进行检查和验证。单元就是人为规定的最小的被测功能模块,可能是一个单一函数或方法、一个完整的组件或类。

组件的单元测试有很多好处:
提供描述组件行为的文档
节省手动测试的时间
减少研发新特性时产生的 bug
改进设计
促进重构
自动化测试使得大团队中的开发者可以维护复杂的基础代码

5.var和let的区别,var有啥问题,let怎么解决的

var和let区别之前写过 ——>var、let、const的区别
这里主要说下var存在的问题:(let全部给予了解决)

  1. var定义的变量没有块作用域。let只有块级作用域
  for (let i =0 ; i<10 ; i ++){}console.log(i);//undefinedfor( var i = 0 ; i<10 ; i++){}console.log(i);//10
  1. var定义的全局变量会自动添加全局window对象的属性。这个var、let、const的区别中有提到,delete不掉

  2. var存在变量提升,let没有,var后声明的会输出undefined,let后声明的会报Cannot access ‘xxx’ before initialization

总结: 简巨好像只要当年毕业的,农业相关的公司,hr直说了996,牛

和风天气(没面挂)

总结: 他们只要6个月+的md,甚至都没给我做题的机会淦

联想(联想研究院)offer

总结: 一共进行了两次电话面试,主要都是在围绕项目经验在问,问的比较杂,比较像即兴提问,还问了爬虫的事情。
工作时间早9晚6,弹性,200一天。

爱奇艺(一面挂)

  1. Number(‘1a’) => 输出什么
    输出NAN
    (我当时说的输出1,上来就一个白给)
    如果要输出1的话,得是parseInt(‘1a’)
  2. vue 父子组件加载顺序

父 beforeCreate
父 created
父 beforeMount
子1 beforeCreate
子1 created
子1 beforeMount
(子2 beforeCreate
子2 created
子2 beforeMount…如果有多个孩子以此类推)
子1 mounted
(子2 mounted…如果有多个孩子以此类推)
父 mounted

  1. 现有ABC三类,C类的实例c要有AB的全部属性和方法,如何做到
    JS中类的继承主要有6种:
    例——
function Person(name){this.name = name ;this.callname = function(){alert(this.name)}}Person.prototype.age = 10const person = new Person()person.school = 'g'

1.原型链继承

 //原型链继承function MT(){this.name = 'mt'}MT.prototype = new Person() //让新实例的原型等于父类的实例。const mt = new MT()console.log(mt.name); //mtconsole.log(mt.age); // 10console.log(mt instanceof Person);//true

实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)
缺点:1. 新实例无法向父类构造函数传参。
   2. 继承单一。
   3. 所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)

2.借用构造函数继承

 //借用构造函数继承function test2(){Person.call(this,'person') //用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))this.age = 12 }var t2 = new test2()console.log(t2.name);//personconsole.log(t2 instanceof Person);//false

解决了原型链继承缺点,可以继承多个构造函数属性(call多个)。在子实例中可向父实例传参。(this,‘person’)
缺点:1. 只能继承父类构造函数的属性。没有继承父类原型的属性。
   2. 无法实现构造函数的复用。(每次用每次都要重新调用)
   3. 每个新实例都有父类构造函数的副本,臃肿。

3.组合继承(组合原型链继承和借用构造函数继承)(常用)

  //组合继承function mix(name){Person.call(this,name)}mix.prototype = new Person()var m = new mix('mix')console.log(m.name);//mixconsole.log(m.age);//10

结合了两种模式的优点,传参和复用,可以继承父类原型上的属性,可以传参,可复用。每个新实例引入的构造函数属性是私有的。
缺点: 调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

这道题可以让B成为A的原型链上的实例B.protoype = new A() ,再让C成为B原型链上的实例 C.prototype = new B()
也可以让C在函数内部call这两个类 A.call() B.call()

  1. Vue生命周期
    Vue官方生命周期图

  2. Vue通信方式
    上面有提到

  3. 箭头函数和普通函数的区别

    1. this指向不一样:
      箭头函数的this默认指向定义它时,所处上下文的对象的this指向。即ES6箭头函数里this的指向就是上下文里对象this指向,偶尔没有上下文对象,this就指向window
      普通函数的this指向的是它的调用者,谁调用这个this,this就指向谁,如果没有调用者,那这个this就指向window在严格模式下(设置“use strict”),this为undefined
    2. 箭头函数是匿名函数,普通函数式具名函数
    3. 箭头函数无protoype,不可用于构造函数;普通函数有prototype,可用于构造函数
  4. Promise有几种状态

    1. pending未定
    2. fulfilled履行
    3. rejected拒绝
      同一时刻只能存在一种状态, 状态一变就不能再变,即一个promise的状态只可能从==“等待”转到“完成”态或者“拒绝”态==,不能逆向转换
      promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致,then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
  5. 算法题,判断字符串是否为回文(如‘abcddcba’、‘123454321’)
    中心扩散法

 //中心扩散法判断回文const checkhuiwen = (s) => {if (s.length === 1) return true//中心判断法const check_center =(m,n) => {while(s[m] === s[n] && s[m] && s[n]){m--;n++}if (m=== -1 && n === s.length) return trueelse return false}if (s.length % 2 === 0){//偶数双中心return check_center((s.length/2)-1,s.length/2)}else {//奇数单中心return check_center(s.length/2,s.length/2)}}console.log(checkhuiwen('bbbd'));//falseconsole.log(checkhuiwen('abcddcba'));//true
  1. 判断数组与对象

    let a = [2,3,4]
    let b = {name:'mt'}
    //isArray
    console.log(Array.isArray(a));//true
    console.log(Array.isArray(b));//false//.length
    console.log(a.length);//3
    console.log(b.length);//undefined
    if(!undefined){console.log('!undefined判断条件为正');
    }//.toString()
    console.log(a.toString());//2,3,4
    console.log(b.toString());//[object Object]//Object.prototype.toString.call
    console.log(Object.prototype.toString.call(a));//[object Array]
    console.log(Object.prototype.toString.call(b));//[object Object]//.contructor()
    console.log(a.constructor());//[]
    console.log(b.constructor());//{}// .Array.prototype.isPrototypeOf
    console.log(Array.prototype.isPrototypeOf(a));//true
    console.log(Array.prototype.isPrototypeOf(b));//false

滴滴(offer)

一面

  1. ES6的新特性

    1. 函数默认参数
    2. 箭头函数
    3. const、let
    4. 解构赋值
    5. 模板表达式
    6. Promise
  2. add(1)(2)(3) = 6
    知识点:闭包+console.log/alert 调用toString方法
 const add = (num) => {const s = (a)=> {num += areturn s}s.toString = ()=>{return num}return s}console.log(add(1)(2)(3)(4)(5)) //15

拓展:add(1)(2)(3)(4)…
3. 获取无重复的最长子串

 const longsubstring = (s) =>{let max = 0let m = ""let len = 0let ls = []for (n of s){if (m.indexOf(n) !== -1){m += n m = m.slice(m.indexOf(n)+ 1)}else {m += n len = m.lengthif(len > max){max = len ls.length = 0ls.push(m)}else if (len === max){ls.push(m)}}}return ls}
  1. 闭包,实现闭包

  2. webpack中cssloader和styleloader的区别
    webpack使用JS写的,运行在node环境,所以默认打包时只会处理JS之间的依赖关系
    所以像.css这样的文件不是一个js模块,需要配置webpack使用cssloader或者styleloader去合理地处理他们,
    如果在js中导入了css,就需要css-loader进行识别
    css-loader会处理 import / require() @import / url 引入的内容。

      //base.css
    .bg {background: #000;
    }
    const style = require('./base.css')
    console.log(style, 'css')
    

    css-loader处理后是这样的

    这是个数组,页面是无法直接使用,这时我们需要用到style-loader来处理。
    style-loader 是通过一个JS脚本创建一个style标签,里面包含一些样式。style-loader是不能单独使用的,因为它并不负责解析 css 之前的依赖关系,每个loader的功能都是单一的,各自拆分独立。

  3. 强制缓存和协商缓存
    强缓存就是给资源设置个过期时间,客户端每次请求资源时都会看是否过期;只有在过期才会去询问服务器。所以,强缓存就是为了给客户端自给自足用的
    而当某天,客户端请求该资源时发现其过期了,这是就会去请求服务器了,而这时候去请求服务器的这过程就可以设置协商缓存。这时候,协商缓存就是需要客户端和服务器两端进行交互的。
    协商缓存流程——
    发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>没过期–>返回304状态码–>客户端用缓存的老资源。
    or
    发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>过期–>返回200状态码–>客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。

  4. 常见状态码
    常见状态码

  5. Vue计算属性的作用 computerd(){}

    computed和methods对比

    计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
    相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
    缓存的意义?假设我们有一个性能开销比较大的的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

二面

  1. vue的key的作用

  2. 计算属性v-modal ,有什么优点
    计算属性的优点在于其缓存机制,即只有在它相关依赖发生改变时才会重新求值。

  3. 微任务、宏任务、eventLoop

  4. 跨域

  5. webpack

  6. js中的静态方法与实例方法

    function A() {}
    //say为a的静态方法
    A.say = function () {console.log('im static');
    }
    //A是方法对象
    A.say()function B() {}
    //say为B的实例方法
    B.prototype.say = function () {console.log('im not dynamic')
    }//bb是B的引用,可以找到say方法
    var bb = new B()
    bb.say()

    静态方法通过函数名.方法名的方式直接定义。

  7. 函数节流,函数防抖
    防抖:所有操作在一定时间间隔内都将只触发一次回调,应用:滚动,搜索栏相关词
    实现:

       //防抖
    const debounce = (fn,content,delay)=>{let movement = nullreturn function () {let args = argumentsif(movement) clearTimeout(movement)movement = setTimeout(()=>{fn.apply(content,args)},delay)}
    }
    

    节流:所有操作在一定时间内都将只触发一次回调,应用:监听滚动调位置

     //节流
    const throttle = (fn,content,delay) => {let isAvail = truereturn function () {let args = argumentsif(isAvil){isAvail = falsefn.apply(content,args)setTimeout(()=>{isAvail = true},delay)}}}
    

字节 懂车帝 一面挂

面试体验最好也算最拉跨的一次。。没见过这么耐心的面试官+中途没网+回答基本白给

  1. 实现垂直居中,给定一个#block元素,实现在浏览器窗口居中(利用定位)

    1.利用flex 给block套一个父元素,父元素的样式为 (需要父元素已知高度)

    display: flex;justify-content: center;align-items: center;

    2.利用定位 (子元素和父元素高度全部未知)

     position: absolute;top: 50%;left:50%;transform:translate(-50%,-50%)
    

    3.若父容器下只有一个元素,且父元素设置了高度,则只需要使用相对定位即可

     position: relative;top: 50%;left:50%;transform:translate(-50%,-50%)
    
  2. 简单聊一下对flex的理解,flex的常用属性有哪些
    2.1. Flexbox 是 flexible box 的简称(注:意思是“灵活的盒子容器”),是 CSS3 引入的新的布局模式。采用display:flex的元素称为flex container,其所有子元素自动称为容器成员,称为flex item

    2.2. flex常用属性-容器属性

    (1) flex-direction

    (2) justify-content

    (3)align-items

    (4)align-content:定义多根轴线

    2.3. 项目属性

    (1) order 数越小越靠前

    (2) flex-grow 放大比例

    (3)flex-shrink属性
    如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。

    (4)align-self属性
    align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

  3. 给一个div设置它父集div的宽度是100px,然后再设置它的padding-top为20%,问现在div有多高?如果父级定位是absolute呢?
    考察盒子模型 div元素高度 = content高度 + padding高度 所以答案是20px+div本身高度 和父级定位无关
    还有个知识点——

    我才知道。。padding-right,padding-bottom,padding-left也一样

  4. 代码段说答案,主要考察原型链和继承

 var a = function(){this.b = 3}var c = new a()a.prototype.b = 9var b = 7a()console.log(b);console.log(c.b);

输出 3 3

解释:首先定义了全局,即window的b = 7
而后执行函数a,this指向a的调用者,即window,所以window.b = 3
c为构造函数a的实例,所以c的内部有属性b 即c.b = 3
虽然下一句为a的原型添加了属性b = 9 ,但是c.b会先搜索c内部有没有属性b,其次再搜索a的原型中有没有属性b

  1. 我是傻逼之这道题都能错,考察事件循环,宏事件执行顺序
setTimeout(() => {setTimeout(() => {console.log(1);}, 100);console.log(2);setTimeout(() => {console.log(3);}, 0);}, 0);setTimeout(() => {console.log(4);}, 100);console.log(5);

控制台输出:52341
事件放到调用栈中开始执行(settimeout计时器开始计时),52不用多说了,41放入后计时器开始计时,而3没有计时,直接输出,41根据执行顺序4先开始计时,所以先4后1

  1. 实现一个函数getPrime,每次调用返回下一个质数,要求:
    1)不使用全局变量 -》暗示用闭包
    2)函数本身不接受任何参数 -》疯狂暗示闭包
          //判断一个数是不是质数const ifz = (num) =>{for(let i = num ; i > 0 ; i--)if (num % i == 0 && num !== i && i !== 1) return falsereturn true}//从2开始(包括2)往上加const Prim = () =>{let i = 2const getnum = () =>{while(!ifz(i)) i++console.log(i++);}return getnum}var getPrim = Prim()getPrim()  //2getPrim()  //3getPrim() //5getPrim() //7
  1. 谈谈对react hooks的理解(useMemo和useCallback)

Hook 是 React 16.8 的新增特性。它可以在不编写 class 的情况下让函数式组件使用 state 以及其他的 React 特性。Hook的定义:一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数
为了解决复用复杂组件困难问题,hook将组件中相互关联的部分拆分成更小的函数,而并非强制按照生命周期划分

都有哪些hook?

  1. State Hook useState()
    类似class组件中的this.setstate,但他不会把新的state和旧的state进行合并

  2. Effect Hook useEffect()
    一个函数可以做到和componentDidMount、componentDidUpdate 和 componentWillUnmount三个生命周期函数一样的功能

  3. Context Hook useContext()

const themes = {light: {foreground: "#000000",background: "#eeeeee"
},
dark: {foreground: "#ffffff",background: "#222222"
}
};const ThemeContext = React.createContext(themes.light);function App() {return (<ThemeContext.Provider value={themes.dark}><Toolbar /></ThemeContext.Provider>
);
}function Toolbar(props) {return (<div><ThemedButton /></div>
);
}function ThemedButton() {const theme = useContext(ThemeContext);
return (<button style={{ background: theme.background, color: theme.foreground }}>I am styled by theme context!</button>
);
}

相当于class组件中的

static contextType = MyContext

useContext(MyContext) 只是让你能够读取 context 的值以及订阅 context 的变化。你仍然需要在上层组件树中使用 <MyContext.Provider> 来为下层组件提供 context。

  1. Callback Hook useCallback()

    const memoizedCallback = useBcallback(()=>{doSomething(a, b);
    },[a,b],)
    

    返回一个memoized回调函数。
    它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

  2. Memo Hook useMemo()

    const memoizedValue = useMemo(
    () => computeExpensiveValue(a, b), [a, b]
    );
    

    返回一个memoized值
    把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
    如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

猿辅导

  1. Vue和React的区别

  2. Vue数据绑定的原理

  3. Vue的template语法和React的JSX语法各自的优势与劣势

  4. MVVM原理

  5. axios里get和post的区别
    level1——get和post是http通信的两种请求方法:get是从服务器获取数据,post是给服务器发送数据(废话)

    level2——get的参数直接放在url中,post则放在request body中,故post比get安全性高(半句废话)

    level3——get的传送数据量较小,不能大于2kb;post传送的数据量较大,此外对于get,server端通过request.QueryString获取变量,而对于post,server端用request.form获取数据(涉及后端)

    level4——get和post本质上都是tcp连接,但由于http规定和浏览器、服务器的限制,导致他们在应用过程中体现出不同。(本质)

    level5——get请求:浏览器把http header和data一并发送,服务器响应200 ; post请求:浏览器先发送header,服务器响应100 continue,浏览器再发送data,浏览器响应200(答这句==装起来)

  6. HttpRequest自行封装类axios包来进行数据通信

  7. 笔试题

    1. 纯css实现一个按钮闪烁
    2. Array.flat()实现
    3. JS时间组件类,on,on,on,emit实现类vue的消息传递(消息订阅发布原理)
  8. 项目中遇到的最大的困难,以及如何解决的

字节 抖音 一面

  1. 浏览器输入url->显示页面 过程简述

    1. 首先浏览器接收到url,开启网络请求线程,如果是域名则要先经过DNS服务器解析,得到目标ip
    2. 发出http请求,经过网络五层的封装与解封装,目标服务器收到http请求
    3. 收到请求的服务器大多数情况下是反向代理的调度服务器,它会根据调度算法,分配给相应集群中的不同服务器,等待处理完毕后,再将响应发送给代理服务器
    4. 代理服务器将响应再发送给浏览器,浏览器收到响应后进行解析,首先解析html生成DOM树,遇到css样式会构建css规则树,遇到JavaScript会通过DOM API 和 CSS DOM API 来操作DOM树和css规则树,解析完成后将两者合成为render树,最后通过布局(确定每个节点在屏幕上的位置)和绘制,生成最后的页面。期间如果发现外链js脚本时,会先进行下载,再进行html解析。
  2. dns解析过程

    1. 浏览器得到url后,先看本地host有没有映射关系,若无则查找本地dns解析器缓存。
    2. 如果本地(host和dns解析器缓存)没有,则查找本地DNS服务器(TCP/IP参数中的首选DNS服务器),先查找本地配置资源,若无则查找本地DNS服务器中的缓存,若无则根据本地DNS服务器的设置进行查询。
    3. 若为转发模式,则会把请求转发至上一级DNS服务器,由上一级进行解析,若不能,则再传给上一级,循环直至解析完毕;
    4. 若未使用转发模式,则将请求发送至13台根DNS服务器,根服务器将域名所属的顶级dns服务器的ip发送回本地dns服务器,本地服务器再根据这个ip联系该顶级dns服务器,若该顶级无法解析,就会找管理下一级的DNS服务器地址给本地DNS服务器,重复上面的动作直至解析完毕。本地dns服务器收到ip后再返回给客户端。
      本地客户机到本地DNS服务器属于递归查询,DNS服务器之间是迭代查询。
      一图流:
  3. 三次握手和四次挥手为什么要这么做

    • 为什么三次握手?
      双方都做好发送数据的准备;双方可就初始序列号进行协商,这个序列号在握手过程中被发送和确认
      如果不是三次握手,则会造成:

      • 资源浪费 如果C给S发了个a1,但链路故障,导致a1需要长时间才能到达S,C由于长时间没收到答复,所以认为上一个包失败,故清楚a1,发送a2,S收到a2,回复b2,而后又收到a1,回复b1,但由于C清除了a1,所以会丢到b1,但server不知道,所以会保持这个僵尸链接,造成资源浪费(延时造成的影响)

        “三次握手”的目的是为了解决“网络中存在延迟的重复分组”的问题——《计算机网络》

      • 死锁可能 C给S发送了一个连接请求分组,S给C回了个确认应答,如果这个确认应答丢了,C没有收到,不知道S建立什么样的序列号,此时C会忽略S发来的任何数据,只等着确认应答,而S在发出的分组超时后,会重复发送,形成死锁。(丢包造成的影响)

    • 为什么四次挥手?

      • TCP是一个全双工协议,必须单独拆除每一条信道。4次挥手的目的是终止数据传输,并回收资源,必须要等到两方向都没有数据传输,才能拆除虚拟链路。如果三次挥手,即第二第三次合并,ACK和FIN同时回复,如果被动方有还没发送的数据,则无法满足条件。close-wait阶段就是为这一可能而存在的。
  1. js、css、html加载、渲染、之间的阻塞关系

    js下载和加载会阻塞浏览器解析。
    css下载不会阻塞解析DOM,但会阻塞渲染。

  2. 强缓存、协商缓存,应用场景

    • 强缓存和协商缓存是缓存的两种类型
    • 强缓存在过期时间之内不会去重复请求,这个时间通过第一次访问服务器时的响应头字段设置 http1.0 :expires ;http1.1 : Cache-Control,Cache-Control中有多个值,其中的max-age代表过期时间,总之强缓存只在首次和服务器通信。
    • 协商缓存,每次读取数据都要和服务器通信,第一次请求服务器时,服务器会将资源的etag(唯一标识)返回给浏览器,下次浏览器再请求时,服务器会匹配发过来的etag和自己的etag是否匹配,若不匹配,则表示资源更新,服务器会将新数据和新etag返回给浏览器;若匹配,则返回304(条件不符合),浏览器读取本地缓存资源
    • 应用场景:index.html协商缓存保证资源更新,其他资源采用强+协商缓存
  3. 网络五层

    - 应用层决定了向用户提供应用服务时通信的活动,http、ftp、dns
    - 传输层,对应用层提供处于网络连接中两台计算机间的数据传输 TCP、UDP
    - 网络层,将运输层产生的报文段或者用户数据报封装成分组或者包进行传送(IP数据报)
    - 数据链路层,数据链路层量网络层交下来的IP数据报封装成帧(frameing)
    - 物理层 0,1信号传输

  4. 防抖和节流的区别、作用,手写实现

  • 防抖
    在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。某一次操作后在指定时间内没有再操作才有效
function debounce(fn, content,delay ) { //fn为回调函数,delay为延迟时间,content为目标上下文let movement = nullreturn function() {let args = arguments// 清空上一次操作clearTimeout(movement)// delay时间之后,任务执行movement = setTimeout(function() {fn.apply(content, args)}, delay)}
}
  • 节流
    规定时间内只触发一次
function throttle(fn, content,delay) {let isAvail = truereturn function () {let args = arguments // 开关打开时,执行任务 if (isAvail) {fn.apply(content, args)isAvail = false // delay时间之后,任务开关打开 setTimeout(function () { isAvail = true }, delay)}}
}
  1. 事件循环 event loop(略)

  2. TS type和interface的区别

    1. interface只能定义对象类型
      type声明可以定基础类型、联合类型、交叉类型
    2. interface可以extends,implement,从而扩展接口或类,type不能,type只能用交叉类型&,来使成员合并
    3. interface同名会合并,type同名则报错
    4. type可以使用typeof获取实例类型
    let div = document.createElement('div')
    type B = typeof div
    
  3. TS泛型,常用api
    泛型:可用于类和构造器,思想都是将类型作为特殊的参数传入。

  4. 算法(全排序)

function  get_res(nums) {const used = new Array(nums.length).fill(false)const res = []const getres = (t)=>{if(t.length === nums.length) {res.push(t.slice(0)) //注意这里 必须要push一个新数组,而不能是t,因为t最后都会清空为[]}else {for(let i = 0 ; i<nums.length ; i++){if(used[i]) continueused[i] = truet.push(nums[i])getres(t)t.pop()used[i] = false}}}getres([])return res
}

字节抖音 二面 挂

  1. 介绍项目亮点、难点
    亮点全无,难点全不难

  2. 自己的前端学习规划

  3. hook是如何在特定时间点执行的(像生命周期一样)
    通过useEffect中的依赖项的值来分别实现componentDidMount、componentDidUpdate、componentWillUnmount

  4. 用useEffect实现useDeepCompareEffect
    情景:useEffect后的依赖如果是数组和对象的情况,如何判断

    
    import useDeepCompareEffect from 'use-deep-compare-effect';useDeepCompareEffect(() => {console.log('deep-keys', defaultKeys);}, [defaultKeys]);

    思路:给useEffect做一层封装,考虑到useEffect的依赖是基本类型时,可以检测到,所以我们使用useRef制作一个信号量compare,当deps改变时,信号量改变,使得useeffect间接监听到依赖的变化。

useEffect(()=>{},[]) // 分别对应fn ,depsimport { isEqual } from 'lodash';const useDeepCompareEffect = (fn,deps) =>{const compare = useRef(0) //信号量const predeps = useRef(deps) //先前的依赖if(!isEqual(predeps.current,deps)){ //比较两者compare.current ++}predeps.current = deps //将新的依赖存起来return useEffect(fn,[compare.current])
}

引申:这里用到useRef跨越渲染周期的作用
useRef:可用来获取组件实例对象或DOM实例对象
如:

import React, { useState, useEffect, useMemo, useRef,useCallback } from 'react';export default function App(props){const [count, setCount] = useState(0);const doubleCount = useMemo(() => {return 2 * count;}, [count]);const couterRef = useRef();const add = ()=>{setCount(count + 1)}useEffect(() => {console.log('count变力');console.log(count) //1console.log(couterRef.current);  //<button>"Count:" /n "1" /n ",doubleCount:" /n "2" /n</button>}, [count]);return (<><button ref={couterRef} onClick={add}>Count: {count}, double: {doubleCount}</button></>);
}

通过访问couterRef.current就可以访问到button对应的DOM对象。
但是除此以外,useRef还有别的用法,因为useRef存储的值修改后不会引起组件的重新渲染,这就是得它可以跨渲染周期,组件渲染后也能保持不变。

import React, { useState, useEffect, useMemo, useRef,useCallback } from 'react';export default function App(props){const [count, setCount] = useState(0);const doubleCount = useMemo(() => {return 2 * count;}, [count]);const couterRef = useRef();const timerref = useRef()useEffect(()=>{//给timerref挂一个定时器,timerref的改变不会使组件重新渲染timerref.current = setInterval(() => {setCount(count => count+1)}, 100);},[])const add = ()=>{setCount(count + 1)}useEffect(() => {if(count>=10) {clearInterval(timerref.current)console.log(timerref);}}, [count]);return (<><button ref={couterRef} onClick={add}>Count: {count}, double: {doubleCount}</button></>);
}
  1. 函数柯里化

    实现一个函数sum,运算结果可以满足如下预期结果:
    sum(1,2,3).valueOf(); //6
    sum(2,3)(2).valueOf(); //7
    sum(1)(2)(3)(4).valueOf(); //10
    sum(2)(4,1)(2).valueOf(); //9

function sum(...args) {//args为所有参数组成的数组let res = args.reduce((t,c) => t+c)const get_res = (...args) =>{//reduce(fn , initvalue) initvalue为初始值//这里的initvalue调用了外层的res,形成闭包res = args.reduce((t,c)=>t+c,res)return get_res}get_res.valueOf = () =>{console.log(res);}return get_res}

延伸:arguments和…args

function test(...args) {console.log(args); //输出[1,2,3]console.log(arguments);//输出arguments对象console.log(arguments === args); //false
}test(1,2,3)

arguments对象不是数组,它类似于数组,因为它有length属性和索引元素,它可以被转化为一个真正的数组,即args

function test(...args) {console.log(args);console.log(arguments);console.log(Array.prototype.slice.call(arguments)); //[1,2,3]console.log([].slice.call(arguments));//[1,2,3]console.log([...arguments]);//[1,2,3]
}test(1,2,3)

字节 抖音 二面 捞 挂

  1. 浏览器中js事件冒泡\捕获机制
  • 冒泡

    • 首先,浏览器是事件驱动的,根据用户不同事件,如鼠标键盘事件(onclick)、页面时间(onload)、表单相关事件(onchange)来执行相应操作,需要注意的是事件处理程序中的变量event保留事件对象的信息,比如click事件里有点击位置的页面坐标、触发事件的dom节点信息。
    • 何为冒泡:由于DOM树的原因,点击一个元素那么它的父元素也被点击,以此类推,相当于整个树被点击了,这个逐层向上级元素传递的过程被称为事件冒泡。冒泡的烦恼也由此而来,有时我们只需要触发一层的事件,而不要全部都触发,那么如何解决呢?
    • 解决思路:
      • 阻止事件继续向上传递,利用事件的stopPropagation(),给别的层的事件中添加event.stopPropagation();注:ie中要使用Event.cancelBubble=true;
      • 不阻止冒泡,修改执行函数的方法 ,先判断event.target == event.currentTarget再决定是否执行方法,上一层每一层都要加;
      • 不阻止冒泡,在冒泡到父节点进行判断。不同于上两中每一层都要添加代码,这种办法只需要在冒泡过程中的任一个终止冒泡的节点,进行统一处理,即事件委托。(元素触发事件,但事件执行的方法不在元素本身,而在其父级的某一节点上)
  • 捕获

    • 捕获与冒泡相反,会先从最不精确的对象(DOM)开始触发
  • DOM事件流:

    • DOM2级事件规定的事件流包含三个阶段:事件捕获、处于目标、事件冒泡,先事件捕获为截获事件提供机会,然后到处于目标阶段,最后冒泡回去。dom2级事件定义了addeventlistener和removeEventlistener两种方法,他们包含三个参数:

      • 要处理的事件方式,如click、mouseover、dbclick等
      • 回调函数,如果要删除事件,该函数要为命名函数
      • 布尔值,true:表示在捕获阶段调用,false:表示在冒泡阶段调用(默认)
  1. http和https的区别
    https并非新协议,而是将http通信接口部分用ssl和tls协议代替后的产物。
    它通过ssl采用混合加密机制的处理方式(用ssl的公开密钥加密方式交换之后共享密钥所用密钥),为通信加密,解决了http存在的监听问题;
    通过数字证书认证机制,解决公开密钥真实性问题,从而为通信双方的身份提供保证,解决http的伪装问题;
    通过mac报文摘要,确保报文完整性,防止篡改可能。
  1. tcp和udp的区别,各自的应用场景

    1. 连接

    TCP:面向连接,即传输前要建立好连接(三次握手)
    UDP:无连接,即不建立连接

    1. 可靠性

    TCP:可靠,即无差错、不丢包、不重复、按序到达
    UDP:不可靠,只尽最大努力交付,不能保证可靠交付

    1. 对象

    TCP:点对点的服务,即一条TCP连接只能有两个端点
    UDP:可一对一,一对多,多对多,多对一

    1. 首部大小

    TCP:首部20字节
    UDP:首部8字节(源端口、目的端口、数据长度、校验和(用于发现头部信息和数据中的错误))

    1. 面向

    TCP:面向字节流,报文长度根据接收方的窗口大小和当前网络拥塞情况来决定,在不保留报文边界的情况下以字节流方式传输
    UDP:面向报文,保留应用程序传下来的报文边界,不合并,不拆分。

    1. 速度

    TCP:慢,因为有拥塞控制和流量控制保证安全性,一旦发生丢包,tcp会将后续的包缓存起来,等前面的包重传并接受到后再继续发送,延时会越来越大
    UDP:快,没有拥塞控制,保证实时性

    1. 适用场景

    TCP:文件传输、邮件等要求可靠传输的应用
    UDP:视频会议、直播、网络游戏等要求实时性的应用

  1. 算法

    versions是一个项目的版本号列表,因多人维护,不规则
    如 versions = [‘1.45.0’,‘1.5’,‘6’,‘3.3.3.3.3’]
    要从小到大排序,注:1.45>1.5
    sorted = [‘1.5’,‘1.45.0’,‘3.3.3.3.3’,‘6’]

var versions = ['1.45.0','1.5','1.45.1','6','3.3.3.3.3','3.3.3.3.3']const get_order = (order)=>{const getorder = (a,b,i) =>{let num_a = a.split('.')[i]let num_b = b.split('.')[i]if(num_a === num_b ){return getorder(a,b,i+1)}else{return num_a - num_b}}function fn(a,b) {if(a===b) return a-blet i = 0let num_a = a.split('.')[i]let num_b = b.split('.')[i]    if(num_a === num_b ){return getorder(a,b,i+1)}else{return num_a - num_b}}order.sort(fn)return order //["1.5", "1.45.0", "1.45.1", "3.3.3.3.3", "3.3.3.3.3", "6"]
}
  1. position有哪些值,有什么区别
  1. 左右200px 中间自适应 ,total类是最外层的包裹
.total{height:100vh;display: flex;
}.left{width:200px;
}
.right{width:200px;
}
.center{flex-grow: 1;
}
  1. let、var、const 的区别
    var:函数作用域 & 全局作用域 、 变量提升
    let :块级作用域 、 不存在变量提升、暂时性死区(只要块级作用域内存在let命令,它所声明的变量就绑定这个区域,不收外部影响,在块级作用域中,使用let声明变量前,该变量都不可用)、不允许重复声明
    const:与let基本一致,但它保证了变量指向的内存地址不得改动。
  1. 普通函数的this指向
var length = 10
function fn(){return this.length+1;
}
var obj = {length: 5,test1:function(){return fn()}
};
obj.test2 = fn;console.log(obj.test1());//11
console.log(fn());//11
console.log(obj.test2());//6
  1. 算法:求一棵二叉树的高度(深度)
    ps:面试官说我这个函数有没考虑到的地方,但是我在leetcode上都通过了,不懂哪没考虑到。。
var maxDepth = function(root) {const get_dep = (node,h) =>{if(node) return Math.max(get_dep(node.left,h+1),get_dep(node.right,h+1))else return h}return get_dep(root,0)
};
  1. 事件循环
async function async1(){console.log(1)await async2()console.log(2)
}async function async2(){console.log(3)
}console.log(4)
setTimeout(function(){console.log(5)
},0)
async1();
new Promise(function(resolve){console.log(6)resolve()
}).then(function(){console.log(7)
})
console.log(8)

ans:41368275

  1. get和post的区别(同猿辅导一面)

  2. 对formily的了解

  3. 进程和线程的区别
    进程是资源分配的最小单位,线程是CPU调度的最小单位

    1. 对应关系

    一个进程可以包含多个线程,线程在进程下工作。

    1. 资源消耗

    进程要比线程消耗更多的计算机资源

    1. 资源共享

    不同进程间的数据很难共享,但同一进程下不同线程间数据很易共享,进程使用的内存地址可以通过上锁或信号量来限定使用它的线程。

    1. 崩溃影响

    进程间不会相互影响,而一个线程的崩溃会导致整个进程挂掉

    总的来说,进程和线程不是同一个层面上的概念,线程主抓中央处理器执行代码的过程,其余的资源的保护和管理由整个进程去完成。

  1. history和hash路由的区别

    • hash路由:锚点操作,通过hash值变化感知路由变化,只需要客户端支持,不需要服务器支持,兼容性好。hash是“#/main/video”,改变#后边的内容,浏览器会加载相应位置的内容,不会重新发起请求。
    • history路由:基于html5的history模式,监听url变化,需要客户端和服务端共同支持。基于HTML5中的window.history对象中的方法实现的。常见的如:

腾讯二面

  1. 网络七层
    OSI参考模型

    • 应用层
      网络服务与最终用户的一个接口(http、ftp)
    • 表示层
      数据的表示、安全、压缩(jpeg、ASCII)
    • 会话层
      建立、管理、终止会话
    • 传输层
      定义传输数据的协议端口号,流量控制和差错校验(tcp、udp)
    • 网络层
      进行逻辑地址寻址,实现不同网络之间的路径选择(ip、ICMP)
    • 数据链路层
      将比特组合成字节进而组合成帧,用mac地址访问介质
    • 物理层
      01信号传输
  1. 谈谈tcp三次握手、四次挥手的理解
    握手挥手过程,以及为什么要这么做
  1. 协商缓存
    同上,重点在于etag的对比
  1. react类组件生命周期
    重点三个生命周期函数
    componentDidMount
    componentDidUpdate
    componentWillUnmount
  1. react hooks
    16.8新特性,让函数式组价能使用state和其他react特性,为了解决复用复杂组件困难的问题,hook将组件中相互关联的部分拆分成更小的函数,而并非强制按照生命周期划分。
  1. react fiber
  1. 301和302的区别
    永久重定向和临时重定向
    location中都有新的url
  1. sessions和cookies的联系和区别

    • cookie存在客户端上,session存在服务器上
    • cookie不安全,别人可以分析本地cookie进行cookie欺骗,考虑到安全应使用session
    • session会在一定时间内保存在服务器上,访问增多时会占用服务器性能,考虑到减轻服务器性能方面,应使用cookie
    • cookie保存的数据不超过4k,一般考虑将登陆信息等重要信息存放为session,其他相对不重要信息放在cookie中
  1. 算法:中序遍历

前端实习面试经验汇总相关推荐

  1. 【面试经验分享】十分值得一看! 研一下-算法实习-面试经验汇总

    青藤云安全(2月26日) 机器学习算法实习岗 微信视频面试,15分钟.主要问了下简历上做阿里云比赛的经历,后未联系,预计已挂. 主要存在的问题: 1)做的东西太简单太基础,不够看,导致面试官其实没啥可 ...

  2. 今日头条前端实习面试经验

    这次面试主要死在对原生js代码不熟练,框架用多了唉. 总结:最好要到别人的面经,每个公司面试题都是有套路的,如果没有就去百度那个公司的面试经验并总结.代码题要刷. 一面: 主要是计算机网络相关的知识, ...

  3. Android实习面试经验汇总,骚年你的屏幕适配方式该升级了

    前言 从17年毕业至今,就职过几家公司,大大小小项目做了几个,非常感谢我的两位老大,在我的android成长路上给予我很多指导,亦师亦友的关系. 从年前至今参加面试了很多公司,也收到了几家巨头的off ...

  4. Java春招实习面试经验汇总,面试篇

    Part1 SpringIOC 学习Spring最重要的无非是Spring IOC以及Spring AOP,首先咱们把Spring IOC吃透,以下内容将截图展示. Spring IOC主要学习内容分 ...

  5. Java面试总结,Java实习面试经验汇总

    程序员:给多少工资,干多少事 我们不是经常会看到一个关于西游记的"悖论"吗: 为什么孙悟空初期大闹天宫的时候那么厉害?因为他自己当老板,打一群天庭的打工仔. 为什么取经路上又变得不 ...

  6. 【干货】Java实习面试经验汇总

    前言 分布式,是程序员必备技能之一,在面试过程中属于必备类的,在工作中更是会经常用到.而Kafka是一个分布式的基于发布订阅的消息队列,目前它的魅力是无穷的,对于Kafka的奥秘,还需要我们细细去探寻 ...

  7. 在线打包app平台,Android春招实习面试经验汇总

    免费在线制作App的无线应用开发工具集合 -追信魔盒成就全球最大的手机软件在线制作平台 不会编程?教你用开发工具制作高下载量的App! 现在很多站长,商家和个人都想自己开发优质的App,利用高下载量去 ...

  8. 6轮字节前端校招面试经验分享

    大家好,我是若川.最近金三银四,今天分享一篇字节前端校招面试经验的轻松好文,相信看完会有所收获.也欢迎点击下方卡片关注或者星标我的公众号若川视野 因为我错过了2020年的秋招(ps: 那时候连数据结构 ...

  9. 一年半前端跳槽面试经验(头条、微信、shopee)

    一年半前端跳槽面试经验(头条.微信.shopee) 在2019年末的时候,突然想搞点大事,思来想去,感觉只有跳槽是最刺激的. 由于我比较懒,不想换城市,所以这次只面试了头条.微信和 shopee.十分 ...

  10. 记一次网易前端实习面试

    记一次网易前端实习面试 很幸运地能收到网易的面试通知,就毫不犹豫翘了课去面试了hhhh~三点的面试,因为从来没去过那个中关村西北旺区,吃完饭早早就去了,想象中那里应该是繁华的地方hhhh,到了发现都在 ...

最新文章

  1. Redis源码分析:基础概念介绍与启动概述
  2. java输出流输入流的使用_Java中的IO流之文件输入输出流
  3. linux shell 里面执行python 程序_Linux下编写脚本Shell和Python的区别?
  4. Jenkins pipeline JENKINS_NODE_COOKIE踩坑记录
  5. 如何判断三极管好坏?
  6. csgo怎么控制电脑玩家_电脑远程控制怎么弄
  7. linux主机名包含点
  8. 织梦自定义表单限制IP24小时只能提交一次方法
  9. C/C++ 格式化读取和读取一行
  10. 人工智能常用的编程语言
  11. java保护表格_java poi Excel单元格保护
  12. word无法创建工作文件请检查临时环境变量
  13. Ubuntu 启动图标变成问号
  14. [生命科学] snapgene 构建载体方法分享
  15. 互联网产品推广分为哪些阶段?
  16. 机器学习核心算法各个击破
  17. STM32F4的DMA
  18. COMSOL初级学习之一
  19. 题解 | Popping Balloons-2019牛客暑期多校训练营第十场F题
  20. 基础知识 | BOM 事件

热门文章

  1. [学习IMU](MEMS 三轴加速计、三轴陀螺仪、三轴磁力计)6轴IMU+磁力计,9轴传感器讲解
  2. 走进波分 -- 03.光纤传输系统关键参数
  3. 工商银行支付接口开发Java
  4. ps新手零基础知识入门教程学习_图文
  5. 浅谈DNS协议,DNS协议的作用以及DNS的查询方式,使用DNS做负载均衡
  6. nali命令--输出IP地址显示地理信息
  7. 开题报告(3.研究的思路、过程与方法)
  8. 中超16强内援转会更新 陕西重金打造中国银河战舰
  9. mat java_使用MAT分析Java内存
  10. 蓝桥杯:填字母游戏(第八届决赛javaB第五题) 博弈+递归+回溯+map记忆化