2019年前端面试题03

12次阅读

共计 6496 个字符,预计需要花费 17 分钟才能阅读完成。

  1. let 与 var 的区别?
    Let 为 ES6 新添加申明变量的命令,它类似于 var,但是有以下不同:
    1、var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
    2、let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
    3、let 不允许重复声明.
  2. 封装一个函数,参数是定时器的时间,.then 执行回调函数。

    function sleep (time) {return new Promise((resolve) => setTimeout(resolve, time));
    }
  3. 项目做过哪些性能优化?
    1、减少 HTTP 请求数
    2、减少 DNS 查询
    3、使用 CDN
    4、避免重定向
    5、图片懒加载
    6、减少 DOM 元素数量
    7、减少 DOM 操作
    8、使用外部 JavaScript 和 CSS
    9、压缩 JavaScript、CSS、字体、图片等
    10、优化 CSS Sprite
    11、使用 iconfont
    12、字体裁剪
    13、多域名分发划分内容到不同域名
    14、尽量减少 iframe 使用
    15、避免图片 src 为空
    16、把样式表放在 中
    17、把脚本放在页面底部
  4. 怎么判断两个对象相等?
    1、转化成字符串后比较字符串是否一致:

    JSON.stringify(obj)===JSON.stringify(obj2);

    2、Object.is(obj1,obj2):判断两个值是否 相同。如果下列任何一项成立,则两个值相同。

       * 两个值都是 [undefined](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined) 
       * 两个值都是 [null](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/null) 
       * 两个值都是 true 或者都是 false
       * 两个值是由相同个数的字符按照相同的顺序组成的字符串
       * ~ 两个值指向同一个对象~
       * 两个值都是数字并且
           * 都是正零 +0
           * 都是负零 -0
           * 都是 [NaN]
           * 都是除零和 [NaN] 外的其它同一个数字
       
    
  5. 什么是模块化开发?

    实现 mvvm 主要包含两个方面,数据变化更新视图,视图变化更新数据:
    图片描述

    关键点在于 data 如何更新 view,因为 view 更新 data 其实可以通过事件监听即可,比如 input 标签监听 ‘input’ 事件就可以实现了。所以我们着重来分析下,当数据改变,如何更新视图的。
    数据更新视图的重点是如何知道数据变了,只要知道数据变了,那么接下去的事都好处理。如何知道数据变了,其实上文我们已经给出答案了,就是通过 Object.defineProperty()对属性设置一个 set 函数,当数据改变了就会来触发这个函数,所以我们只要将一些需要更新的方法放在这里面就可以实现 data 更新 view 了。

    Vue 是通过 Object.defineProperty()来实现数据劫持的。它可以来控制一个对象属性的一些特有操作,比如 set()、get()、是否可以枚举。
    图片描述
    实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器 Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者 Watcher 看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器 Dep 来专门收集这些订阅者,然后在监听器 Observer 和订阅者 Watcher 之间进行统一管理的。接着,我们还需要有一个指令解析器 Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者 Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者 Watcher 接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下 3 个步骤,实现数据的双向绑定:
    1. 实现一个监听器 Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
    2. 实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
    3. 实现一个解析器 Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

    流程图如下:
    图片描述

  6. 浏览器兼容性问题
    图片描述
    1、Normalize.css
    2、不同浏览器的标签默认的外边距和内边距不同:
    3、IE6 双边距问题:在 IE6 中设置了 float , 同时又设置 margin , 就会出现边距问题:设置 display:inline;
    4、图片默认有间距:使用 float 为 img 布局
    5、IE9 以下浏览器不能使用 opacity:

    opacity: 0.5;
    filter: alpha(opacity = 50);
    filter: progid:DXImageTransform.Microsoft.Alpha(style = 0, opacity = 50);

    6、cursor:hand 显示手型在 safari 上不支持:统一使用 cursor:pointer
    7、当标签的高度设置小于 10px,在 IE6、IE7 中会超出自己设置的高度:超出高度的标签设置 overflow:hidden, 或者设置 line-height 的值小于你的设置高度
    8、CSS HACK 的方法:

    height: 100px;    // 所有浏览器 通用 
    _height: 100px;    // IE6 专用 
    *+height: 100px;    // IE7 专用 
    *height: 100px;    // IE6、IE7 共用 
    height: 100px !important;        // IE7、FF 共用 

    代码的顺序一定不能颠倒了,要不又前功尽弃了。因为浏览器在解释程序的时候,如果重名的话,会用后面的覆盖前面的,就象给变量赋值一个道理,所以我们把通用的放前面,越专用的越放后面

  7. 前端跨域

    1. 什么是跨域?

      >     只要协议、域名、端口有任何一个不同,都被当作是不同的域。>     同源策略是浏览器的行为,是为了保护本地数据不被 JavaScript 代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。>     之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。> 
      > 注意点:> 如果是协议和端口造成的跨域问题“前台”是无能为力的;> 在跨域问题上,域仅仅是通过“URL 的首部”来识别而不会去尝试判断相同的 ip 地址对应着两个域或两个域是否在同一个 ip 上。(“URL 的首部”指 window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。)
      
    2. 什么是同源策略?

      > 所谓同源是指,域名,协议,端口相同。浏览器采用同源策略,就是禁止页面加载或执行与自身来源不同的域的任何脚本。
    3. 通过 document.domain 跨域(只适用于不同子域的框架间的交互)

      浏览器有一个同源策略,其限制之一是不能通过 ajax 的方法去请求不同源中的文档。第二个限制是浏览器中不同域的框架之间是不能进行 js 的交互操作的。不同的框架之间是可以获取 window 对象的,但却无法获取相应的属性和方法。比如,有一个页面,它的地址是 [www.damonare.cn/a.html],在这个页面里面有一个 iframe,它的 src 是 [damonare.cn/b.html],很显然,这个页面与它里面的 iframe 框架是不同域的,所以我们是无法通过在页面中书写 js 代码来获取 iframe 中的东西的:
    4. 通过 location.hash 跨域
    5. 通过 HTML5 的 postMessage 方法跨域
    6. 通过 jsonp 跨域

      JSONP 的优缺点
          * JSONP 的优点是:它不像 XMLHttpRequest 对象实现的 Ajax 请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要 XMLHttpRequest 或 ActiveX 的支持;并且在请求完毕后可以通过调用 callback 的方式回传结果。* JSONP 的缺点则是:它只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求;它只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题。
    7. 通过 CORS 跨域

      CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS 背后的基本思想就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

    8. 通过 window.name 跨域

      window 对象有个 name 属性,该属性有个特征:即在一个窗口 (window) 的生命周期内, 窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

  8. 前端优化:浏览器缓存

    前端优化:浏览器缓存技术介绍 – 掘金
    浏览器缓存分为强缓存和协商缓存。

    当客户端请求某个资源时,获取缓存的流程如下:
    1)浏览器在加载资源时,先根据这个资源的一些 http header 判断它是否命中强缓存,强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。比如:某个 css 文件,如果浏览器在加载它所在的网页时,这个 css 文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个 css,连请求都不会发送到网页所在服务器;

    2)当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些 http header 验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回,但是不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源;

    3)强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器。

    4)当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据。

  9. jsonp 的原理与实现

    Jsonp 是一种跨域通信的手段,它的原理其实很简单:

       > 1. 首先是利用 script 标签的 src 属性来实现跨域。> 2. 通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。> 3. 由于使用 script 标签的 src 属性,因此只支持 get 方法
    

    实现流程:

    1、设定一个 script 标签

    <script src="http://jsonp.js?callback=xxx"></script>

    2、callback 定义了一个函数名,而远程服务端通过调用指定的函数并传入参数来实现传递参数,将 function(response)传递回客户端

    $callback = !empty($_GET['callback']) ? $_GET['callback'] : 'callback';
    echo $callback.'(.json_encode($data).)';

    3、客户端接收到返回的 js 脚本,开始解析和执行 function(response)

    简单的实例:
    一个简单的 jsonp 实现,其实就是拼接 url,然后将动态添加一个 script 元素到头部。

    function jsonp(req){var script = document.createElement('script');
        var url = req.url + '?callback=' + req.callback.name;
        script.src = url;
        document.getElementsByTagName('head')[0].appendChild(script); 
    }

    前端 js 示例:

    function hello(res){alert('hello' + res.data);
    }
    jsonp({
        url : '',
        callback : hello 
    });

    服务器端代码:

    var http = require('http');
    var urllib = require('url');
    
    var port = 8080;
    var data = {'data':'world'};
    
    http.createServer(function(req,res){var params = urllib.parse(req.url,true);
        if(params.query.callback){console.log(params.query.callback);
            //jsonp
            var str = params.query.callback + '(' + JSON.stringify(data) + ')';
            res.end(str);
        } else {res.end();
        }
        
    }).listen(port,function(){console.log('jsonp server is on');
    });
    • 可靠的 jsonp 实例:

      (function (global) {
          var id = 0,
              container = document.getElementsByTagName("head")[0];
      
          function jsonp(options) {if(!options || !options.url) return;
      
              var scriptNode = document.createElement("script"),
                  data = options.data || {},
                  url = options.url,
                  callback = options.callback,
                  fnName = "jsonp" + id++;
      
              // 添加回调函数
              data["callback"] = fnName;
      
              // 拼接 url
              var params = [];
              for (var key in data) {params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
              }
              url = url.indexOf("?") > 0 ? (url + "&") : (url + "?");
              url += params.join("&");
              scriptNode.src = url;
      
              // 传递的是一个匿名的回调函数,要执行的话,暴露为一个全局方法
              global[fnName] = function (ret) {callback && callback(ret);
                  container.removeChild(scriptNode);
                  delete global[fnName];
              }
      
              // 出错处理
              scriptNode.onerror = function () {callback && callback({error:"error"});
                  container.removeChild(scriptNode);
                  global[fnName] && delete global[fnName];
              }
      
              scriptNode.type = "text/javascript";
              container.appendChild(scriptNode)
          }
      
          global.jsonp = jsonp;
      
      })(this);
使用示例:```js
jsonp({
    url : "www.example.com",
    data : {id : 1},
    callback : function (ret) {console.log(ret);
    }
});
```

年轻的前端攻城狮一枚,整理不易,感谢阅读、点赞和收藏,也欢迎大家一起交流分享前端知识技术!
2019 年前端面试题 -01
2019 年前端面试题 -02
2019 年前端面试题 -03

正文完
 0