zoukankan      html  css  js  c++  java
  • 面试总结(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 更新------------------------  */

  • 相关阅读:
    RIGHT-BICEP单元测试——“二柱子四则运算升级版”
    个人项目之“二柱子四则运算升级版”(续)
    个人项目之 “二柱子四则运算”升级版
    “进度条”博客
    课后实验3--四则运算3
    构建之法阅读笔记01
    第二周学习进度
    四则运算2单元测试
    课后实验2--四则运算2
    课后实验1--四则运算
  • 原文地址:https://www.cnblogs.com/jeodeng/p/10473636.html
Copyright © 2011-2022 走看看