前端面试题(一)

(一)腾讯区域研发前端
一面
自我介绍
介绍一下你最难的项目
1.谈谈对 vue 和 react 的理解,以及他们的区别

  • 响应式的区别
    react和vue都是做组件化的,整体的功能都类似,但是他们的设计思路是有很多不同的。使用react和vue,主要是理解他们的设计思路的不同。
    react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,所以在react中,是单向数据流。即数据变化会影响视图变化,视图变化不会影响数据变化。
    vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。即数据变化会影响视图变化,视图变化也会影响数据变化。
    总之,react的性能优化需要手动去做,而vue的性能优化是自动的,但是vue的响应式机制也有问题,就是当state特别多的时候,Watcher也会很多,会导致卡顿,所以大型应用(状态特别多的)一般用react,更加可控。
  • react通过js来操作一切,vue是把html,css,js组合到一起,用各自的处理方式,vue有单文件组件,可以把html、css、js写到一个文件中,html提供了模板引擎来处理。
  • react是类式的写法,而vue是声明式的写法,通过传入各种options,api和参数都很多。所以react结合typescript更容易一起写,vue稍微复杂。
  • 内置or社区?
    react做的事情很少,很多都交给社区去做,vue很多东西都是内置的,写起来更方便。

2.vue 常用指令

3.父子组件传值?多个组件共享状态?有什么更好的处理方法?(vuex或者其他状态管理解决方案)

//1.父组件向子组件传值,单向数据流的一般表现了: 父级 prop 的更新会向下流动到子组件中,但是反过来则不行。

//父组件将值放在属性里面
<Parent value = "I am a big girl!"/>

//子组件通过props来接收
export default{
  props:{
   value,
}
}
//子组件向父组件传值
<!-- 父组件 -->
<template>
    <div class="test">
      <test-com @childFn="parentFn"></test-com>
      <br/> 
      子组件传来的值 : {{message}}
    </div>
</template>

<script>
export default {
    // ...
    data() {
        return {
             message: ''
        }
    },
    methods: {
       parentFn(payload) {
        this.message = payload;
      }
    }
}
</script>

<!-- 子组件 -->
<template> 
<div class="testCom">
    <input type="text" v-model="message" />
    <button @click="click">Send</button>
</div>
</template>
<script>
export default {
    // ...
    data() {
        return {
          // 默认
          message: '我是来自子组件的消息'
        }
    },
    methods: {
      click() {
            this.$emit('childFn', this.message);
        }
    }    
}
</script>

vue-router 用过吗?说一下它常见的钩子函数
https://blog.csdn.net/qq_41161982/article/details/112648684 这个链接讲地超好

vue-router怎么重定向的? -> routes:[{undefined{ path: '/a', redirect: '/b' }}]
vue-router 是什么?它有哪些组件 -> vue用来写路由一个插件。有router-link、router-view
vue-router钩子函数有哪些?都有哪些参数?有哪些作用?->
全局:前置守卫:beforeEach((to, from, next)=>{to:即将进入的路由对象form:当前导航正要离开的路由next():进行管道中的下一个钩子})
解析守卫:beforeResolve((to, from, next)=>{})
后置钩子:afterEach((to,form)=>{})
路由:beforeEnter((to, from, next)=>{})
组件:beforeRouteEnter (to, from, next) {// 在渲染该组件的对应路由被 confirm 前调用// 不!能!获取组件实例 this// 因为当守卫执行前,组件实例还没被创建},
beforeRouteUpdate (to, from, next) {// 在当前路由改变,但是该组件被复用时调用// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。// 可以访问组件实例 this},
beforeRouteLeave (to, from, next) {// 导航离开该组件的对应路由时调用 //可以访问组件实例 this}。

$route 和 $router 的区别是什么?
$router是VueRouter的实例,在script标签中想要导航到不同的URL,使用$router.push方法。返回上一个历史history用$router.to(-1)

$route为当前router跳转对象。里面可以获取当前路由的name,path,query,parmas等。

vue路由的 hash 和 history 模式?
hash模式:即地址栏 URL 中的 # 符号;使用变更hash不会刷新页面的特性, 来变更路由, 做到单页面无刷新
history模式:window.history对象打印出来可以看到里边提供的方法和记录长度。利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)。可以在router文件夹下的index.js文件中配置为mode:‘history’
vue-router实现路由懒加载( 动态加载路由 )的方式有哪些?

答:有三种方式

第一种:vue异步组件技术 ==== 异步加载,vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .但是,这种情况下一个组件生成一个js文件。

第二种:路由懒加载(使用import)。

第三种:webpack提供的require.ensure(),vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。

vue 自定义指令
vue 双向绑定原理
vue 虚拟DOM 的 Diff 算法
浏览器事件循环机制
node 事件循环机制呢?
nextTick() 是什么任务?(宏任务or微任务?)
用 node.js 做过什么东西?
websocket 怎么做的聊天服务器

websocket可以在用户的浏览器和服务器之间打开交互式通信会话,使用websocket可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。
前端websocket基本接口

// 实例化直接建立连接,连接完成可以开始通讯了
let socket = new Websocket(url[, protocols])
// 发送消息
socket.send(msg)
//关闭
socket.close()
//src/socket/socket.js
封装一个websocket类,对类进行实例化就创建了一个socket连接:
class CWebSocket {
  constructor (url) {
    this.socket = new Websocket(url)
  }
}

//发消息函数
因为收发的消息是字符串格式,所以需要用JSON.stringify转为字符串。
实例化后就可能会调用发送消息,但是此时连接可能未建立完成,所以用一个待发送消息列表将未发送的消息保存下来,等连接成功后发送。
class CWebSocket {
  constructor (url) {
    this.socket = new Websocket(url)
  	this.waitingList = []
  	this.socket.open = (...args) => {
  		this.checkWaitingList()
  	}
  }
  checkWaitingList () {
  	this.waitingList.forEach(this.C2SMessage)
  },
  C2SMessage (data) {
  	if (this.socket.readyState !== this.socket.OPEN) {
  		this.waitingList.push(data)
  		return
	}
  	let msg = JSON.stringify(data)
  	this.socket.send(msg)
  }
}

//收消息函数
class CWebSocket {
	constructor (url) {
		// ...
		this.listenerList = []
		this.socket.onmessage = (...args) => {
	      this.S2CMessage(...args)
	    }
	}
	addListener (func) {
		this.listenerList.push(func)
	}
	removeListener (func) {
		let index = this,listenerList.indexOf(func)
		index != -1 && this.listenerList.splice(index, 1)
	}
	S2CMessage (event) {
		let msg = JSON.parse(event.data)
		this.listenerList.forEach(func => func(msg))
	}
}

HTTP 和 WebSocket
TCP 三次握手和四次挥手

常见的网络攻击?(说了 xss, xsrf, sql注入)

  • 1.xss,跨站脚本攻击。是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。分为两类:反射型和持久型。
    反射型XSS攻击场景:用户点击嵌入恶意脚本的链接,攻击者可以获取用户的cookie信息或密码等重要信息,进行恶性操作。
    解决:开启cookie的HttpOnly属性,禁止JavaScript脚本读取cookie信息。
  • 持久型XSS攻击场景:攻击者提交含有恶意脚本的请求(通常使用

因为从WEB页面产生的所以请求,包括文件请求,都会带上cookie,这样,只要用户在网站A的会话还没有过期,访问恶意网站B时就可能被动发出请求到网站A,同时携带cookie信息,从而达到伪造用户进行恶性操作。
再次强调一下cookie的机制:浏览器向一个域名发起http请求时(GET请求)会带上浏览器保存的关于那个域名的cookies,而不管你从哪个网站发请求。
解决方案:

  1. 同源检测
    既然CSRF大多来自第三方网站,那么我们就直接禁止外域(或者不受信任的域名)对我们发起请求。
    可以通过origin header或者referer header判断请求源(注意:某些情况下,浏览器不会发送referer参数,比如,从https跳转到http,为了安全性不会发送referer)。
    缺点:CSRF大多数情况下来自第三方域名,但并不能排除本域发起。如果攻击者有权限在本域发布评论(含链接、图片等,统称UGC),那么它可以直接在本域发起攻击,这种情况下同源策略无法达到防护的作用。

2.CSRF token验证

提交请求中携带token,并且每次请求的token值都是合法的随机数:
校验原理:
1.后端生成 token,并存在 session 中。
2.用户请求成功后,后端将 token 发送到客户端,主要方式为:
2.1 服务端将 token 渲染到 html 中(同源策略会限制脚本 API 操作)
2.2 服务端将 token 设置到 cookie 中,客户端从 cookie 中获取(同源策略限制 cookie 操作)
3.客户端在获取到 token 后,在下一次进行比较关键的请求操作时,将 token 发送到服务端,主要方式包括两为:
3.1 在请求头中将获取到的 token 设置到自定义header或者cookies中。
3.2 将 token放到请求参数中。
4.服务端接收到请求后,从请求头中取出token,并和session 中的 token 进行比较,一致则表示身份验证通过,再返回相应的信息;否则,则校验不通过。

  1. 开启cookie SameSite 强校验

Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。除了None是默认携带Cookie(Https模式下才能开始None),其他两种都可以加强Cookie校验。

Strict:跨站点时,任何情况下都不会发送 Cookie,也就是说,当前网页的 URL与请求目标URL一致,才会带上 Cookie。
Lax:访问目标网址的 Get 请求-链接/预加载请求/GET 表单才会带上Cookie。如下图:

前端面试题(一)_第1张图片

  1. 网络请求劫持

请求劫持是指网站资源在请求过程中因为人为原因而没有加载到正确的资源。

4.1 DNS劫持

在浏览器输入URL,浏览器完成连接准备之后,第一件事情就是DNS查询。如果本地没有缓存,那么需要向DNS服务商请求该域名的IP地址。这个过程中如果发生恶意劫持,都导致正确的网址不能得到正确解析,且用户可能访问了一个非法服务器获取到恶意资源。

解决方案:DNS over HTTP(用 HTTP 协议来传输 DNS )

最简单的实现是使用一个 固定的 IP 地址作为域名服务器,每次不发生 UDP ,而是向这台服务器发送 HTTP 请求来获取解析结果。
缺点:由于浏览器没有暴露 DNS 相关的接口,此方案无法在Web中得以使用。iOS 和 Android可行

4.2 HTTP劫持

在用户浏览器和目标服务器之间建立的网络数据传输通道中的任何环节,比如,网关,防火墙等,发生的恶意劫持。从而在用户浏览器上展示了广告或其他内容(常见插入iframe)。最可能的劫持方就是ISP(internet service provider互联网服务商)。

解决方案:HTTPs

二面
结合自身学习介绍项目
我有说到发布 npm 包,封装组件,总结反思优化什么的,所以大部分问题都围绕我的介绍来问
npm 包开发测试到上传的流程
用 node 做了一个小的聊天服务器
如何看待 Vue 和 React 这些框架带来的好处?
模块开发的好处?
你觉得 element-ui 的哪些组件封装的较好?
如果让你封装自己的组件应该怎么做?
面对新的框架和技术你怎么学习?
你怎么规划未来的前端学习?
三面HR面
自我介绍
为什么打算来这?
你的期望薪资?
你在学校做了哪些对自己有意义的事?
面试官让等待后续,通过的话会打电话或发邮件通知接下来的进展
总结
总结下来感觉问题都是交叉的,基础、框架、网络安全、项目都有。二面的时候让自我介绍加项
目介绍,后面问题都是围绕你的回答展开。HR面正常回答就好,表现出你想去公司工作的愿望

(二)字节跳动前端
一面(1h)
自我介绍

Flex布局,实现两个子元素垂直,并且一个靠右一个靠左

css代码
.parent {
display: flex;
align-items: center;
justify-content: space-between;
height: 400px;
border: 1px black solid;
}
.left {
width: 100px;
height: 150px;
border: 1px red solid;
}
.right {
width: 100px;
height: 150px;
border: 1px green solid;
}

JS基本数据类型
1.值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
2.引用数据类型:对象(Object)、数组(Array)、函数(Function)。

js数据类型检测方法
1.typeof

//对于原始类型来说,除了 null 都可以调用typeof显示正确的类型。 
//typeof返回的结果都是字符串
console.log(typeof ""); ‘string’
console.log(typeof 1); ‘number’
console.log(typeof true);‘boolean’
console.log(typeof undefined);undefinedtypeof Symbol() // 'symbol'
console.log(typeof null); ‘object’

// 但对于引用数据类型,除了函数之外,都会显示"object"。
console.log(typeof []); ‘object’ 
console.log(typeof {}); ‘object’
console.log(typeof function(){});function

可以看到,typeof对于基本数据类型判断是没有问题的,但是遇到引用数据类型(如:Array)是不起作用的。

2.instanceof

instanceof的原理是基于原型链的查询,只要处于原型链中,判断永远为true

console.log("1" instanceof String); false
console.log(1 instanceof Number); false
console.log(true instanceof Boolean);false
//            console.log(null instanceof Null);
//            console.log(undefined instanceof Undefined);
console.log([] instanceof Array);false
console.log(function(){} instanceof Function);false
console.log({} instanceof Object);false

3.constructor

console.log(("1").constructor === String);
console.log((1).constructor === Number);
console.log((true).constructor === Boolean);
//console.log((null).constructor === Null);
//console.log((undefined).constructor === Undefined);
console.log(([]).constructor === Array);
console.log((function() {}).constructor === Function);
console.log(({}).constructor === Object);

4.Object.prototype.toString.call()
所有的数据类型,这个办法都可以判断出来。
var a = Object.prototype.toString;
console.log(a.call(“aaa”));
console.log(a.call(1));
console.log(a.call(true));
console.log(a.call(null));
console.log(a.call(undefined));
console.log(a.call([]));
console.log(a.call(function() {}));
console.log(a.call({}));

var num = 123
num.toString() // '123'

var str = 'hello'
str.toString() // 'hello'

var bool = false
bool.toString() // 'false'

var arr = [1, 2, 3]
arr.toString()  // '1,2,3'

var obj = {lang:'zh'}
obj.toString()  // '[object Object]'

var fn = function(){}
fn.toString()  // 'function(){}'

null.toString()  // Cannot read property 'toString' of null

undefined.toString() // Cannot read property 'toString' of undefined
s

其实toString是对象上的方法,每一个对象上都有这个方法,那就意味着数字、字符串和布尔值这些基本数据类型不能使用toString()方法,但上例中的基本数据类型却是可以使用,这要归功于javascript中的包装类,即Number、String和Boolean。原始值不能有属性和方法,当要使用toString()方法时,会先将原始值包装成对象再使用。
其实各数据类型使用toString()后的结果表现不一的原因在于:所有类在继承Object的时候,改写了toString()方法。原始Object上的toString()方法是可以输出数据类型的,如上例中的’[object Object]'这个结果,所以当我们想要判断数据类型时,必须使用Object上的toString()方法。

原型链
闭包

JS预编译

所谓预编译,就是为代码接下来的执行创建一个执行上下文,这样代码才能顺利执行下去。这个执行上下文又称为作用域,负责变量(标识符)的读写。
通常将JavaScript归类为“动态”或“解释执行”语言,但事实上它是一门编译语言。与传统的编译语言不同,JavaScript的编译过程不是发生在构建之前,它的大部分编译发生在代码执行前的几微秒,一般把这个过程称为预编译。
函数执行前的预编译过程

一般分为以下四步:
1.创建活动对象(Active object,下面简称AO)
2.查找函数形参和函数内变量声明,作为AO的属性名,不赋值
3.形参与实参值统一
4.查找函数声明,函数名作为AO的属性名,值为函数引用

function foo(a){
  var b = 2;
  function bar(){}
  console.log(a + b);
}
1.当代码执行到 foo(1) 的时候,会为函数foo创建一个AO 
AO{}

2.将形参和变量声明放入AO
AO{
 a: undefined,
 b: undefined
}

3. 形参实参值统一
AO{
 a: 1,
 b: undefined
}

4. 将函数声明放到AO并赋值
AO{
 a: 1,
 b: undefined,
 bar: function bar(){}
}

全局代码块执行前的预编译
一般分为以下三步:
1.创建全局活动对象(Global object,以下简称GO)
2.查找变量声明,作为GO的属性名,不赋值
3.查找函数声明,函数名作为GO的属性名,值为函数引用

	var a = 1;
	function foo(b){
	  console.log(b)
	}
	foo(2)
	
1.首先创建一个全局活动对象GO
GO{}

2. 将变量声明放入GO
GO {
 a: undefined
}

3.将函数声明放入GO并赋值
GO{
 a: undefined,
 foo: function foo(){...}
}

预编译只是生成了作用域,这里我们聊一下作用域是如何被使用的。

作用域就是编程语言用来存取变量(标识符)的规则。

  var global = 1;
  function foo(a){
   var b = 2;
   console.log(a + b);
  }
  foo(1);
  1.首先在全部代码执行之前会生成全局作用域:
  GO{
	 global: undefined,
	 foo: function foo(){...}
	}
  2.执行到第一行
   GO{
	  global: 1,
	  foo: function foo(){...}
	 }
  3.跳到第5行,这一行是一个对foo函数调用,引擎会在GO中查找到foo,然后对其进行函数执行。在执行前,会进行预编译生成foo函数的作用域AOAO{
	 a: 1,
	 b: undefined
	}
  4.然后执行函数内的第一行(第2行),从AO中找到b并为其赋值:
  AO{
	 a: 1,
	 b: 2
	}

在代码执行的过程中,通常需要同时顾及几个作用域。当一个块或者函数嵌套在另一个块或者函数中时,就发生了作用域嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套作用域中继续查找,直到找到该变量,或抵达最外层作用域(也就是全局作用域)为止。 -这样逐级嵌套的作用域就是作用域链。

接下来,我们来看一个示例:

var a = 1;
function foo(){
  var b = 2;
  function bar(){
   var c = 3;
   console.log(c + b + a);
  }
  bar();
}
foo();

逐行分析代码
作用域一般有两种工作模式。一种是应用最为普遍的词法作用域,另外一种叫做动态作用域。
而JavaScript中的作用域就是词法作用域。顾名思义,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪来决定的。

var a = 1;
function bar(){
 console.log(a);
}
function foo(){
 var a = 2;
 bar();
}
foo();

无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定,这就是JavaScript的词法作用域。

你说到了作用域链,那么你来谈一下作用域?
说到了执行上下文,所以又问了执行上下文
变量提升、词法作用域、动态作用域

词法作用域

看一点相关的题目吧
console.log(foo);
function foo(){
console.log(“foo”);
}
var foo = 1;

输出
//ƒ foo(){
// console.log(“foo”);
//}

var value = ‘tom’;
function foo() {
console.log(value)
}
function bar() {
var value = ‘bob’;
foo();
}
bar();

输出
//tom

function foo() {
return function() {
console.log(this)
}
}
foo()();
输出
//window

var foo = {
bar: function () {
return this;
}
}
foo.bar();
输出
//{bar: f}

http的请求方法
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。

1 GET 请求指定的页面信息,并返回实体主体。
2 HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
3 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
4 PUT 从客户端向服务器传送的数据取代指定的文档的内容。
5 DELETE 请求服务器删除指定的页面。
6 CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
7 OPTIONS 允许客户端查看服务器的性能。
8 TRACE 回显服务器收到的请求,主要用于测试或诊断。
9 PATCH 是对 PUT 方法的补充,用来对已知资源进行局部更新 。

注意:
1)方法名称是区分大小写的,当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Mothod Not Allowed);当服务器不认识或者不支持对应的请求方法时,应返回状态码501(Not Implemented)。
2)HTTP服务器至少应该实现GET和HEAD/POST方法,其他方法都是可选的,此外除上述方法,特定的HTTP服务器支持扩展自定义的方法。

HTTP 请求/响应的步骤:

客户端连接到Web服务器->发送Http请求->服务器接受请求并返回HTTP响应->释放连接TCP连接->客户端浏览器解析HTML内容

1、客户端连接到Web服务器
一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.baidu.com

2、发送HTTP请求
通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。

3、服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

4、释放连接TCP连接
若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

5、客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

HTTP请求消息Request

客户端发送一个HTTP请求到服务器的请求消息包括以下内容:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成 请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本
前端面试题(一)_第2张图片
Get请求例子,使用Charles抓取的request:

GET /562f25980001b1b106000338.jpg HTTP/1.1
Host    img.mukewang.com
User-Agent    Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
Accept    image/webp,image/*,*/*;q=0.8
Referer    http://www.imooc.com/
Accept-Encoding    gzip, deflate, sdch
Accept-Language    zh-CN,zh;q=0.8
  • 第一部分:请求行,用来说明请求类型,要访问的资源以及所使用的HTTP版本.
    GET说明请求类型为GET,[/562f25980001b1b106000338.jpg]为要访问的资源,该行的最后一部分说明使用的是HTTP1.1版本。

  • 第二部分:请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息
    从第二行起为请求头部,HOST将指出请求的目的地.User-Agent,服务器端和客户端脚本都能访问它,它是浏览器类型检测逻辑的重要基础.该信息由你的浏览器来定义,并且在每个请求中自动发送等等

  • 第三部分:空行,请求头部后面的空行是必须的
    即使第四部分的请求数据为空,也必须有空行。

  • 第四部分:请求数据也叫主体,可以添加任意的其他数据。
    这个例子的请求数据为空。

POST请求例子,使用Charles抓取的request:

POST / HTTP1.1
Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive

name=Professional%20Ajax&publisher=Wiley

第一部分:请求行,第一行明了是post请求,以及http1.1版本。
第二部分:请求头部,第二行至第六行。
第三部分:空行,第七行的空行。
第四部分:请求数据,第八行。

HTTP请求消息Response

一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。
前端面试题(一)_第3张图片

HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8

<html>
      <head></head>
      <body>
            <!--body goes here-->
      </body>
</html>

第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。
第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok)

第二部分:消息报头,用来说明客户端要使用的一些附加信息
第二行和第三行为消息报头,
Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8

第三部分:空行,消息报头后面的空行是必须的
第四部分:响应正文,服务器返回给客户端的文本信息。

空行后面的html部分为响应正文。

http常见状态码(必问)

GET与POST区别:

“get”方法提交的数据会直接填充在请求报文的URL上

“post”方法提交的数据会附在正文上,一般请求正文的长度是没有限制的,但表单中所能处理的长度一般为100k(不同协议不同浏览器不一样),而且需要考虑下层报文的传输效率,不推荐过长。
所以GET方法可以用来传输一些可以公开的参数信息,解析也比较方便,如百度的搜索的关键词,而POST方法可以用来提交一个用户的敏感信息(如果不使用HTTPS加密,报文正文仍旧是明文,容易被人截获读取
前端面试题(一)_第4张图片
为什么 TCP 建立连接需要三次握手,而不是两次?这是因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。

TCP 和 UDP区别

浏览器同源政策及其规避方法
原文:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

所谓"同源"指的是"三个相同"。协议相同,域名相同,端口相同。

Cookie
Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

举例来说,A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就可以共享Cookie。

document.domain = 'example.com';

注意,这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法,规避同源政策,而要使用下文介绍的PostMessage API。
另外,服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com。

Set-Cookie: key=value; domain=.example.com; path=/

这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie

iframe

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

比如,父窗口运行下面的命令,如果iframe窗口不是同源,就会报错。

// 下面命令中,父窗口想获取子窗口的DOM,因为跨源导致报错。
document.getElementById("myIFrame").contentWindow.document
// Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.

//反之亦然,子窗口获取主窗口的DOM也会报错。
window.parent.document.body
// 报错

对于完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题。

  1. 片段识别符(fragment identifier)
    2.window.name
    3.跨文档通信API(Cross-document messaging)

1 片段识别符

片段标识符(fragment identifier)指的是,URL的#号后面的部分,比如http://example.com/x.html#fragment的#fragment。如果只是改变片段标识符,页面不会重新刷新。

父窗口可以把信息,写入子窗口的片段标识符。

var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

子窗口通过监听hashchange事件得到通知。

   window.onhashchange = checkMessage;
    function checkMessage() {
      var message = window.location.hash;
      // ...
    }

同样的,子窗口也可以改变父窗口的片段标识符。

parent.location.href= target + "#" + hash;

2 window.name
浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。

1.父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入window.name属性。
window.name = data;
2.接着,子窗口跳回一个与主窗口同域的网址。
location = 'http://parent.url.com/xxx.html';
3.然后,主窗口就可以读取子窗口的window.name了。
var data = document.getElementById('myFrame').contentWindow.name;

这种方法的优点是,window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性的变化,影响网页性能。

3 window.postMessage

上面两种方法都属于破解,HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。

这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。
举例来说,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。

var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

子窗口向父窗口发送消息的写法类似。

window.opener.postMessage('Nice to see you', 'http://aaa.com');

父窗口和子窗口都可以通过message事件,监听对方的消息。

window.addEventListener('message', function(e) {
  console.log(e.data);
},false);

message事件的事件对象event,提供以下三个属性。

event.source:发送消息的窗口
event.origin: 消息发向的网址
event.data: 消息内容

下面的例子是,子窗口通过event.source属性引用父窗口,然后发送消息。

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
  event.source.postMessage('Nice to see you!', '*');
}

event.origin属性可以过滤不是发给本窗口的消息。

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
  if (event.origin !== 'http://aaa.com') return;
  if (event.data === 'Hello World') {
      event.source.postMessage('Hello', event.origin);
  } else {
    console.log(event.data);
  }
}

4、LocalStorage

通过window.postMessage,读写其他窗口的 LocalStorage 也成为了可能。下面是一个例子,主窗口写入iframe子窗口的localStorage。

window.onmessage = function(e) {
  if (e.origin !== 'http://bbb.com') {
    return;
  }
  var payload = JSON.parse(e.data);
  localStorage.setItem(payload.key, JSON.stringify(payload.data));
};

上面代码中,子窗口将父窗口发来的消息,写入自己的LocalStorage。

父窗口发送消息的代码如下。

var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
win.postMessage(JSON.stringify({key: 'storage', data: obj}), 'http://bbb.com');

加强版的子窗口接收消息的代码如下。

window.onmessage = function(e) {
  if (e.origin !== 'http://bbb.com') return;
  var payload = JSON.parse(e.data);
  switch (payload.method) {
    case 'set':
      localStorage.setItem(payload.key, JSON.stringify(payload.data));
      break;
    case 'get':
      var parent = window.parent;
      var data = localStorage.getItem(payload.key);
      parent.postMessage(data, 'http://aaa.com');
      break;
    case 'remove':
      localStorage.removeItem(payload.key);
      break;
  }
};

加强版的父窗口发送消息代码如下。

var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
// 存入对象
win.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://bbb.com');
// 读取对象
win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
window.onmessage = function(e) {
  if (e.origin != 'http://aaa.com') return;
  // "Jack"
  console.log(JSON.parse(e.data).name);
};

四、AJAX

同源政策规定,AJAX请求只能发给同源的网址,否则就报错。
除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。

  JSONP
  WebSocket
  CORS

1、JSONP

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。

它的基本思想是,网页通过添加一个script元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入script元素,由它向跨源网址发出请求。

function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data) {
  console.log('Your public IP address is: ' + data.ip);
};

上面代码通过动态添加 script元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。

服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

foo({
  "ip": "8.8.8.8"
});

由于< script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

4.2 WebSocket

webSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

下面是一个例子,浏览器发出的WebSocket请求的头信息

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。

正是因为有了Origin这个字段,所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

4.3 CORS

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。

为什么浏览器的请求有两次,一次options,第二次才是真正请求?
在CORS-跨域资源共享中,可以使用 OPTIONS 方法发起一个预检请求,以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method 首部字段告知服务器实际请求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。浏览器先询问服务器,当前网页的域名是否在服务器的许可名单之中,及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

模拟一个队列
数组扁平化?至少两种方法实现
反问
二面(1h)
问了项目中的D3.js
前端展示统计图或者一些复杂的图片是用图片好还是svg生成,这两种方式的优缺点,以及如何做取舍
最近出了 Vue 3.0,有了解过它与 Vue 2.0 的区别吗?
Vue 3.0 的 Composition API 有了解吗?

Css画圆有哪些方式?
border-radius : 50%

如何知道某个dom元素是否在当前可视窗口呢?
我们需要知道三个坐标就可知道当前dom是否在可见区域内,分别是

  1. 显示窗口的顶部坐标
  2. 显示窗口的底部坐标
  3. dom元素的中心坐标

判断规则就是,当dom元素的中心坐标的X及Y坐标均小于显示窗口的顶部,且大于显示窗口的底部坐标时,那么就可以判断该坐标在可见区域。

scrollTop如何获取?
Css的position的常见属性,sticky用过吗?
Promise说一下
Promise.all用过吗,自己用promise封装一个Promise.all?要求每一个promise能并行执行,并且要保证最后的回调参数顺序与执行顺序一致(解释:每个 promise 封装的请求不一定会按照调用顺序得到响应,可能后面调用的比前面的要快,但一样要保证最后的顺序是按照调用顺序的)
写一个题吧

const obj = {a: {b : {c : {d: 3}}}}

function getValue(obj, str, defaultValue) {}

getValue(obj, ‘a.b.c.d’, 1) => 3 (存在该属性返回该属性对应的值)

getValue(obj, ‘a.b.c.d.e’, 1) => 1 (出现错误返回传入的默认值)

改动,能处理数组

const obj_1 = {a: {b : {c : {d: [{e : 4}]}}}}

getValue(obj_1, ‘a.b.c.d[0].e’, 1) => 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
改动前的代码实现(不一定对,望大家给个建议)
function getRightValue(obj, propertyString, defaultValue) {
let str = propertyString.split(’.’);
for(let i = 0; i < str.length; i++) {
if(obj.hasOwnProperty(str[i])) {
obj = obj[str[i]];
} else {
return defaultValue;
}
}
return obj;
}

const obj = { a: {b: {c: { d: 3 }}}}

console.log(getRightValue(obj, ‘a.b.c.d’, 1))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
改动后加上了数组,所以细节要处理好,我当时卡壳了没弄出来

反问

三面(35min)
前两次的面试感觉怎么样?
通过之前的面试觉得自己还有哪些问题?
说一下你认为做得好的一个项目,遇到了些什么问题?
说了一个项目,然后接下来大部分时间都是围绕这个项目交流
问了自己更偏向于实习还是正式,然后自己回答:优先考虑正式 offer,如果达不到正式批要求,给一个实习 offer 也可以??哈哈哈我太天真了,,,
反问了具体的业务,还有自己有待提高的地方
总结
因为自己没有实习经历,项目都是自己写的,一下子想不出什么难点,只是简单聊了一些功能实现。所以建议大家以后聊项目时,有好的实习经历,就说一下实习的项目,没有实习只有自己项目的,也要往自己项目里面添加一些亮点(难点)。面试之前也要梳理好自己的项目,不然面试一下想不起来细节就尴尬了,别像我一样,,,呜,,,

(三)腾讯日常实习
一面(43min)
介绍一下自己的项目
http 状态码
TCP 和 UDP
get 和 post 区别?
浏览器如何渲染
进程和线程的区别
场景题,5种颜色的球,每种颜色球有无数个,每人每次拿两个球,问至少多少人拿,才能保证至少有两个人拿的球一模一样。
编程题:找出一组数中两两相加为零的所有组合
说一说快排
后端能写些什么
二面(15min)
项目中的难点
聊了一个项目中的部分功能实现
有合作开发的团队项目吗
如何调试数据的
高数学过吗???
线性代数学过吗?说一说用矩阵的知识解方程
概率论在it中的应用
什么时候能来实习?实习多久?
总结
这个日常实习很迷,而且一开始面试就明确说了实习后面不会留。考察比较偏算法和逻辑思维

(四)小米前端
一面(30min)
自我介绍
统计字符串字符出现个数
原型链说一下
原型继承写一下
vue怎么通信的
用类写一个发布订阅者模式
Css 垂直居中
vue组件的 data 为什么是一个函数
http 与 https 区别
http 304 ?
说一说你的优势
说一说自己未来三年规划
反问
二面(20min)
学习方式?
项目中的难点
统计页面标签个数?
聊了一些经历,对事情的看法
给面试官看了自己的学习笔记
自己的优缺点
反问,
因为没有实习经历,问了公司项目的大体开发流程。面试官小哥哥很有耐心的概括了一遍。非常感谢!
问了自己不足的地方
总结
小米前端的面试整体上来说是比较偏基础的,面试官人也很好。算法题需要掌握一些基础的

后续
希望这些大厂面经能对你有所帮助,虽说是2020年的秋招,但是万变不离其宗,我想2021年秋招考察的依然是这些 --> 扎实基础 + 基本必须掌握的算法 + 知识的广度与深度。

学弟学妹以及万千互联网求职者们,加油!
————————————————
版权声明:本文为CSDN博主「Greatiga」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44210637/article/details/118885120

你可能感兴趣的:(前端,javascript,react.js)