ITPub博客

首页 > 应用开发 > Javascript > 好程序员web前端培训分享JS面试题总结一

好程序员web前端培训分享JS面试题总结一

原创 Javascript 作者:好程序员 时间:2020-07-23 14:32:58 0 删除 编辑

  好程序员 web 前端培训分享 JS 面试题总结一,准备参加面试的小伙伴们一起看一看吧,希望本篇文章能够对从事 web 前端工作的小伙伴们有所帮助。

  一、说说你对闭包的认识

  ( ) 什么是闭包

  一句话解释:

  能够读取其他函数内部变量的函数。

  稍全面的回答:

  在js 中变量的作用域属于函数作用域 , 在函数执行完后 , 作用域就会被清理 , 内存也会随之被回收 , 但是由于闭包函数是建立在函数内部的子函数 , 由于其可访问上级作用域 , 即使上级函数执行完 , 作用域也不会随之销毁 , 这时的子函数 ( 也就是闭包 ), 便拥有了访问上级作用域中变量的权限 , 即使上级函数执行完后作用域内的值也不会被销毁。

  这里涉及到对函数作用域的认识: js 变量分为全局变量和局部变量 ; 函数内部可以直接读取全局变量 , 而在函数外部自然无法读取函数内的局部变量

  ( ) 闭包解决了什么问题

  1. 可以读取函数内部的变量

  2. 让这些变量的值始终保持在内存中。不会在函数调用后被清除

  可以通过下面的代码来帮助理解上面所说的:

  function addCounter() {

  let counter = 0

  const myFunction = function () {

  counter = counter + 1

  return counter

  }

  return myFunction

  }

  const increment = addCounter()

  const c1 = increment()

  const c2 = increment()

  const c3 = increment()

  console.log('increment:', c1, c2, c3);

  // increment: 1 2 3

  在这段代码中increment 实际上就是闭包函数 myFunction, 它一共运行了三次,第一次的值是 1 ,第二次的值是 2 ,第三次的值是 3 。这证明了,函数 addCounter 中的局部变量 counter 一直保存在内存中,并没有在 addCounter 调用后被自动清除。

  ( ) 闭包的应用场景

  在开发中, 其实我们随处可见闭包的身影 , 大部分前端 JavaScript 代码都是“事件驱动”的 , 即一个事件绑定的回调方法 ; 发送 ajax 请求成功 | 失败的回调 ;setTimeout 的延时回调 ; 或者一个函数内部返回另一个匿名函数 , 这些都是闭包的应用。

  下面是具体应用的栗子:

  1. 老掉牙的取正确值问题

  for (var i = 0; i < 10; i++) {

  setTimeout(function () {

  console.log(i) //10 10

  }, 1000)

  }

  怎么取到每一次循环的正确值呢? 闭包这样用 :

  for (var i = 0; i < 10; i++) {

  ((j) => {

  setTimeout(function () {

  console.log(j) //1-10

  }, 1000)

  })(i)

  }

  声明了10 个自执行函数,保存当时的值到内部

  2. 使用闭包模拟私有变量

  私有变量在java 里使用 private 声明就可以了 , 但是在 js 中还没有,但是我们可以使用闭包模拟实现。

  var counter = (function () {

  var privateCounter = 0;

  function changeBy(val) {

  privateCounter += val

  }

  return {

  increment: function () {

  changeBy(1)

  },

  decrement: function () {

  changeBy(-1)

  },

  value: function () {

  return privateCounter

  }

  }

  })();

  counter.value() //0

  counter.increment() //1

  counter.increment() //2

  counter.decrement() //1

  匿名函数已经定义就立即执行, 创建出一个词法环境包含 counter.increment counter.decrement counter.value 三个方法 , 还包含了两个私有项 :privateCounter 变量和 changeBy 函数。这两个私有项无法在匿名函数外部直接访问,必须通过匿名包装器返回的对象的三个公共函数访问。

  ( ) 闭包的缺点

  1. 由于闭包会是的函数中的变量都被保存到内存中 , 滥用闭包很容易造成内存消耗过大 , 导致网页性能问题。解决方法是在退出函数之前,将不再使用的局部变量全部删除。

  2. 闭包可以使得函数内部的值可以在函数外部进行修改。所有,如果你把父函数当作对象 (object) 使用,把闭包当作它的公用方法 (Public Method) ,把内部变量当作它的私有属性 (private value) ,这时一定要小心,不要随便改变父函数内部变量的值。

  二、跨域问题有哪些处理方式

  跨域解决方案

  1. 通过 jsonp 跨域

  2. 跨域资源共享 (CORS)

  3. nodejs 中间件代理跨域

  4. nginx 反向代理中设置 proxy_cookie_domain

  Ⅰ . 通过 jsonp 跨域

  通常为了减轻web 服务器的负载,我们把 js css img 等静态资源分离到另一台 独立域名的服务器上,在 html 页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建 script ,再请求一个带参网址实现跨域通信。

  1. 原生实现

  <script>

  var script = document.createElement('script');

  script.type = 'text/javascript';

  // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数

  script.src = '

  document.head.appendChild(script);

  // 回调执行函数

  function jsonCallback(res) {

  alert(JSON.stringify(res));

  }

  </script>

  服务器端返回如下( 返回即执行全局函数 )

  jsonCallback({"status": 0, "user": "admin"})

  2. jquery 方式实现

  $.ajax({

  url: '

  type: 'get',

  dataType: 'jsonp', // 请求方式为 jsonp

  jsonpCallback: "handleCallback", // 自定义回调函数名

  data: {}

  });

  Ⅱ . 跨域资源共享 (CORS)

  CORS 是一个 W3C 标准,全称是 " 跨域资源共享 "(Cross-origin resource sharing) 跨域资源共享 CORS 详解。看名字就知道这是处理跨域问题的标准做法。 CORS 有两种请求,简单请求和非简单请求。

  · 简单请求

  只要同时满足以下两大条件, 就属于简单请求 :

  1. 请求方法是以下三种方法之一 :

  · HEAD

  · GET

  · POST

  2. HTTP 请求头的信息不超出以下几种字段:

  · Accept

  · Accept-Language

  · Content-Language

  · Last-Event-ID

  · Content-Type :只限于三个值 application/x-www-form-urlencoded multipart/form-data text/plain

  如果是简单请求, 后端处理即可 , 前端什么也不用干 ; 这里注意的是如果前端要带 cookie, 前端也需要单独设置

  · 原生 ajax ( 前端 )

  var xhr = new XMLHttpRequest();

  // 前端设置是否带 cookie

  xhr.withCredentials = true;

  ...

  · jquery ( 前端 )

  $.ajax({

  ...

  xhrFields: {

  withCredentials: true // 前端设置是否带 cookie

  },

  crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含 cookie

  ...

  });

  · vue 中使用 axios ( 前端 )

  axios.defaults.withCredentials = true

  · 后端 node

  可以借助koa2-cors 快速实现

  const path = require('path')

  const Koa = require('koa')

  const koaStatic = require('koa-static')

  const bodyParser = require('koa-bodyparser')

  const router = require('./router')

  const cors = require('koa2-cors')

  const app = new Koa()

  const port = 9871

  ...

  // 处理 cors

  app.use(cors({

  origin: function (ctx) {

  return '

  },

  credentials: true,

  allowMethods: ['GET', 'POST', 'DELETE'],

  allowHeaders: ['t', 'Content-Type']

  }))

  // 路由

  app.use(router.routes()).use(router.allowedMethods())

  // 监听端口

  ...

  Ⅲ .nodejs 中间件代理跨域

  跨域原理: 同源策略是浏览器的安全策略 , 不是 HTTP 协议的一部分。服务器端调用 HTTP 接口只是使用 HTTP 协议, 不会执行 js 脚本 , 不需要检验同源策略 , 也就不存在跨域问题。

  实现思路:通过起一个代理服务器, 实现数据的转发,也可以通过设置cookieDomainRewrite 参数修改响应头 cookie 中域名 , 实现当前域下 cookie 的写入

  · 在 vue 框架下实现跨域

  利用node + webpack + webpack-dev-server 代理接口跨域。在开发环境下,由于 vue 渲染服务和接口代理服务都是 webpack-dev-server 同一个,所以页面与代理接口之间不再跨域,无须设置 headers 跨域信息了。后台可以不做任何处理。

  webpack.config.js 部分配置

  module.exports = {

  entry: {},

  module: {},

  ...

  devServer: {

  historyApiFallback: true,

  proxy: [{

  context: '/login',

  target: ' // 代理跨域目标接口

  changeOrigin: true,

  secure: false, // 当代理某些 https 服务报错时用

  cookieDomainRewrite: ' // 可以为 false ,表示不修改

  }],

  noInfo: true

  }

  }

  Ⅳ .nginx 反向代理中设置

  和使用node 中间件跨域原理相似。前端和后端都不需要写额外的代码来处理, 只需要配置一下 Ngnix

  server{

  # 监听 9099 端口

  listen 9099;

  # 域名是 localhost

  server_name localhost;

  # 凡是 localhost:9099/api 这个样子的,都转发到真正的服务端地址

  location ^~ /api {

  proxy_pass ;

  }

  }

  对于跨域还有挺多方式可以实现, 这里就不一一列举了。

  三、for...in for...of 的区别

  1. for...of ES6 新引入的特性,修复了 ES5 引入的 for...in 的不足

  2. for...in 循环出的是 key for...of 循环出的是 value

  3. for...of 不能循环普通的对象,需要通过和 Object.keys() 搭配使用

  4. 推荐在循环对象属性的时候,使用 for...in, 在遍历数组的时候的时候使用 for...of

  四、new 一个对象,这个过程中发生了什么

  var obj = new Object("name","sansan");

  1. 创建一个新对象,如: var obj = {};

  2. 新对象的 _proto_ 属性指向构造函数的原型对象。

  3. 将构造函数的作用域赋值给新对象。 ( 也所以 this 对象指向新对象 )

  4. 执行构造函数内部的代码,将属性添加给 obj 中的 this 对象。

  5. 返回新对象 obj

  五、js 的防抖和节流是什么

  · 防抖 : 在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。

  使用场景:

  1. 给按钮加函数防抖防止表单多次提交。

  2. 对于输入框连续输入进行 AJAX 验证时,用函数防抖能有效减少请求次数。

  简单的防抖(debounce) 代码 :

  function debounce(fn, wait) {

  var timeout = null;

  return function () {

  if (timeout !== null) clearTimeout(timeout)

  timeout = setTimeout(fn, wait)

  }

  }

  // 处理函数

  function handle() {

  console.log(Math.random())

  }

  // 滚动事件

  window.addEventListener('scroll', debounce(handle, 2000));

  · 节流 : 就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。

  function throttle(func, delay) {

  var prev = Date.now();

  return function () {

  var context = this;

  var args = arguments;

  var now = Date.now();

  if (now - prev >= delay) {

  func.apply(context, args);

  prev = Date.now();

  }

  }

  }

  function handle() {

  console.log(Math.random());

  }

  window.addEventListener('scroll', throttle(handle, 2000));

  区别:

  函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69913864/viewspace-2706481/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-03-20

  • 博文量
    342
  • 访问量
    150242