• 面试总结(2019/03/17更新)


    本来想分一期二期写的,想想第一篇博客要特殊点,哈哈。那就像流水账一样一直往下写吧。

    题目不分先后不分重要,包括自己面试的一些比较有代表的知识点和别处看到的面试点。

    1、JS继承(原型链继承)

    继承这个题目其实挺大的,继承的方式也有很多种,目前我也不是全部理解各种方法的继承,以后摸透了写个继承专题吧。

    直接挂代码

     1 // 声明 人 这个类
     2 function People(sex) {
     3   this.sex = sex;
     4   this.saySex = function saySex() {
     5     console.log(`我的性别是${this.sex}`);
     6   }
     7 }
     8 
     9 // 创建女性
    10 function Woman() {
    11 
    12 }
    13 Woman.prototype = new People('女');
    14 // 这样Woman这个类别的原型对象是 人 构造的;
    15 const girl = new Woman();
    16 girl.saySex();

    2、什么是闭包,手写一个闭包

    这又是一个很大的问题啊,哈哈以后写个闭包专题详解。其实简单理解一下就是:一个函数记住并能够访问他所在的词法作用域时,即使函数在其他的作用域调用,这就产生了闭包。

    贴一个典型的闭包例子

     1 function fn () {
     2   const str = 'HelloWorld';
     3   function bar () {
     4     console.log(str);
     5   };
     6   return bar;
     7 }
     8 
     9 var fo = fn();
    10 fo() // 输出HelloWorld;

    fo其实就是fn作用域中的函数bar,但是fo在调用的时候并不是在fn内部作用域中调用,即使如此依旧访问到了str这个变量,在这里产生了闭包。

    3、使用闭包实现数据缓存

    废话不多说,直接上代码

     1 const foo = (() => {
     2   const cache = {};
     3   return {
     4     getCache(key) {
     5       return cache[key];
     6     },
     7     setCache(key, val) {
     8       cache[key] = val;
     9     },
    10   };
    11 })();
    12 
    13 foo.setCache('name', 'dsj');
    14 console.log(foo.getCache('name'));

    直接用IIEF返回了一个对象,只暴露出两个方法用来存和取,在这里就用了闭包,函数调用时所在的词法作用域并不是函数声明所在的域,依旧访问到了想要的变量,产生了闭包。

    4、url->页面生成过程

    分为两大块来答
    一个是网络http层面
    首先输入url,进行DNS解析
    DNS解析首先会进行缓存查找,分为以下5部
    1. 浏览器先会在浏览器缓存中去查找该域名是否有对应的IP信息
    2. 接着搜索操作系统中有没有相应的缓存信息,比如hosts文件中
    3. 接着在路由器缓存中进行查找
    4. 接着从网络服务商那边的缓存信息中进行查找
    5. 以上方法如果找到就直接返回IP信息,如果都没有找到就会发起一个迭代DNS解析请求
    1> 向根域名服务器发送请求,该服务器没有域名的全部信息,但是可以返回顶级域名服务器地址,如com域的顶级域名服务器地址
    2> 接着向顶级域名服务器发送请求最终获取IP地址
    6. 本地域名服务器(也就是网络服务商)将IP返回给操作系统同时把该IP缓存起来
    7. 操作系统把IP返给浏览器,同时把IP缓存起来
    8. 最后浏览器得到了IP开始建立连接
    建立连接 => 3次握手
    客户端发送带有SYN标志的数据包给服务端(请求连接)
    服务端发送带有SYN/ACK标志的数据包给客户端(同意建立连接)
    客户端发送带有ACK标志的数据包给服务端(开始连接)
    PS:如果三次握手中某次某一方没接到信号,TCP协议会要求重新发送信号

    建立连接后会开始数据的传递,比如相应的js文件,css文件,图片文件等。同时浏览器开始渲染页面
    当数据传递全部完成,开始四次挥手,断开连接。

    其中浏览器渲染页面过程如下:
    解析HTML,遍历文档节点生成DOM树;
    解析CSS文件生成 CSSOM规则树;
    然后DOM树和和CSSOM树结合生成渲染Render树;
    渲染树开始布局,计算每个节点的位置大小等信息;
    然后绘制每个节点到屏幕上
    PS:在生成DOM树的过程中,如果遇到script标签,会产生阻塞;
    如果JS脚本中还对CSSOM进行了操作,浏览器会延迟脚本的执行和DOM的渲染,先生成CSSOM树;
    渲染树进行绘制的时候,涉及到了重绘和回流
    回流:当元素的大小布局等发生改变的时候就会触发回流,回流必然引发重绘;
    重绘:当元素的颜色背景之类发生改变的时候触发重绘
    每个页面必然发生一次回流,所以必然有一次重绘,也就是页面加载渲染的时候;
    因为回流会导致渲染树重新构建,花销较大,所以应该尽可能的避免回流;
    比如减少DOM操作等。

    5、JS的数据类型,基本类型哪些,和引用类型的区别

    数据类型: null, undefined, Number, String, Object, Boolean, Symbol
    基本类型: undefined, null, boolean, number, String, 指的是保存在栈内存中的简单数据
    引用类型: Array, Object等对象类型,指的是保存在堆内存中的对象,变量实际保存的是一个指针,指针指向内存中的一个位置,该位置保存对象

    6、节流和防抖

    节流和防抖都是用来预防某些操作重复触发函数,比如重复请求,造成性能卡顿问题。
    防抖原理:无论如何触发事件,在n秒后我才执行这个函数,如果n秒内再次触发该函数就以新触发的为准,n秒后再执行

     1 function debounce(fuc, delay = 300) {
     2   let timer = null;
     3   return function fn() {
     4     const self = this; // 保存调用函数的this,防止setTimeout改变this指向
     5     const args = arguments; // 保存函数调用的传参
     6     if (timer) clearTimeout(timer); // 清除定时器
     7     timer = setTimeout(() => {
     8       fuc.apply(self, args); // 函数指定this,同时把参数传入
     9     }, delay);
    10   };
    11 }
    12 
    13 // 比如给一个div绑上一个mousemove事件,
    14 function move(event) {
    15   console.log(1, event);
    16 }
    17 div.onmousemove = debounce(move);

    可以直接试一下。

    节流原理:一段时间内某个函数只能触发一次,比如3秒内fn函数只能触发一次,便会在0秒是触发一次该函数直至3秒都不会再次触发
    目前主流采用时间戳或者定时器来实现
    时间戳方法会让函数立刻执行一次,但是在停止触发的时候并不会再执行了,如下

     1 function throttle(fuc, delay = 300) {
     2   let timer = 0;
     3   return function fn() {
     4     const self = this;
     5     const now = +new Date();
     6     const args = Array.prototype.slice.apply(arguments);
     7     if (now - timer > delay) {
     8       fuc.apply(self, args);
     9       timer = now;
    10     }
    11   };
    12 }

    7、手写bind实现

    这个问题真的非常常见,但是知识点还蛮多的。写了一年业务代码很少用到 bind, call, apply这些函数,也对这些理解得不咋深,结果面试问到就露馅,回过头来多理解一下,多写写也搞懂了一些。

    首先讲一下bind的用法,bind一般用来改变一个函数中this的指向,该方法会返回改变this后的函数,以下是一个范例 

     1 var obj = {
     2   name: '阿狸',
     3   age: '18',
     4 };
     5 function fuc(sex, from) {
     6   console.log(`我叫${this.name}`);
     7   console.log(`我是${sex}孩子,来自${from}`);
     8   console.log(`今年${this.age}岁啦`);
     9   return '哈哈哈哈哈';
    10 }
    11 
    12 fuc.bind(obj, '女')('瓦罗兰大陆');

    输出肯定是没啥问题的,可以直接贴f12试下,用法试了一回那么怎么手写一个实现怎么做呢?

     1 Function.prototype.bindHandler = function (ctx) {
     2   const self = this;
     3   // ctx也就是this要指向的对象
     4   return function() {
     5     self.apply(ctx);
     6   }
     7 }
     8 // 试验一下
     9 fuc.bindHandler(obj, '女', '瓦罗兰大陆');
    10 // 发现结果有点不对,参数没有拿到,而且还有一个点没考虑到,fuc函数如果返回一个值,能拿到吗?明显不能,改进一下函数
    11 Function.prototype.bindHandler = function (ctx) {
    12   // ctx也就是this要指向的对象
    13   const self = this;
    14   // 保存一下除了ctx以外传进来的参数,
    15   const args = Array.prototype.slice.apply(arguments, [1]);
    16   return function() {
    17     const argsOther = Array.prototype.slice.apply(arguments);
    18     const result = self.apply(ctx, args.concat(argsOther));
    19     return result;
    20   }
    21 }
    22 // 再试一下, 完全objk。
    23 fuc.bindHandler(obj, '女', '瓦罗兰大陆');
    24 fuc.bindHandler(obj, '女')('瓦罗兰大陆');

    搞定!

    8、手写trim实现
    其实就是去掉首尾空格,一个简单的正则搞定

    1 String.prototype.trimHandler = function() {
    2   return this.replace(/(^s*)|(s*$)/g, '');
    3 }

    先写到这里,下次再来更新!

    /* ----------------------------------------------- 2019/3/4 更新------------------------  */

    又回来啦,最近这段时间一直在面试。运气不错,拿到offer了。

    躺了一天不能懒了,回来记录下面试过程中印象比较深的一些题吧。

     

    续上

    9、如何理解Nginx代理解决跨域
    跨域问题在前端算是个必考点,其实不想说这个的,一个基础的前端开发是必然要了解跨域相关的知识。

    不过面试次次问,那就说一下我的理解,希望给面到这个问题的同学一点帮助。

    其实在这之前还得理解一下什么是 "同源策略",网上其实有很多解释了,不过我希望同学们记住一点,这是浏览器的一个功能!(划重点)

    也就是说解决跨域,就为了绕过浏览器的这个限制。

    在这里贴一个链接,里面有讲到同源策略和一些普遍的跨域方法,我就不赘述了(https://segmentfault.com/a/1190000007326671)。

    我想说的主要是现在我们开发中很常用的配置nginx代理去解决跨域。

    原理其实不复杂,就是绕过了浏览器的同源策略去解决。

                              

    正常来说我们写代码发送请求给目标地址,会产生跨域,那么我们可以做个中转站服务器,让这个代理服务器可以我们的请求,然后这个代理服务器再转发给目标服务器。

    服务器和服务器之间是可以直接请求的,之前说过同源策略是浏览器的功能嘛。这样就绕过了服务器的限制,解决了跨域,其实就这么简单。

    不过一般来说正式项目上线,服务器会给配置一个网关,这个网关去过滤一部分请求,才能到目标服务器。不然服务器的安全性就太低了,任意请求被攻击怎么办?

    这样我们在开发的时候就可以解决跨域问题,快乐的开发。至于上线,也可以配置Nginx代理服务器的,然后在服务器端改一下配置允许对应代理的请求即可,这部分一般是运维的工作。

     

    10、JS实现事件绑定的几种方法

    这个问题简单,但是经常让你手写一个兼容绑定事件的方法,可以瞟一眼。

    假设有个函数

    // 假设有个函数
    function fn() {
      alert('Hello World !');
    }
    // 直线元素绑定
    // html代码
     <div id='btn' @onclick='fn()'>按钮</div> 
    
    // 或者通过js绑定
    // 这是dom0级事件绑定,此方法只能绑定一个事件,绑定多个会被覆盖
     document.getElementById('btn').onclick = fn;
    
    // dom2级事件绑定
    // 如果绑定多个函数,会根据绑定的顺序来执行
     document.getElementById('btn').addEventListener('click', fn, false);
    // 其中这个布尔值,true指的是在事件捕获阶段调用方法,false就是在冒泡阶段,默认为false

    不过addEventListener有兼容问题,在IE8及以下版本要用attachEvent这个方法,那么最后贴一个兼容方法。

    function addEventHandler(obj, type, fn) {
      // obj是元素对象,type是要绑定的事件类型,fn是函数
      // 能力判断
      if(element.addEventListener) { // Chrome,Firefox等浏览器,IE9及以上
        element.addEventListener(type, fn, false);
      } else if (element.attachEvent) { // IE8及以下 
        element.attachEvent("on" + type, fn);
      } else {
        element["on" + type] = fn;
      }
    }

    那么如果是移除事件呢?也有兼容性方法的,自己想想怎么写吧!基本类似。

     

    11、说说对var, let, const的理解

    每次面对这种XXX的理解的问题就很头大,只能尽力去把自己知道的表达出来。

    我当时的回答:

    var可以重复声明,存在声明提升,绑定全局作用域,例:

    // 一般来说直接
    console.log(test);
    // 是会报错的,因为test没有声明。这大家都理解
    
    // 那么下面这段代码缺会输出undefined
    console.log(test);
    var test = 1;
    
    // 因为上面这段代码等同于
    var test;
    console.log(test);
    test = 1;
    // var的变量提升会把声明变量提升到"当前作用域"的顶部
    // 同时var还会绑定全局作用域,直接用代码理解,前提是var在全局作用域中声明使用
    var test = 'test';
    console.log(test); // test
    console.log(window.test); // test

    但是const和let不存在声明提升,也不会绑定全局作用域,那么let和const之间也有区别

    const用于声明常量,值被设定以后不能修改,否则报错,

    不过const可以理解为不可修改绑定,可以修改其属性值,看代码

    const num = 1;
    num = 2; // 报错
    
    const obj = { name: '张三' };
    obj.name = '李四';
    obj.sex = '男';
    console.log(obj); // { name: '李四', sex: '男' };

    而且let和const还有一个特性,临时死区(Temporal Dead Zone),简写为 TDZ (划重点!!!)。

    之前在了解到let和const不会提升的时候,我就想过一个问题

    var name = '张三';
    function fn() {
      console.log(name);
    }
    fn();
    // 这里会输出张三,因为js的作用域链,在自己的作用域找不到该变量时会去到上一级作用域找该变量
    
    // 那么如果是以下情况
    const name = '张三';
    function fn() {
      console.log(name); // 此时fn的作用域未声明name
      const name = '李四'; // 不会提升变量
      console.log(name);
    }
    fn();

    当初我的想法是,第一句console未声明name,那么会去上一级作用域找name,输出“张三”。
    第二句console因为上一句代码声明了name,输出李四。
    结果以上想法是错的,程序直接报错,就是因为临时死区的存在才会报错。
    在fn中只要const或者let声明了一个变量,虽然不会提升到顶部,但会把变量放在DTZ中。
    这样在声明之前访问变量就会报错,执行过变量声明语句之后才会从DTZ中移出来,我把这理解为一种另类的提升。

     

    12、浏览器缓存禁用

    实际开发中有这样一个场景,因为http的缓存机制,项目打包上线以后,可能页面加载的js是浏览器中的缓存,并不是最新的js文件,总不能告诉用户去手动清缓存吧?那有什么办法解决呢。

    其实我对http缓存这一块了解不深,但是这个问题我觉得很实用,也是我项目中确实碰到的问题,然后查了下解决方案,有两种比较常用:

      (一) html里面通过meta标签去禁用缓存

    <meta http-equiv="pragma" content="no-cache"> 
    <meta http-equiv="Cache-Contro" content="no-cache, must-revalidate"> 
    <meta http-equiv="expires" content="0">

      我了解不深怕解释不清,就不详说,这几个属性值大家可以自己查查了解,这样印象也会深一些。

    (二)

      css和js带参数(形如.css?time=与.js?time=) ,加一个随机数或者时间戳,这样请求资源的路径变了,就不会加载缓存的资源。 

     

    主要详写的是我容易忘或者比较特别的问题,当然面试还有很多问题,网上有非常多的解释也没有什么特别好说的,

    我在此列举一些,看这篇帖子的同学们也可以去了解一下。

    • 垂直水平居中的几种方法(flex, position等)
    • 移动端页面适配(可以去了解一下手淘flexible.js的方案)
    • http状态码,三次握手四次挥手了解一下
    • 数组有哪些操作方法
    • cookie和本地存储
    • 页面加载性能优化(雪碧图减少http请求之类的)
    • 因为我的技术栈是vue,所以还问了关于vue的东西
    • vue的生命周期,各个周期代表的意义,实际运用(如mounted中获取页面数据)
    • vue的组件通信 ($emit,props等)
    • vue数据双向绑定实现的原理,这个问题5个面试里有3个都问,这个我没写过,一般我就说了下原理思路,可以百度看看,不过大部分说的是通过Object.defineProperty来监听,在vue3.0里面改用了proxy,建议都看看,也算体现对技术更新的热爱。后续我可能写一篇实现双向绑定的demo,也想自己啃一下。

     

    虽然已经准备入职新公司,但是平常会看看有什么好玩的题,会分享在这里,或者另外写,下次再来更新!

    有什么问题都可以给我留言哈~

     

    /* ----------------------------------------------- 2019/3/17 更新------------------------  */

  • 相关阅读:
    Java 第十一届 蓝桥杯 省模拟赛 梅花桩
    Java 第十一届 蓝桥杯 省模拟赛 梅花桩
    Java 第十一届 蓝桥杯 省模拟赛 梅花桩
    Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量
    Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量
    Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量
    Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离
    Java 第十一届 蓝桥杯 省模拟赛 递增序列
    Java 第十一届 蓝桥杯 省模拟赛 递增序列
    Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离
  • 原文地址:https://www.cnblogs.com/jeodeng/p/10473636.html
Copyright © 2020-2023  润新知