2019前端面试题整理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37793545/article/details/88310376
1.写出下面程序的运行结果:
console.log([] == ![])	// true
console.log([] == [])	// false
console.log([] == {})	// false
console.log(new String('a') == new String('a')) // false
console.log(isNaN(NaN))	// true
console.log(isNaN('32131dsafdas'))	// true
console.log(NaN === NaN)	// false
console.log(NaN === undefined)	// false
console.log(undefined === undefined)	// true
console.log(typeof NaN)	 // number

console.log({} + [])	// 0
console.log([] + {})	// "[object Object]"
console.log({} + {})	// "[object Object][object Object]"
console.log([]+ [])	// ""
console.log({} + 1)	// 1
console.log([] + 1)	// "1"

隐式类型转换参考:https://blog.csdn.net/m0_37793545/article/details/87862847

2.写出下面程序的运行结果:
  var name = 'world';
  (function () {
    if (typeof name === 'undefined') {
      var name = 'jack'
      console.log('hi' + name)
    } else {
      console.log('hello' + name)
    }
  })()
  
  // hijack
3.写出下面程序的运行结果:
  var test = (function (a) {
    this.a = a;
    return function (b) {
      return this.a + b
    }
  }(function (a, b) {
    return a
  }(1, 2)))
  test(4)
  
  // 5
4.写出下面程序的运行结果:
  var x = 1,
    y = z = 0

  function add(n) {
    return n = n + 1
  }
  y = add(x)

  function add(n) {
    return n = n + 3
  }
  z = add(x)
  console.log(x, y, z)
  
  // 1 4 4
5.写出下面程序的运行结果:
  function bar() {
    return foo;
    foo = 10;

    function foo() {}
    var foo = 11
  }
  alert(typeof bar())

  // function
6.写出下面程序的运行结果:
  var x = 3;
  var foo = {
    x: 2,
    baz: {
      x: 1,
      bar: function () {
        return this.x
      }
    }
  }
  var go = foo.baz.bar
  go()
  foo.baz.bar()
  
  // 3
  // 1
7.写出下面程序的运行结果:
  console.log([1, 2] + [2, 1])
  
  // 1,2,2,1
8.判断一串字符串是否为回文
  var test = '12344321'
  var test1 = test.split('').reverse().join('')
  console.log(test === test1)
9.数组去重,数组排序
  Array.from(new Set([1,2,3,5,2,1,22,'1']))

更多去重方式:https://juejin.im/post/5949d85f61ff4b006c0de98b
数组排序总结:https://juejin.im/post/57dcd394a22b9d00610c5ec8#heading-24

10.js继承的实现方式
  // (1) 借助构造函数实现继承
  function Parent() {
    this.name = 'parent'
  }
  function Child() {
    Parent.call(this)
    this.age = 18
  }
  // 这种方式只能实现部分继承,即父类的构造方法中的属性,子类可以继承,其缺点是,父类原型上的属性或方法,子类无法继承。
  // (2)借助原型链实现继承
  function Parent() {
    this.name = 'parent'
    this.play = [1, 2, 3]
  }
  function Child() {
    this.age = 18
  }
  Child.prototype = new Parent()
  // 这种继承方式的缺点是用子类Child实例化两个对象后,var s1 = new Child(); var s2 = new Child(); s1.play.push(4); console.log(s2.play); 也会打印出[1,2,3,4],其原因是两个对象的__proto__指向了同一个原型对象。
  // (3)组合方式(继承的完美实现)
  function Parent() {
    this.name = 'parent'
  }
  function Child() {
    Parent.call(this)
    this.age = 18
  }
  Child.prototype = Object.create(Parent.prototype)
  Child.prototype.constructor = Child
11.JavaScript事件循环机制相关问题
// 请写出以下代码执行的顺序

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

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

// 2 4 3 1
在同步代码执行完成后才回去检查是否有异步任务完成,并执行对应的回调,而微任务又会在宏任务之前执行。
同步代码=>异步代码(微任务=>宏任务)
宏任务: setTimeout
微任务: Promise.then
12.伪数组转数组
var a={length:2,0:'aaa',1:'bbb'};  
// ES6:
Array.from(a)
// ES5:
Array.prototype.slice.call(a);//  ["aaa", "bbb"]   

var a={length:2};  
Array.prototype.slice.call(a);//  [undefined, undefined]  
13.写出下面程序的运行结果:
  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() // 2
  getName() // 4
  Foo().getName() // 1
  getName() // 1
  new Foo.getName() // 2
  new Foo().getName() // 3
  new new Foo().getName() // 3
  // 详细解释 https://www.jb51.net/article/79461.htm
14. 函数防抖与函数节流

(1) 函数防抖

    function debounce(func, delay) {
      var timeout
      return function(e) {
        console.log('清除', timeout, e.target.value)
        clearTimeout(timeout)
        var context = this,
          args = arguments
        console.log('新的', timeout, e.target.value)
        timeout = setTimeout(function() {
          console.log('----')
          func.apply(context, args)
        }, delay)
      }
    }

    var validate = debounce(function(e) {
      console.log('change', e.target.value, new Date() - 0)
    }, 380)
    // 绑定监听
    document.querySelector('input').addEventListener('input', validate)

(2) 函数节流

	// 函数节流
	    function throttle(fn, threshhold) {
      var timeout
      var start = new Date()
      var threshhold = threshhold || 160
      return function() {
        var context = this,
          args = arguments,
          curr = new Date() - 0

        clearTimeout(timeout) //总是干掉事件回调
        if (curr - start >= threshhold) {
          console.log('now', curr, curr - start) //注意这里相减的结果,都差不多是160左右
          fn.apply(context, args) //只执行一部分方法,这些方法是在某个时间段内执行一次
          start = curr
        } else {
          //让方法在脱离事件后也能执行一次
          timeout = setTimeout(function() {
            fn.apply(context, args)
          }, threshhold)
        }
      }
    }
    var mousemove = throttle(function(e) {
      console.log(e.pageX, e.pageY)
    })

    // 绑定监听
    document.querySelector('#panel').addEventListener('mousemove', mousemove)

15. 什么是hash函数

hash(散列、杂凑)函数,是将任意长度的数据映射到有限长度的域上。直观解释起来,就是对一串数据m进行杂糅,输出另一段固定长度的数据h,作为这段数据的特征(指纹)。也就是说,无论数据块m有多大,其输出值h为固定长度。

hashmap,hash值(key)存在的目的是加速键值对的查找

详解:https://www.zhihu.com/question/26762707

16. Vue 组件 data 为什么必须是函数

每个实例可以维护一份被返回对象的独立的拷贝,否则;将导致多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响。

17. jQuery.extend()和jQuery.fn.extend()的区别
jQuery.extend()为扩展jQuery类本身.为类添加新的方法。如 $.wang()
jquery.fn.extend(object);给jQuery对象添加方法。如 $('#test').wang()
18. Vue中的虚拟Dom diff 算法
(1)所谓的virtual dom,也就是虚拟节点。它通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点
dom diff 则是通过JS层面的计算,返回一个patch对象,即补丁对象,在通过特定的操作解析patch对象,完成页面的重新渲染
(2)DOM DIFF
比较两棵DOM树的差异是Virtual DOM算法最核心的部分.简单的说就是新旧虚拟dom 的比较,如果有差异就以新的为准,然后再插入的真实的dom中,重新渲染。
比较只会在同层级进行, 不会跨层级比较。新旧虚拟dom比较的时候,是先同层比较,同层比较完看看时候有儿子,有则需要继续比较下去,直到没有儿子。

比较后会出现四种情况:
1、此节点是否被移除 -> 添加新的节点 
2、属性是否被改变 -> 旧属性改为新属性
3、文本内容被改变-> 旧内容改为新内容
4、节点要被整个替换 -> 结构完全不相同 移除整个替换

详解:https://juejin.im/post/5ad6182df265da23906c8627

19.常见Http状态码
// 100  Continue  继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
// 200  OK   正常返回信息
// 201  Created  请求成功并且服务器创建了新的资源
// 202  Accepted  服务器已接受请求,但尚未处理
// 301  Moved Permanently  请求的网页已永久移动到新位置。
// 302 Found  临时性重定向。
// 303 See Other  临时性重定向,且总是使用 GET 请求新的 URI。
// 304  Not Modified  自从上次请求后,请求的网页未修改过。

// 400 Bad Request  服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。
// 401 Unauthorized  请求未授权。
// 403 Forbidden  禁止访问。
// 404 Not Found  找不到如何与 URI 相匹配的资源。

// 500 Internal Server Error  最常见的服务器端错误。
// 503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。
20. 什么情况出现跨域,如何解决?

同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。若地址里面的协议、域名和端口号均相同则属于同源。

jsonp跨域、nginx反向代理、node.js中间件代理跨域、后端设置http header、后端在服务器上设置cors。

21. 重绘和回流

重绘:当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

回流:当Render Tree(DOM)中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
回流要比重绘消耗性能开支更大。
回流必将引起重绘,重绘不一定会引起回流。

详解:https://juejin.im/post/5a9923e9518825558251c96a

22. 浏览器从加载到渲染的过程,比如输入一个网址到显示页面的过程。

加载过程:

  • 浏览器根据 DNS 服务器解析得到域名的 IP 地址
  • 向这个 IP 的机器发送 HTTP 请求 服务器收到、处理并返回 HTTP 请求
  • 浏览器得到返回内容

渲染过程:

  • 根据 HTML 结构生成 DOM 树
  • 根据 CSS 生成 CSSOM
  • 将 DOM 和 CSSOM 整合形成 RenderTree
  • 根据 RenderTree 开始渲染和展示
  • 遇到<script>时,会执行并阻塞渲染
23. 浏览器缓存机制

强制缓存:catch-control,expires,catch-control优先级高。

协商缓存:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。

详解:https://juejin.im/entry/5ad86c16f265da505a77dca4
https://segmentfault.com/a/1190000008377508

24. Vue双向绑定原理

详解:https://juejin.im/post/5acd0c8a6fb9a028da7cdfaf#heading-9

25. 路由导航钩子(导航守卫)

首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。

beforeEach主要有3个参数to,from,next:

  1. to:route即将进入的目标路由对象
  2. from:route当前导航正要离开的路由
  3. next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。

afterEach和beforeResolve(全局解析守卫)没有next参数

路由独享的守卫:beforeEnter,参数同全局前置守卫
组件内的守卫:beforeRouteEnter(组件实例未创建,不能使用this),beforeRouteUpdate,beforeRouteLeave

详解:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

26. Vue的路由模式,有什么区别?

hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。

history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

27. 安全问题,如 XSS 和 CSRF
Xss
  • 跨站脚本攻击,是一种网站应用程序的安全漏洞攻击,是代码注入的一种。常见方式是将恶意代码注入合法代码里隐藏起来,再诱发恶意代码,从而进行各种各样的非法活动。

防范:所有用户输入的都是不可信的,输入过滤和转义,重要cookie设置为HttpOnly

CSRF
  • 跨站请求伪造,也称 XSRF,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。与 XSS 相比,XSS利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。

防范:

  1. 验证Http Referer字段,判断请求来源是否合法,Referer记录了该 HTTP 请求的来源地址
  2. 在请求地址中添加 token 并验证
  3. 在 HTTP 头中自定义属性并验证

详解:https://www.jianshu.com/p/64a413ada155

28. Css实现文本溢出显示省略号
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
29. js实现类似于add(1)(2)(3)调用方式的方法

简单方法:

var add = function(a){
    return function(b){
        return function(c){
            return a+b+c;
        };
    };
};

但是这样的代码在add(1)(2)或者add(1)(2)(3)(4)时便不对了。
正确实现:

function add () {
    var args = Array.prototype.slice.call(arguments);
 
    var fn = function () {
        var arg_fn = Array.prototype.slice.call(arguments);
        return add.apply(null, args.concat(arg_fn));
    }
    
 	// 重写valueOf方法,是为了处理隐式类型转换
    fn.valueOf = function () {
        return args.reduce(function(a, b) {
            return a + b;
        })
    }
 
    return fn;
}

详解:https://www.cnblogs.com/coco1s/p/6509141.html
https://blog.csdn.net/m0_37793545/article/details/87862847

30. 定义一个log方法,让它可以代理console.log的方法(支持传入n个参数)
function log(){ 
 console.log.apply(console, arguments); 
}
31. 当new Foo()时发生了什么
  1. 创建了一个新对象
  2. 将新创建的空对象的隐式原型指向其构造函数的显示原型。
  3. 将this指向这个新对象
  4. 如果无返回值或者返回一个非对象值,则将新对象返回;如果返回值是一个新对象的话那么直接直接返回该对象。
手写一个new实现
function create() {
	// 创建一个空的对象
    var obj = new Object(),
	// 获得构造函数,arguments中去除第一个参数
    Con = [].shift.call(arguments);
	// 链接到原型,obj 可以访问到构造函数原型中的属性
    obj.__proto__ = Con.prototype;
	// 绑定 this 实现继承,obj 可以访问到构造函数中的属性
    var ret = Con.apply(obj, arguments);
	// 优先返回构造函数返回的对象
	return ret instanceof Object ? ret : obj;
};

详解:《JS高程》 6.2.2 《你不知道的Javascript》2.2.4

32. Css样式的百分比都相对于谁?
  • 相对于父级宽度的:
    max-width、min-width、width、left、right、text-indent、padding、margin、grid-template-columns、grid-auto-columns、column-gap 等;

  • 相对于父级高度的:

    max-height、min-height、height、top、bottom、grid-template-rows、grid-auto-rows、row-gap 等;

  • 相对于主轴长度的:

    flex-basis 等;

  • 相对于继承字号的:

    font-size 等;

  • 相对于自身字号的:

    line-height 等;

  • 相对于自身宽高的:

    border-radius、background-size、border-image-width、transform: translate()、transform-origin、zoom、clip-path 等;

  • 相对于行高的:

    vertical-align 等;

  • 特殊算法的:
    background-position (方向长度 / 该方向除背景图之外部分总长度)、border-image-slice (相对于图片尺寸)、filter 系列函数等;

如果自身设置 position: absolute,“父级”指:Boring:破坏文档流的div高度设为百分比是相对谁而言的?

如果 position: fixed,“父级”指视口(父级不存在 transform 为非 none 值的情况下)。

来源:知乎
链接:https://www.zhihu.com/question/36079531/answer/65809167

33. 什么是闭包? 堆栈溢出? 内存泄漏? 哪些操作会造成内存泄漏?怎么样防止内存泄漏?

闭包:就是能够读取其他函数内部变量的函数。
堆栈溢出:就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据越界,结果覆盖了别的数据。经常会在递归中发生。
内存泄露是指:用动态存储分配函数内存空间,在使用完毕后未释放,导致一直占据该内存单元。直到程序结束。指任何对象在您不再拥有或需要它之后仍然存在。

造成内存泄漏:
setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
防止内存泄露:
1、不要动态绑定事件;
2、不要在动态添加,或者会被动态移除的dom上绑事件,用事件冒泡在父容器监听事件;
3、如果要违反上面的原则,必须提供destroy方法,保证移除dom后事件也被移除,这点可以参考Backbone的源代码,做的比较好;
4、单例化,少创建dom,少绑事件。

34. 移动端上什么是点击穿透?如何解决?

点击穿透现象有3种:
点击穿透问题:点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件跨页面点击穿透问题:如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转另一种跨页面点击穿透问题:这次没有mask了,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了

解决方案:
1、只用touch
最简单的解决方案,完美解决点击穿透问题
把页面内所有click全部换成touch事件( touchstart 、’touchend’、’tap’)

2、只用click
下下策,因为会带来300ms延迟,页面内任何一个自定义交互都将增加300毫秒延迟

3、tap后延迟350ms再隐藏mask
改动最小,缺点是隐藏mask变慢了,350ms还是能感觉到慢的

4、pointer-events
比较麻烦且有缺陷, 不建议使用mask隐藏后,给按钮下面元素添上 pointer-events: none; 样式,让click穿过去,350ms后去掉这个样式,恢复响应缺陷是mask消失后的的350ms内,用户可以看到按钮下面的元素点着没反应,如果用户手速很快的话一定会发现

猜你喜欢

转载自blog.csdn.net/m0_37793545/article/details/88310376