社招中级前端笔试面试题总结-答案及拓展

最近看到有一篇文章总结了一些前端的面试题,面向的对象应该是社招中初、中级的前端,感受有必定的参考价值,所以开一个帖子尝试解答这些问题,顺便当作本身的面试题积累。
原文连接戳这里
JavaScript基础 一、声明提早类问题 在网上找到一篇文章,里面有一道面试题,考察了包括变量定义提高、this指针指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级等许多知识点,而就其中声明提早相关的知识,我以为也十分有参考价值:javascript

function Foo() {
    getName = function () { alert (1); };
    return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}

// 请写出如下输出结果:
Foo.getName();
getName(); // 声明提早
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

这道题的答案是:二、四、一、一、二、三、3。
这里考察声明提早的题目在代码中已经标出,这里声明getName方法的两个语句:php

var getName = function () { alert (4) };
function getName() { alert (5) }

实际上在解析的时候是这样的顺序:css

function getName() { alert (5) }
var getName;
getName = function () { alert (4) };

若是咱们在代码中间再加两个断点:html

getName(); // 5
var getName = function () { alert (4) };
getName(); // 4
function getName() { alert (5) }

在第一次getName时,function的声明和var的声明都被提早到了第一次getName的前面,而getName的赋值操做并不会提早,单纯使用var的声明也不会覆盖function所定义的变量,所以第一次getName输出的是function声明的5; 而第二次getName则是发生在赋值语句的后面,所以输出的结果是4,因此实际代码的执行顺序是这样:前端

function getName() { alert (5) }
var getName;
getName(); // 5
getName = function () { alert (4) };
getName(); // 4

二、浏览器存储 localStorage,sessionStorage和cookie的区别 共同点:都是保存在浏览器端、仅同源可用的存储方式
数据存储方面
cookie数据始终在同源的http请求中携带(即便不须要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,能够限制cookie只属于某个路径下
sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。
存储数据大小
存储大小限制也不一样,cookie数据不能超过4K,同时由于每次http请求都会携带cookie、因此cookie只适合保存很小的数据,如会话标识。
sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,能够达到5M或更大
数据存储有效期
sessionStorage:仅在当前浏览器窗口关闭以前有效;
localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,所以用做持久数据;
cookie:只在设置的cookie过时时间以前有效,即便窗口关闭或浏览器关闭
做用域不一样
sessionStorage不在不一样的浏览器窗口中共享,即便是同一个页面;
localstorage在全部同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在
cookie: 也是在全部同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在
三、跨域 不久我写了一个帖子,对同源策略及各类跨域的方式进行了总结:什么是跨域,为何浏览器会禁止跨域,及其引发的发散性学习
四、Promise的使用及原理 Promise是ES6加入的新特性,用于更合理的解决异步编程问题,关于用法阮一峰老师在ECMAScript 6 入门中做出了详细的说明,在此就不重复了。
30分钟,让你完全明白Promise原理
上面这篇文章则是对Promise的原理进行的详细的说明,在这里,我提取最简单的Promise实现方式来对Promise的原理进行说明:vue

function Promise(fn) {
    var value = null,
        callbacks = [];  // callbacks为数组,由于可能同时有不少个回调

    this.then = function (onFulfilled) {
        callbacks.push(onFulfilled);
    };

    function resolve(value) {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }

    fn(resolve);
}

首先,java

then

里面声明的单个或多个函数,将被推入面试

callbacks

列表,在Promise实例调用vuex

resolve

方法时遍历调用,并传入编程

resolve

方法中传入的参数值。
如下,使用一个简单的例子来对Promise的执行流程进行分析:

functionm func () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve('complete')
        }, 3000);
    })
}

func().then(function (res) {
    console.log(res); // complete
})
func

函数的定义是返回了一个Promise实例,声明实例时传入的回调函数加入了一个

resolve

参数(这个

resolve

参数在Promise中的

fn(resolve)

定义中获取

resolve

的函数实体),回调中执行了一个异步操做,在异步操做完成的回调中执行了

resolve

函数。
再看执行步骤,

func

函数返回了一个Promise实例,实例则能够执行Promise构造函数中定义的

then

方法,

then

方法中传入的回调则会在

resolve

(即异步操做完成后)执行,由此实现了经过

then

方法执行异步操做完成后回调的功能。
五、JavaScript事件循环机制 原文中贴出的文章具备很大参考价值,先贴个连接:详解JavaScript中的Event Loop(事件循环)机制。
JavaScript是一种单线程、非阻塞的语言,这是因为它当初的设计就是用于和浏览器交互的:
单线程:

JavaScript

设计为单线程的缘由是,最开始它最大的做用就是和

DOM

进行交互,试想一下,若是

JavaScript

是多线程的,那么当两个线程同时对

DOM

进行一项操做,例如一个向其添加事件,而另外一个删除了这个

DOM

,此时该如何处理呢?所以,为了保证不会 发生相似于这个例子中的情景,

JavaScript

选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。
非阻塞:当代码须要进行一项异步任务(没法马上返回结果,须要花必定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(

pending

)这个任务,而后在异步任务返回结果的时候再根据必定规则去执行相应的回调。而

JavaScript

实现异步操做的方法就是使用Event Loop。

setTimeout(function () {
    console.log(1);
});

new Promise(function(resolve,reject){
    console.log(2)
    resolve(3)
}).then(function(val){
    console.log(val);
})

下面经过一段代码来分析这个问题,首先

setTimeout

Promise

中的

then

回调都是异步方法,而

new Promise

则是一个同步操做,因此这段代码应该首先会当即输出

2

JavaScript

将异步方法分为了

marco task

(宏任务:包括

setTimeout

setInterval

等)和

micro task

(微任务:包括

new Promise

等),在

JavaScript

的执行栈中,若是同时存在到期的宏任务和微任务,则会将微任务先所有执行,再执行第一个宏任务,所以,两个异步操做中

then

的回调会率先执行,而后才执行

setTimeout

的回调,所以会依次输出三、1,因此最终输出的结果就是二、三、1。
六、ES6做用域及let和var的区别 这个问题阮一峰老师在ECMAScript 6 入门中的

let 和 const 命令

章节对这个问题做出了详细的说明,下面提取一些我认为关键的点进行讲解。
ES6引入了使用

{}

包裹的代码区域做为块级做用域的声明方式,其效果与ES5中

function

声明的函数所生成的函数做用域具备相同的效果,做用域外部不能访问做用域内部声明的函数或变量,这样的声明在ES6中对于包括

for () {}

if () {}

等大括号包裹的代码块中都会生效,生成一个单独的做用域。
ES6新增的

let

声明变量的方式相比

var

具备如下几个重要特色:

let

声明的变量只在做用域内有效,以下方代码,

if

声明生成了一个块级做用域,在这个做用域内声明的变量在做用域外部没法访问,假如访问会产生错误:

if (true) {
    let me = 'handsome boy';
}
console.log(me); // ReferenceError
let

声明的变量与

var

不一样,不会产生变量提高,以下方代码,在声明以前输出代码,会产生错误:

// var 的状况
console.log(foo); // 输出undefined
var foo = 2;

// let 的状况
console.log(bar); // ReferenceError
let bar = 2;
let

的声明方式不容许重复声明,如重复声明会报错,而

var

声明变量时,后声明的语句会对先声明的语句进行覆盖:

// 报错
function func() {
  let a = 10;
  var a = 1;
}

// 报错
function func() {
  let a = 10;
  let a = 1;
}

只要块级做用域内存在

let

命令,它所声明的变量就“绑定”(

binding

)这个区域,再也不受外部的影响,这个特性称为

暂时性死区

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

七、闭包 待补充
八、原型及原型链 待补充
九、浏览器的回流与重绘 (Reflow & Repaint) 参考:https://juejin.im/post/5a9923...
浏览器在接收到

html

css

后,渲染的步骤是:

html

通过渲染生成

DOM

树,

css

通过渲染生成

css

渲染树,二者再通过结合,生成

render tree

,浏览器就能够根据

render tree

进行画面绘制。
若是浏览器从服务器接收到了新的

css

,须要更新页面时,须要通过什么操做呢?这就是回流

reflow

与重绘

repaint

。因为浏览器在从新渲染页面时会先进行

reflow

再进行

repaint

,所以,回流必将引发重绘,而重绘不必定会引发回流。
重绘:当前元素的样式(背景颜色、字体颜色等)发生改变的时候,咱们只须要把改变的元素从新的渲染一下便可,重绘对浏览器的性能影响较小。发生重绘的情形:改变容器的外观风格等,好比

background:black

等。改变外观,不改变布局,不影响其余的

DOM

。    回流:是指浏览器为了从新渲染部分或者所有的文档而从新计算文档中元素的位置和几何构造的过程。
由于回流可能致使整个

DOM

树的从新构造,因此是性能的一大杀手,一个元素的回流致使了其全部子元素以及

DOM

中紧随其后的祖先元素的随后的回流。下面贴出会触发浏览器

reflow

的变化:
页面首次渲染
浏览器窗口大小发生改变
元素尺寸或位置发生改变
元素内容变化(文字数量或图片大小等等)
元素字体大小变化
添加或者删除可见的DOM元素
激活CSS伪类(例如::hover)
查询某些属性或调用某些方法
优化方案: CSS 避免使用

table

布局。
尽量在

DOM

树的最末端改变

class


避免设置多层内联样式。
将动画效果应用到

position

属性为

absolute

fixed

的元素上。
避免使用

CSS

表达式(例如:

calc()

)。
JavaScript 避免频繁操做样式,最好一次性重写

style

属性,或者将样式列表定义为

class

并一次性更改

class

属性。
避免频繁操做

DOM

,建立一个

documentFragment

,在它上面应用全部

DOM

操做,最后再把它添加到文档中。
也能够先为元素设置

display: none

,操做结束后再把它显示出来。由于在

display

属性为

none

的元素上进行的

DOM

操做不会引起回流和重绘。
避免频繁读取会引起回流/重绘的属性,若是确实须要屡次使用,就用一个变量缓存起来。
对具备复杂动画的元素使用绝对定位,使它脱离文档流,不然会引发父元素及后续元素频繁回流。
十、JS对象的深复制 通常的思路就是递归解决,对不一样的数据类型作不一样的处理:

function deepCopy (obj) {
  let result = {}
  for (let key in obj) {
    if (obj[key] instanceof Object || obj[key] instanceof Array) {
      result[key] = deepCopy(obj[key])
    } else {
      result[key] = obj[key]
    }
  }
  return result
}

这个只能复制内部有数组、对象或其余基础数据类型的对象,假若有一些像

RegExp

Date

这样的复杂对象复制的结果就是一个

{}

,没法正确进行复制,由于没有对这些特殊对象进行单独的处理。若要参考对复杂对象进行复制,能够参考

lodash

中数组深复制方法

_.cloneDeep()

的实现方案,下面这篇文章对数组深复制的方法进行了详细的解析,有必定参考价值: http://jerryzou.com/posts/div...
另外若是要复制的对象数据结构较为简单,没有复杂对象的数据,那么能够用最简便的方法:

let cloneResult = JSON.parse(JSON.stringify(targetObj))

十一、JS运算精度丢失 此前转载了一篇文章,对JavaScript运算精度丢失的缘由及解决方案都有比较详细的说明: https://blog.csdn.net/qq_3527...
浏览器相关 一、浏览器从加载到渲染的过程,好比输入一个网址到显示页面的过程 加载过程: 浏览器根据 DNS 服务器解析获得域名的 IP 地址
向这个 IP 的机器发送 HTTP 请求
服务器收到、处理并返回 HTTP 请求
浏览器获得返回内容
渲染过程: 根据 HTML 结构生成 DOM 树
根据 CSS 生成 CSSOM
将 DOM 和 CSSOM 整合造成 RenderTree
根据 RenderTree 开始渲染和展现
遇到

<script>

时,会执行并阻塞渲染
二、浏览器缓存机制 参考文章:https://segmentfault.com/a/11...
三、性能优化 参考文章:https://blog.csdn.net/na_sama...
Vue 一、组件间通讯方式 Vue的官方文档对组件间的通讯方式作了详细的说明:https://cn.vuejs.org
父组件向子组件传输 最经常使用的方式是在子组件标签上传入数据,在子组件内部用

props

接收:

// 父组件
<template>
  <children name="boy"></children>
</template>

// 子组件:children
export default {
  props: {
    name: String
  }
}

还能够在子组件中用

this.$parent

访问父组件的实例,不过官方文档有这样一段文字,很好的说明了

$parent

的意义:节制地使用

$parent

$children

—— 它们的主要目的是做为访问组件的应急方法。更推荐用

props

events

实现父子组件通讯。
子组件向父组件传输 通常在子组件中使用

this.$emit('eventName', 'data')

,而后在父组件中的子组件标签上监听

eventName

事件,并在参数中获取传过来的值。

// 子组件
export default {
  mounted () {
    this.$emit('mounted', 'Children is mounted.')
  }
}

// 父组件
<template>
  <children @mounted="mountedHandle"></children>
</template>

<script>
export default {
  methods: {
    mountedHandle (data) {
      console.log(data) // Children is mounted.
    }
  }
}
</script>

$parent

同样,在父组件中能够经过访问

this.$children

来访问组件的全部子组件实例。
非父子组件之间的数据传递 对于非父子组件间,且具备复杂组件层级关系的状况,能够经过

Vuex

进行组件间数据传递: https://vuex.vuejs.org/zh/

Vue 1.0

中经常使用的

event bus

方式进行的全局数据传递,在

Vue 2.0

中已经被移除,官方文档中有说明:

$dispatch

$broadcast

已经被弃用。请使用更多简明清晰的组件间通讯和更好的状态管理方案,如:

Vuex

二、双向绑定原理 https://blog.seosiwei.com/det... https://blog.seosiwei.com/detail/36 https://blog.seosiwei.com/det...
三、路由导航钩子 https://router.vuejs.org/zh-c...
转载于猿2048:☞《社招中级前端笔试面试题总结-答案及拓展》

相关文章
相关标签/搜索