JS 基础
问:浏览器输入网址到页面显示经历哪些步骤?
答:
1.发送到 DNS 服务器到,并获取域名对应的 ip 地址
2.与 web 服务器建立 tcp 连接
3.浏览器向 web 服务器发送 http 请求
4.web 服务器响应请求并返回指定 url 的数据
5.浏览器下载 web 服务器返回的数据及解析 html 源文件
6.生成 DOM 树,解析 css 和 js,渲染页面,直至显示完成
问:http 请求常见的请求头有哪些?
答:
Accept :浏览器能够处理的内容类型
Accept-Charset:浏览器能够显示的字符集
Accept-Encoding:浏览器能够处理的压缩编码
Accept-Language:浏览器当前设置的语言
Connection:浏览器与服务器之间连接的类型
Cookies:当前设置的任何 cookie
Host:发出请求的页面所在的域
Referer:发出请求的页面的 url
User-Agent:浏览器的用户代理字符串
问:http 请求返回的状态码有哪些?
答:
1 开头表示临时相应,并需要请求者继续执行操作
2 开头表示请求成功
3 表示重定向
4 开头表示请求出错(403 服务器拒绝请求,用户访问被禁止)
5 开头表示服务端错误
问:知道原型链吗?
答:
原型链是用于查找引用类型(对象)的属性,查找属性会沿着原型链依次进行,如果找到该属性会停止搜索并做出相应的操作,否则会沿着原型链依次查找直到结尾。常见的应用是用在创建对象和继承中。
问:知道闭包吗?
答:
闭包就是能够读取其他函数内部变量的函数,因为在 js 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解为“定义在一个函数内部的函数”。所以本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
问:this 的指向
答:
单独的 this 指向的是 window
在严格模式下,this 是 undefined
定时器中的 this ,指向的是 window
在构造函数中 this 就指向这个对象
在元素绑定事件,事件触发后,执行的函数中的 this,指的是当前元素
问:new 一个对象的过程(原理)
答:
首先用 Object.create() 创建一个空对象 obj
去除第一个参数,也就是我们传入的构造函数,利用 shift 方法修改原数组,所以 arguments 会被去除第一个参数
将 obj 的 proto 指向构造函数的 prototype,这样 obj 就能访问到构造函数原型上的属性方法
将构造函数的 this 用 call 或 apply 方法指向 obj,让 obj 拥有函数内部属性方法
返回 obj
问:什么是协商缓存?什么是强缓存?
答:
协商缓存:在第一次请求数据时,服务器会返回所请求资源与标识,浏览器将其缓存在本地,在第二次请求同样的资源时,浏览器会将该标识携带在请求头中发给服务器,服务器验证标识后,如果所请求资源未发生变化,就返回 304 状态码,告诉浏览器命中协商缓存,直接调用缓存,如果请求资源发生变化,则将新的资源和标识发送给浏览器,重新缓存
强缓存:在第一次请求资源时,服务器返回所请求资源与有效时间,浏览器将其缓存在本地,在第二次请求该资源时,浏览器判断该资源是否在有效期内,如果在有效期内,就直接调用该资源,不向服务端发起请求
问:前端实现跨域的方法有哪些?
答:
(前端)将 JSON 升级为 JSONP,在 JSON 的基础上,利用
(后端)服务器配置 CORS (跨域资源共享)
(后端)node.js 或 nginx,反向代理,把跨域改造成同域
问:关于安全了解多少?
答:
XSS 攻击:
描述:跨站脚本攻击---简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如(Cookie、SessionId 等),进而危害数据安全
注入点:
1. HTML 的内容或属性(因为属性包含一些事件)
2. 富文本
3. JavaScript 代码
存储型(持久):常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等
1. 恶意代码提交到目标网站的数据库
2. 服务端将恶意代码从数据库取出,拼接在 html 中,浏览器执行恶意代码
3. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
反射型(非持久):常见于通过 url 传递参数的功能,如网站搜索、跳转等
1. 构造出特殊的 url ,其中包含恶意代码
2. 网站服务端将恶意代码从 url 中取出,拼接在 html 中返回给浏览器
3. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
CSRF 攻击:
描述:跨站请求伪造---简称 XSS,诱导受害者进入第三方网站,或者用户的注册凭证,在第三方网站中,向被攻击网站发送跨站请求,因为已经获取了凭证,所以达到冒充用户对被攻击的网站执行目的
流程:
1. 受害者登录 a.com,并且保留了登录凭证(Cookie)
2. 攻击者引诱受害者访问第三方网站 b.com,第三方网站获得受害者的 cookie
3. b.com 向 a.com 发送请求,浏览器会默认携带 Cookie
4. a.com 收到请求并获得 Cookie,确认是受害者的,误以为受害者自己发送请求,执行了某些操作
5. 攻击完成,在受害者不知情的情况下,让 a.com 执行了自己定义的操作
方式:
1. get 请求,例如图片地址,受害者访问了一个有 img 标签的页面,浏览器会自动向 src 地址发送请求,请求地址的服务器就会收到受害者的 Cookie 信息
2. post 请求,通常使用一个自动提交的表单;访问页面,表单自动提交
3. 链接方式,用户点击链接,向第三方发送请求,获取受害者登录凭证,通常在论坛中嵌入恶意链接,或者以夸张的广告方式诱导用户点击
用不受同源影响的 script,img 或者 iframe 之类的标签加载地址,浏览器会附带上 a 站此登录用户的授权 cookie 信息
防御 XSS/CSRF
1. 同源检测,服务器可通过 request headers 里 origin 和 referer 两个字段确定请求的来源域,攻击者设置 Referer Policy 隐藏了 referer,可以直接阻止
2. 使用 token,要求所有的用户请求都携带一个攻击者无法获取到的 token,服务器通过校验请求是否携带正确的 token 防范 csrf 的攻击
3. 使用验证码,请求时要求再次输入验证码和密码之类的,打断 csrf 的进程,简单粗暴且有效
4. 输入过滤,输入内容长度控制(不可靠,因为一旦攻击者绕过前端过滤,直接构造请求,就可以提交恶意代码了)
5. 转义 html (html 转义是非常复杂的,应当避免自己写转义库,而应当采用成熟的、业界通用的转义库)
- & 转 &
< 转 <
> 转 >
" 转 "
' 转 '
/ 转 /
6. cookie设置 HttpOnly 和 Secure;cookie中设置了 HttpOnly 属性,禁止 JavaScript 读取敏感 Cookie;cookie中设置了Secure属性,规定只能在https协议下才能够发送到服务器;
7. csp 内容安全策略
问:什么是事件循环?宏任务和微任务知道吗?
答:同步模式与异步模式:
首先要确定 JS 是单线程语言,js 在设计之初用作用户互动,以及操作 DOM。这决定了它只能是单线程(例如多线程操作同一 dom,一个是删除一个是修改,这样会产生冲突)
但倘若只有同步模式,遇到耗时操作,页面便会阻塞,就像接口请求不到数据,或者图片未加载完成,页面就卡住一直等待。这样显得不现实也不实用。所以,异步模式应运而生。
EventLoop
所谓事件环,就是三步不断循环得 js 得执行闭环
1.同步主线程
2.异步函数放入 eventTable 注册,等待完成后放入 eventQueue
3.同步主线程完成,取 eventQueue 放入主线程
宏任务与微任务:
两个任务皆为 异步任务,区别就是执行顺序
消息队列 有微先走微,微可插宏队
宏任务:script (主线程)、setTimeout、setInterval、setImmediate
微任务:Promise 得 then (promise 传入得执行函数会立即执行属于同步)、process.nextTick(node环境)、Object.observe(已废弃)、MutationObserver(观测 dom 变化)
问:数据类型分为几种?有几种判断数据类型的方法?
答:ES5 中有五种:Number、String、Boolean、undefined、Null;
ES6 中新加了一种:Symbol
判断方法:typeof
instanceof
constructor
Object.prototype.toString()
问:http 和 https 的区别?
答:https 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用。
http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl/tls 加密传输协议
http 和 https 使用的完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443
http 的连接很简单,是无状态的, https 协议是由 SSL/TLS + HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全
问:javascript 数组的常用方法有哪些?
一:操作方法
数组基本操作可以归纳为增、删、改、查,需要留意的是哪些方法会对原数组产生影响,哪些方法不会
下面对数组常用的操作方法做一个归纳
增
下面前三种是对原数组产生影响的增添方法,第四种则不会对原数组产品影响
push()
unshift()
splice()
concat()
push()
push() 方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度
let colors = []; //创建一个数组
let count = colors.push('red','green'); //推入两项
console.log(count) //2
unshift()
unshift() 在数组开头添加任意多个值,然后返回新的数组长度
let colors = new Array(); // 创建一个数组
let count = colors.unshift('red','green'); // 从数组开头推入两项
alert(count); // 2
splice
传入三个参数,分别是开始位置、0(要删除的元素数量)、插入的元素,返回空数组
let colors = ['red','green','blue'];
let removed = colors.splice(1,0,'yellow','orange')
console.log(colors) // red,yellow,orange,green,blue
console.log(removed) // []
concat()
首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组
let colors = ['red','green','blue'];
let colors2 = color.concat('yellow',['black','brown']);
console.log(colors);// ['red','green','blue']
console.log(colors2); // ['red','green','blue','yellow','black','brown']
删
下面三种都会影响原数组,最后一项不影响原数组:
pop()
shift()
splice()
slice()
pop()
pop() 方法用于删除数组的最后一项,同时减少数组的 length 值,返回被删除的项
let colors = ['red','green]
let item = colors.pop(); // 取得最后一项
console.log(item) // green
console.log(colors.length) // 1
shift()
shift() 方法用于删除数组的第一项,同时减少数组的 length 值,返回被删除的项
let colors = ['red','green']
let item = colors.shift(); // 取得第一项
console.log(item) // red
console.log(color.length) // 1
splice()
传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
let colors = ['red','green','blue']
let removed = colors.splice(0,1); // 删除第一项
console.log(colors) // green,blue
console.log(removed) // red,只有一个元素的数组
slice()
slice() 用于创建一个包含原有数组中的一个或多个元素的新数组,不会影响原始数组
let colors = ['red','green','blue','yellow','purple']
let colors2 = color.slice(1)
let colors3 = color.slice(1,4)
console.log(colors) // red,green,blue,yellow,perple
console.log(colors2); // green,blue,yellow,purple
console.log(colors3); // green,blue,yellow
改
即修改原来数组的内容,常用 splice
splice()
传入三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,对原数组产生影响
let colors = ['red','green','blue'];
let removed = colors.splice(1,1,'red','purple'); // 插入两个值,删除一个元素
console.log(colors); // red,red,purple,blue
console.log(removed); // green,只有一个元素的数组
查
即查找元素,返回元素坐标或者元素值
indexOf()
includes()
find()
indexOf()
返回要查找的元素在数组中的位置,如果没找到则返回1
let numbers = [1,2,3,4,5,4,3,2,1]
numbers.indexOf(4) // 3
includes()
返回要查找的元素在数组中的位置,找到返回 true,否则 false
let numbers = [1,2,3,4,5,4,3,2,1]
numbers.includes(4) //true
find()
返回第一个匹配的元素
const people = [{
name:'Matt',
age:27
},{
name:'Nicholas',
age:29
}]
people.find((element,index,array) => element.age < 28) // // {name:'Matt',age:27}
二、排序方法
数组有两个方法可以用来对元素重新排序:
reverse()
sort()
reverse()
顾名思义,将数组元素方向排列
let values = [1,2,3,4,5]
values.reverse();
alert(values); // 5,4,3,2,1
sort()
sort() 方法接受一个比较函数,用于判断哪个值应该排在前面
function compare(value1,value2){
if(value1 < value2){
return -1
}else if(value1 > value2){
return 1
}else {
return 0
}
}
let values = [0,1,5,10,15]
value.sort(compare);
alert(values); // 0,1,5,10,15
三、转换方法
常见的转换方法有:
join()
join() 方法接收一个参数,即字符串分隔符,返回包含所有项的字符串
let colors = ['red','green','blue']
alert(colors.join(',')); // red,green,blue
alert(colors.join('||')) // red || green || blue
四、迭代方法
常用来迭代数组的方法(都不改变原数组)有如下:
some()
every()
forEach()
filter()
map()
some()
对数组每一项都运行传入的函数,如果有一项函数返回 true ,则这个方法返回 true
let numbers = [1,2,3,4,5,4,3,2,1]
let someResult = number.every((item,index,array)=>item > 2)
console.log(someResult) // true
every()
对数组的每一项都运行传入的函数,如果对每一项函数都返回 true,则这个方法返回 true
let numbers = [1,2,3,4,5,4,3,2,1]
let everyResult = numbers.every((item,index,array) => item > 2);
console.log(everyResult) // false