文章目录
- 1. 数据类型有哪些?如何判断数据类型?
- 2. 字符串、数组 有哪些方法?手写去重、排序
- 3. 元素dom操作
- 4. 事件捕获、事件冒泡、阻止事件冒泡、阻止默认事件
- 5. Js事件循环机制
- 6. call、apply、bind区别
- 7. 面向对象、原型、原型链、继承
- 8. 什么是闭包,使用场景及优缺点?
- 9. 什么是深拷贝、浅拷贝?
- 10. this指向
- 11. 什么是防抖、节流?
- 12. es6新增哪些特性?箭头函数和普通函数区别?什么是promise?
- 13. cookie、sessionStorage、localstorage
- 14. 什么是ajax?
- 15. http和https区别?http请求方式、请求报文、响应报文、get、post区别、状态码
- 16. http缓存
- 17. new 的作用
- 18. token和cookie的区别
- 19. 提升加载速度
- 20. 虚拟dom和真实dom的区别
- 21. 回流和重绘
- 22. 跨域问题
- 23. 三次握手和四次挥手
1. 数据类型有哪些?如何判断数据类型?
数据类型
-
- 基本数据类型:string,number,Boolean,null,undefined;
-
- 引用数据类型object(Object,Array),function,regex 正则 ,Date
-
- ES6新增 symbol
如何判断数据类型
typeof
优点:能够快速区分基本数据类型 + function
缺点:不能将Object、Array和Null区分,都返回objectconsole.log(typeof 2); // number console.log(typeof true); // boolean console.log(typeof ‘str’); // string console.log(typeof undefined); // undefined console.log(typeof []); // object console.log(typeof{}); // object console.log(typeof function(){}); //function console.log(typeof null); // objectinstanceof
优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象
缺点:Number,Boolean,String基本数据类型不能判断console.log(2 instanceof Number); // false console.log(true instanceof Boolean); // false console.log('str' instanceof String); // false console.log([] instanceof Array); // true console.log(function(){} instanceof Function); // true console.log({} instanceof Object); // trueObject.prototype.toString.call()
优点:精准判断数据类型
缺点:写法繁琐不容易记,推荐进行封装后使用console.log(toString.call(2)); //[object Number] console.log(toString.call(true)); //[object Boolean] console.log(toString.call(‘str’)); //[object String] console.log(toString.call([])); //[object Array] console.log(toString.call(function(){})); //[object Function] console.log(toString.call({})); //[object Object] console.log(toString.call(undefined)); //[object Undefined] console.log(toString.call(null)); //[object Null]
2. 字符串、数组 有哪些方法?手写去重、排序
判断数组的方法
- 1、从原型入手:Array.prototype.isPrototypeOf(obj);
- 2、从构造函数入手:obj instanceof Array
- 3、跨原型链调用toString():Object.prototype.toString.call(obj)
- 4、ES5新增的方法:Array.isArray()
数组方法:JavaScript数组的常用方法
- 1.shift 删除数组中的第一个元素
- 2.pop 删除数组中的最后一个元素
- 3.unshift 增加元素在数组的前面
- 4.push 增加元素在数组的后面
- 5.map 循环,并且返回新的数组
- 6.forEach 循环,遍历
- 7.filter 过滤,筛选出数组中的满足条件的,并且返回新的数组
- 8.concat 合并数组
- 9.find 查找出第一个符合条件中的数组元素
- 10.findIndex 查找出第一个符合条件中的数组元素,所在的索引位置
- 11.flat 将多维数组转为一维数组
- 12.join将数组转为字符串
- 13.reverse 颠倒数组中的顺序
- 14.every检测数组中元素是否都是符合条件 === Boolean
- 15.some检测数组中元素是否有满足条件的元素 === Boolean
- 16.splice(start,n,添加元素) 开始位置 删除个数,添加元素
- 17.sort 排序
- 18.slice(start,end) 选中[start.end)之间的元素
- 19.indexOf 查找值所在的位置
- 20.includes 查看数组中是否存在此元素
- 21.copyWithin( ) 指定位置的成员复制到其他位置
- 22.fill( )填充数组
- 23.Array.from( )方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map
- 24.Array.of( ) 用于将一组值,转换为数组
- 25.flatMap( )只能展开一层数组-----flat 降维 与 map 有返回值的遍历 的结合
字符串方法:
- 1、charAt() 返回在指定位置的字符
- 2、charCodeAt() 返回指定的位置的字符的Unicode编码
- 3、concat() 连接两个或多个字符串,返回新的字符串
- 4、fromCharCode() 将Unicode编码转为字符
- 5、indexOf() 返回某个指定的字符串值在字符串中首次出现的位置
- 6、includes() 查找字符串中是否包含指定的字符串
- 7、lastIndexOf() 从后向前搜索字符串并从起始位置(0)开始计算返回字符串最后出现的位置
- 8、slice() 提取字符串的片段,并在新的字符串中返回被提取的部分
- 9、split() 把字符串分割为字符串数组
- 10、startsWith() 查看字符串是否以指定的字符串开头
- 11、substr() 从起始索引提取字符串中指定数目的字符
- 12、substring() 提取字符串中两个指定索引之间的字符
- 13、toLowerCase() 把字符串转为小写
- 14、toUpperCase() 把字符串转为大写
- 15、trim() 去掉字符串两边的空白
- 16、search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。
- 17、replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
数组去重
var arr=[1,1,1,3,3,45,65,87,152,654,895,787,62,87,654,152]
-
第一种:通过查找关键字,从newArr查找arr里面的关键字,当没有的时候返回的值是-1,代表不重复,再把返回-1的arr[i]添加到newArr
var newArr=[] for(var i=0;i<arr.length;i++){ if(newArr.indexOf(arr[i])===-1){ newArr.push(arr[i]) } } console.log(newArr) -
第二种:先sort排序,这是相同的都在一起,在用前一个比较后一个,不相等的时候,返回a.push。相等则不做任何操作,最后返回的数值都是不相同的
arr.sort() var a=[arr[0]] for(var i=1;i<arr.length;i++){ if(arr[i]!==arr[i-1]){ a.push(arr[i]) } } console.log(a) -
第三种:搜索关键字默认得到的是角标,当有相同字时,默认得到第一个,得到的角标跟i相等,则代表没有重复的,不相等,代表重复,自动pass掉
var b=[] for(var i=0;i<arr.length;i++){ if(arr.indexOf(arr[i])===i){ b.push(arr[i]) } } console.log(b) -
第四种:双for循环,用arr[0]第零个,跟后面每一项比较,相等,则删除,不相等,则继续循环。
for(var i=0;i<arr.length;i++){ for(var j=i+1;j<arr.length;j++){ if(arr[i]===arr[j]){ arr.splice(j,1) j-- } } } console.log(arr) -
第五种:Array.filter + Array.indexOf
filter() 方法:创建一个新数组,新数组中的元素是指定数组中符合某种条件的所有元素。如果没有符合条件的元素则返回空数组。
语法:array.filter(function(item,index,arr))
filter() 不会对空数组进行检测。
filter() 不会改变原始数组。
原理:返回 item 第一次出现的位置等于当前的index的元素let newArr = arr.filter((item, index) => arr.indexOf(item) === index); // [1, 2, 4, null, "3", "abc", 3, 5] -
第六种:Array.filter + Object.hasOwnProperty
hasOwnProperty() 方法:返回一个布尔值,表示对象自身属性中是否具有指定的属性
原理:利用对象的键名不可重复的特点。let obj = {} arr.filter(item => obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item) -
第七种:Array.reduce + Array.includes
reduce() 方法:接收一个函数作为累加器,数组中的每个值从左到右开始计算,最终计算为一个值。
语法:arr.reduce(function(total, currValue, currIndex, arr), initValue)let newArr = arr.reduce((accu, cur) => { return accu.includes(cur) ? accu : accu.concat(cur); // 1. 拼接方法 // return accu.includes(cur) ? accu : [...accu, cur]; // 2. 扩展运算 }, []);// [1, 2, 4, null, "3", "abc", 3, 5] -
第八种:Array.indexOf
indexOf() 方法:返回数组中某个指定的元素位置。该方法遍历数组,查找有无对应元素并返回元素第一次出现的索引,未找到指定元素则返回 -1
let newArr = [] for (var i = 0; i < arr.length; i++) { if (newArr.indexOf(arr[i]) === -1) newArr.push(arr[i]) } //等同于 forEach 写法 arr.forEach( item => newArr.indexOf(item) === -1 ? newArr.push(item) : '') console.log(newArr) // [1, 2, 4, null, "3", "abc", 3, 5] -
第九种:new Set + 扩展运算符 || Array.from
Set本身是一个构造函数,可以接受一个具有 iterable 接口数据结构作为参数(如数组,字符串),用来初始化
let newArr = [...new Set(arr)]; // [1, 2, 4, null, "3", "abc", 3, 5] let newArr = Array.from(new Set(arr)); // [1, 2, 4, null, "3", "abc", 3, 5] let newStr = [...new Set('ababbc')].join('') // 'abc' -
第十种:new Map
ES6 提供了新的数据结构 Map 。类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
get方法读取key对应的键值,如果找不到key,返回undefined。
has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。let map = new Map(); let newStr = []; for (let i = 0; i < arr.length; i++) { if (!map.has(arr[i])) { map.set(arr[i], true); newStr.push(arr[i]); } } console.log(newArr) // [1, 2, 4, null, "3", "abc", 3, 5]
排序
- 1.冒泡
var arr=[25,65,45,85,995,46,2,36,135,66] for(var j=0;j<arr.length-1;j++){ for(var i=0;i<arr.length-1-j;i++){ if(arr[i]>arr[i+1]){ var b=arr[i] arr[i]=arr[i+1] arr[i+1]=b } } } console.log(arr) - 2.sort
var arr=[24,23,1,65,75,48,98,126,548,699,58,71] arr.sort(function(a,b){return a-b}) console.log(arr) - 3、 快排
function kfn(arr) { if (arr.length <= 1) { return arr } var pt = Math.floor(arr.length / 2); var pi = arr.splice(pt, 1)[0]; //取中间项 var left = [],right = []; for (var i = 0; i < arr.length; i++) { if (arr[i] < pi) { left.push(arr[i]) //小的放左边 } else { right.push(arr[i]) //大的放右边 } } return kfn(left).concat([pi], kfn(right)) //拼接递归 }
3. 元素dom操作
-
①. DOM的概念和作用
DOM 是 JavaScript操作网页的api接口,全称为“文档对象模型,浏览器会根据 DOM 模型,将结构化文 档(比如
HTML和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。DOM 不是 JavaScript 语法的一部分,但是 DOM 操
作是JavaScript 最常见的任务. -
②.节点树
一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是DOM树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,倒过来就像一棵树。浏览器原生提供document节点,代表的是整个文档。
-
③.DOM选择器:(查询、创建、添加,修改,删除)
①返回匹配指定id属性的元素节点。如果没有发现匹配的节点,则返回null document.getElementById:
②返回一个类似数组的对象(HTMLCollection实例),包括了所有class名字符合指定条件的元素(兼容问题,低版本ie),如果没有发现匹配的节点,则返回空数组[ ]:.getElementsByClassName:
③搜索 HTML
标签名,返回符合条件的元素。它的返回值是一个类似数组的对象(HTMLCollection实例):document.getElementsByTagName:
④接受一个 CSS
选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null;
document.querySelector:(ES5新增选择器)
⑤与querySelector用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点;
document.querySelectorAll:(ES5新增选择器) -
④.DOM的基本操作
①.添加 document.createElement 用来生成元素节点,并返回该节点;
createElement方法的参数为元素的标签名,即元素节点的tagName属性,对于 HTML
网页大小写不敏感,即参数为div或DIV返回的是同一种节点;
②.插入 把newDiv添加到oDiv内部的最后面oDiv.appendChild(newDiv); 例: // 创建 var span =document.createElement(“span”); console.log(span); // 插入到将来的父元素 varoBox = document.getElementById(“box”); oBox.appendChild(span);sapn.innerHTML = “这是一个span”;
Element.innerHTML属性返回一个字符串,等同于该元素包含的所有 HTML代码。该属性可读写,常用来设置某个节点的内容。它能改写所有元素节点的内容,包括和元素。如果将innerHTML属性设为空,等于删除所有它包含的所有节点。
③.替换 box.replaceChild(newNode,oldNode);
④.删除 var el =document.getElementById(‘mydiv’); el.remove(); box.removeChild(oldNode);
4. 事件捕获、事件冒泡、阻止事件冒泡、阻止默认事件
事件流分为两种
- 捕获事件流
捕获事件流从根节点开始执⾏。⼀直往⼦节点查找执⾏,直到查找执⾏到⽬标节点
当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。 - 冒泡事件流
冒泡事件流从⽬标节点开始执⾏,⼀直往⽗节点冒泡查找执⾏,直到查到到根节点
从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。
事件流分三个阶段,
- ⼀个是捕获节点,
- ⼀个是处于⽬标节点阶段,
- ⼀个是冒泡阶段
阻止冒泡
- 法1:e.stopPropagation (); 阻止事件冒泡;用于JS
- 法2:e.preventDefault ();阻止默认事件;
- 法3: return false; 阻止冒泡,用于JQ;
function(event){
event.stopPropagation();//阻止冒泡事件
}
function(event){
event.preventDefault();//阻止默认行为
//return false;//也可以
}
阻止默认事件
- 1.event.stopPropagation()方法 这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开,
- 2.event.preventDefault()方法 这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡 会传递到上一层的父元素;
扩展:
什么是事件
JavaScript和HTML之间的交互是通过事件实现的
事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。可以使用监听器(或事件处理程序)来预定事件,以便事件发生时执行相应的代码。通俗的说,这种模型其实就是一个观察者模式。(事件是对象主题,而这一个个的监听器就是一个个观察者)
什么是事件流
事件流描述的就是从页面中接收事件的顺序。而IE和Netscape提出了完全相反的事件流概念。IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。
事件捕获:
当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的
事件目标:
当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件冒泡:
目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发
Vue 阻止默认事件`
@click.stop 代表阻止冒泡事件
@click.prevent 代表阻止默认事件
5. Js事件循环机制
JS 的 事件执行机制 先同步 —> 所有异步 微任务 —> 异步宏任务
- 异步 微任务:promise 回调(then , .catch)
- 异步 宏任务: setTimeOut SetInterval 注意: 有哪些是 一创建就立即执行的
1、为什么js是单线程
- js作为主要运行在浏览器的脚本语言,js主要用途之一是操作DOM。
- 在js高程中举过一个栗子,如果js同时有两个线程,同时对同一个dom进行操作,这时浏览器应该听哪个线程的,如何判断优先级?
- 为了避免这种问题,js必须是一门单线程语言,并且在未来这个特点也不会改变。
2、执行栈与任务列表
因为js是单线程语言,当遇到异步任务(如ajax操作等)时,不可能一直等待异步完成,再继续往下执行,在这期间浏览器是空闲状态,显而易见这会导致巨大的资源浪费。
- 执行栈
当执行某个函数、用户点击一次鼠标,Ajax完成,一个图片加载完成等事件发生时,只要指定过回调函数,这些事件发生时就会进入执行栈队列中,等待主线程读取,遵循先进先出原则。 - 主线程
要明确的一点是,主线程跟执行栈是不同概念,主线程规定现在执行执行栈中的哪个事件。
主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码。
当遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中,我们称之为任务队列(Task Queue)。
当主线程将执行栈中所有的代码执行完之后,主线程将会去查看任务队列是否有任务。如果有,那么主线程会依次执行那些任务队列中的回调函数。 - js 异步执行的运行机制。
所有任务都在主线程上执行,形成一个执行栈。
主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。
主线程不断重复上面的第三步。
宏任务与微任务:
异步任务分为 宏任务(macrotask) 与 微任务(microtask),不同的API注册的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。
- 宏任务(macrotask)
script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、
MessageChannel、setImmediate(Node.js 环境) - 微任务(microtask)
Promise、 MutaionObserver、process.nextTick(Node.js环境)
Event Loop(事件循环):
Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:
- 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
- 检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
- 更新render(每一次事件循环,浏览器都可能会去更新渲染)
重复以上步骤console.log(1); setTimeout(() => { console.log("2"); }, 0); console.log(3); let p = new Promise((resolve, reject) => { console.log(4); // resolve("此处为成功的 信息"); reject("此处为失败!"); }); p.then( (res) => { console.log(5, res); //此处为接收成功的信息 }, (res) => { console.log('6 :>> ', 6, res); //此处为接收失败的信息 } ); console.log(7); // 成功5/失败6 // 1,3,4,7 , 5/6, 2
6. call、apply、bind区别
1 、调用方式区别
- call() 、apply() 自动调用
- bind()() 需要再调用一次 bind()() ; bind是返回对应函数,需要再次调用,apply、call是立即调用
2、参数区别
- call(obj,参数1,参数2) bind(obj,参数1,参数2) 参数 是 逗号隔开
- apply( obj, [ 参数1 , 参数2] ) 参数 是 数组形式
function a(x,y){
console.log(this ,x,y);//
}
a(1,2);// window
var obj={
"name":"gao"
}
a.call(obj,1,2);// obj
a.apply(obj,[1,2] );//obj
a.bind(obj,1,2)();//obj
相同点
- 1、都可以改变this 的指向
- 2、都可以传参
apply方法
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变thi指向一次。
var name="martin";
var obj={
name:"lucy",
say:function(year,place){
console.log(this.name+" is "+year+" born from "+place);
}
};
var say=obj.say;
setTimeout(function(){
say.apply(obj,["1996","China"])
} ,0);
//lucy is 1996 born from China,this改变指向了obj
say("1996","China")
//martin is 1996 born from China,this指向window,说明apply只是临时改变一次this指向
call方法
采纳以参数列表的形式传入,而apply以参数数组的形式传入。
call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。
var arr=[1,10,5,8,3];
console.log(Math.max.call(null,arr[0],arr[1],arr[2],arr[3],arr[4])); //10
bind方法
Function.prototype.bind=function () {
var _this=this;
var context=arguments[0];
var arg=[].slice.call(arguments,1);
return function(){
arg=[].concat.apply(arg,arguments);
_this.apply(context,arg);
}
};
7. 面向对象、原型、原型链、继承
- proto/constructor属性时对象独有的
- prototype属性时函数独有的
面向对象的基本特征:
- 封装: 所用的方法和类都是一种封装,使用时直接调用
- 继承: 子类继承父类/一个类继承(复制)另一个类的属性、方法
- 多态: 方法(接口)重写/方法的个性化
原型:每一个函数 中 都有一个 prototype 对象 ,这个对象叫原型对象;
原型链:每一个对象中 都有__ proto__ 指向构造函数的 prototype (原型/原型对象) ,一直到null 为止,形成的作用域链叫原型链。
继承的几种方式:
- 1、原型链继承
- 2、类式继承
- 3、组合继承
原型链继承的优缺点
- 优点 可以继承 父类的所有 (自身+原型)
- 缺点 不可以 动态的给 父类 构造函数 传参数
类式继承 语法、优缺点
- 借用构造函数实现继承 – 通过改变this 指向 实现继承
- 语法: 子类构造函数内部 父类.call(this, 参数1,参数2);
- 优点: 可以给 父类 构造函数传参
- 缺点: 不能继承 父类 原型
组合继承
- 原型链继承 与 类式继承 的结合
- 原型链继承 继承父类的所有
- 类式继承 可以动态的给父类传参数
8. 什么是闭包,使用场景及优缺点?
闭包 含义:
- 可以访问 一个函数 内部变量 的函数 叫闭包函数,闭包函数简称闭包。
- 函数嵌套函数,内部函数可以访问外部函数的变量
- 闭包是将函数内部和函数外部连接起来的桥梁。
闭包 使用:
- 1.采用函数引用方式的setTimeout调用
- 2.小范围代替全局变量
- 3.访问私有变量的特权方法
- 4.创建特权方法用于访问控制
优点:
- 1、 可以访问 一个函数内部变量
- 2、 闭包变量 长期驻扎内存
缺点:
- 因为 闭包变量 长期驻扎内存 不会触发垃圾回收机制,可能 内存占用过大 导致 内存泄漏
扩展:垃圾回收机制、闭包变量
9. 什么是深拷贝、浅拷贝?
- 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
- 浅拷贝只复制指向某个对象的指针/地址,而不复制对象本身,新旧对象还是共享同一块内存,相互影响。
- 浅拷贝:如果 是基本类型,拷贝的就是基本类型的值;如果 是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了,就会影响到另一个对象。(Object.assign, clone, = 等)
- 深拷贝:在计算机中开辟一块新的内存地址,递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去一一赋值 ,相互不影响(cloneDeep, JSON.stringify, 循环,递归,concat(数组的深拷贝))
10. this指向
函数的调用方式决定了 this 的指向不同
- 普通函数调用,此时 this 指向 window(父级)
- 构造函数调用, 此时 this 指向 实例对象
- 对象方法调用, 此时 this 指向 该方法所属的对象
- 通过事件绑定的方法, 此时 this 指向 绑定事件的对象
- 定时器函数, 此时 this 指向 window
箭头函数中的this指向,箭头函数中没有自己的this,它的this是继承而来,默认指向在定义它时所处的对象(宿主对象)。
当函数被当作监听事件处理函数时,其this指向触发该事件的元素(针对于addEventListener 事件)
11. 什么是防抖、节流?
- 防抖(debounce):
设置延时器,短时间高频率触发只有最后一次触发成功 - 节流(throttle):
设置状态锁,短时间高频率触发只有第一次触发成功
防抖和节流区别:
- 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会真正执行一次事件处理函数,
- 而函数防抖只是在最后一次触发后才会执行。
12. es6新增哪些特性?箭头函数和普通函数区别?什么是promise?
es6新增哪些特性
- 1.新增了块级作用域(let,const)
- 2.提供了定义类的语法糖(class)
- 3.新增了一种基本数据类型(Symbol)
- 4.新增了变量的解构赋值
- 5.函数参数允许设置默认值,引入了rest参数,新增了箭头函数。
- 6.数组新增了一些API,如isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法。
- 7.对象和数组新增了扩展运算符
- 8.ES6新增了模块化(import / export)
- 9.ES6新增了Set和Map数据结构。
-10.ES6原生提供Proxy构造函数,用来生成Proxy实例 - 11.ES6新增了生成器(Generator)和遍历器(Iterator)
箭头函数和普通函数区别?
- (1) 箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- (2) 箭头函数不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- (3) 箭头函数不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数替。
- (4) 箭头函数不可以使用yield命令,因此箭头函数不能用作 Generator 函数
- (5) 箭头函数使用call()和apply()调用—即使是call,apply,bind等方法也不能改变箭头函数this的指向
什么是promise?
简单来说可以把promise当作一个装着异步操作结果的容器。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise 提供统一的API,各种异步操作都可以用同样的方法进行处理。它将异步函数以同步的方式书写,也解决了回调地狱问题
特点:
- (1)对象状态不受外界影响
- (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果
缺点:
- (1)无法取消promise,一旦新建它就会立即执行,无法中途取消
- (2)如果不设置回调函数,promise内部抛出的错误,不会反应到外部
- (3)无法得知目前进展到哪一个阶段(刚刚开始还是即将结束)
三个状态:进行中、已成功、以失败。
13. cookie、sessionStorage、localstorage
- cookie用来保存登录信息,大小限制为4KB左右
- localStorage是Html5新增的,用于本地数据存储,保存的数据没有过期时间,一般浏览器大小限制在5MB
- sessionStorage接口方法和localStorage类似,但保存的数据的只会在当前会话中(会话是指一个终端用户与交互系统进行通讯的过程)保存下来,页面关闭后会被清空。
14. 什么是ajax?
-
1)、ajax的全称是AsynchronousJavascript+XML
异步传输+js+xml。
所谓异步,就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果我们可以再来处理这个事。 -
2)、即异步的 JavaScript 和 XML,是一种用于创建快速动态网页的技术;传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。使用AJAX则不需要加载更新整个网页,实现部分内容更新。
-
3)、Ajax是一种技术方案,但并不是一种新技术。它依赖现有的CSS/HTML/JavaScript,而其中最核心的依赖是浏览器提供的XMLHttpRequest对象,是这个对象使得浏览器可以发出HTTP请求与接收HTTP响应。实现了在页面不刷新个情况下和服务器进行数据交互。
-
4)、ajax请求应该放在mounted,因为js是单线程,ajax异步获取数据
-
5)、具体步骤:
1、创建异步请求对象
2、打开链接
3、发送请求
4、监听状态改变
5、响应得到数据(判断 ajax 状态成功 readyState4 && http 状态成功status200 前提下)
15. http和https区别?http请求方式、请求报文、响应报文、get、post区别、状态码
http和https区别
- HTTP的URL由 http://起始且默认使用端口80,
- 而HTTPS的URL由 https://起始且默认使用端口443
- HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的 SSL 加密传输协议
- HTTP的连接很简单,是无状态的,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全
服务器请求的区别:
- 1、Get请求是可以被缓存的,举个例子,你访问baidu.com,就是向baidu的服务器发了个Get请求,这个请求的返回,也就是baidu的主页页面内容,会被缓存在你浏览器中,短时间再次访问,其实是拿到的浏览器中的缓存内容。另外Get请求只能接收ASCII码的回复
- 2、Post请求是不可以被缓存的。对于Post方式提交表单,刷新页面浏览器会弹出提示框 “是否重新提交表单”,Post可以接收二进制等各种数据形式,所以如果要上传文件一般用Post请求。
参数放请求头和请求体的差别:
- 1、Get请求通常没有请求体,在TCP传输中只需传输一次(而不是一个包),所以Get请求效率相对高。
- 2、Post请求将数据放在请求体中,而实际传输中,会先传输完请求头,再传输请求体,是分为两次传输的(而不是两个包)。Post请求头会比Get更小(一般不带参数),请求头更容易在一个TCP包中完成传输,更何况请求头中有Content-Length的标识,可以更好地保证Http包的完整性。
GET请求和POST请求的区别是什么?
- GET请求参数是通过URL进行传递的,POST请求的参数包含在请求体当中。
- GET请求比POST请求更不安全,因为参数直接暴露在URL中,所以,GET请求不能用来传递敏感信息。
- GET请求在url中传递的参数是有长度限制的(在HTTP协议中并没有对URL的长度进行限制,限制是特定的浏览器以及服务器对他的限制,不同浏览器限制的长度不同。),POST对长度没有限制。
- GET请求参数会完整的保留在浏览器的历史记录中,POST请求的参数不会保留。
- GET请求进行url编码(百分号编码),POST请求支持多种编码方式。
- GET请求产生的URL地址是可以被bookmark(添加书签)的,POST请求不可以。
- GET请求在浏览器回退的时候是无害的,POST请求会.再次提交数据。
- GET请求在浏览器中可以被主动cache(缓存),而POST请求不会,可以手动设置。
- 对于GET请求,浏览器会把http header和data一起发送出去,服务器响应200,请求成功。
对于POST请求,浏览器先发送header,服务器会响应100(已经收到请求的第一部分,正在等待其余部分),浏览器再次发送data,服务器返回200,请求成功。
请求头:
- Accept 告诉服务器,客户端支持的数据类型
- Accept-Charset 告诉服务器,客户端采用的编码。
- Accept-Encoding 告诉服务器,客户机支持的数据压缩格式。
- Accept-Language 告诉服务器,客户机的语言环境
- Host 客户机通过这个头告诉服务器,想访问的主机名。
- If-Modified-Since: 客户机通过这个头告诉服务器,资源的缓存时间。(好多请求中不显示)
- Referer 客户机通过这个头告诉服务器,它是从哪个资源来访问服务器的。(一般用于防盗链)
- User-Agent: 客户机通过这个头告诉服务器,客户机的软件环境
- Cookie 客户机通过这个头告诉服务器,可以向服务器带数据
- Connection 客户机通过这个头告诉服务器,请求完后是关闭还是保持链接
- Date 客户机通过这个头告诉服务器,客户机当前请求时间。
响应头:
- Location 这个头配合302状态码使用,告诉用户端找谁。
- Server 服务器通过这个头,告诉浏览器服务器的类型
- Content-Encoding 服务器通过这个头,告诉浏览器数据采用的压缩格式。
- Content-Length 服务器通过这个头,告诉浏览器回送数据的长度。
- Content-Language 服务器通过这个头,告诉服务器的语言环境。
- Content-Type 服务器通过这个头,回送数据的类型
- Last-Modified 服务器通过这个头,告诉浏览器当前资源的缓存时间。
- Refresh 服务器通过这个头,告诉浏览器隔多长时间刷新一次。
- Content-Disposition 服务器通过这个头,告诉浏览器以下载的方式打开数据。
- Transfer-Encoding 服务器通过这个头,告诉浏览器数据的传送格式。
- ETag 与缓存相关的头。
Expires 服务器通过这个头,告诉浏览器把回送的数据缓存多长时间。-1或0不缓存。 - Cache-Control和Pragma 服务器通过这个头,也可以控制浏览器不缓存数据。
- Connection 服务器通过这个头,响应完是保持链接还是关闭链接
- Date 告诉客户机,返回响应的时间。
HTTP状态码
- 1xx表示客户端应该继续发送请求
- 2xx表示成功的请求
- 200表示OK,正常返回信息
- 201表示请求成功且服务器创建了新的资源
- 202表示服务器已经接受了请求,但还未处理
- 3xx表示重定向
- 301表示永久重定向,请求的网页已经永久移动到新位置
- 302表示临时重定向
- 304表示自从上一次请求以来,页面的内容没有改变过
- 4xx表示客户端错误
- 401表示服务器无法理解请求的格式
- 402表示请求未授权
- 403表示禁止访问
- 404表示请求的资源不存在,一般是路径写错了
- 5xx表示服务器错误
- 500表示最常见的服务器错误
- 503表示服务器暂时无法处理请求,一段时间后可能恢复正常
16. http缓存
- 1.强制缓存
强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程 -
- 缓存存储
在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。
- 缓存存储
- 3.协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
a.协商缓存生效,返回304
b.协商缓存失效,返回200和请求结果
17. new 的作用
- 1.创建空对象
- 2.新对象执行prototype连接原型
- 3.绑定this到新对象上
- 4.执行构造函数
- 5.返回新对象
18. token和cookie的区别
一、token与cookie的区别?
- 1.1、cookie是浏览器用来存储本地信息的文件(4KB);token(令牌)是由服务器按一定算法生成的密令。
- 1.2、服务器生成的用户识别信息必将响应给浏览器的cookie中存储;token可以由前端指定存放到localStorage、sessionStorage或cookie中。
- 1.3、每次浏览器发起HTTP请求都会自动携带cookie内容一起发送给服务器;token严格来说只是程序员定义的字符串,没有任何自主功能,所以浏览器发起的HTTP请求不会自动携带token字符串。
- 1.4、cookie在用户登出后会注销;token不会,因为是一个字符串,但可以手动增加相关操作去实现这个功能。
二、token是如何避免CSRF攻击的?
-
2.1、CSRF(跨域请求伪造)的攻击特点是:需要用户自己点击恶意网站的隐藏HTTP请求链接,这个发出去的HTTP请求会自动的携带上你的cookie信息给服务器,于是完成了请求伪造攻击。(就是恶意网站利用了cookie会被HTTP请求自动添加的特性加以利用攻击)
-
2.2、token因为是自定义字符串,所以HTTP请求不会自动携带它,CSRF也就无法得手了(即使用户点击了恶意网站的请求也无法被拿走token),之所以说token天然防CSRF,但也不是绝对的,前提是不把token存储到浏览器的cookie里。
-
2.3、【补充】Ajax跨域请求不会自动携带cookie,源自浏览器的ajax同源策略,要想ajax自动携带cookie,需要在服务端进行配置。
19. 提升加载速度
- 减少对服务器的文件请求
- 减少图片/文件的大小(压缩)
- 延迟请求/异步加载
20. 虚拟dom和真实dom的区别
- DOM就是文档对象模型
- 虚拟DOM是一个轻量级的JavaScript对象,它原本是真实dom的副本,是一个节点树,它将元素,它们的属性和内容作为对象及属性。渲染页面时会创建一个节点树,然后响应数据模型中的变化来更新该树,该变化是用户或系统完成的各种动作引起的。
- 真实的DOM在浏览器通过dom.api操作的,复杂的对象
- 虚拟DOM:可以通过this.$slots.default查看
1.虚拟dom比真实dom体积小,操作是相对来说消耗性能少,如果在页面中删除一个dom,会引起重绘,影响后边元素的布局
- 1):虚拟Dom不会进行回流和重绘操作
- 2):虚拟dom进行频繁的修改,然后一次性比较并修改真实DOM中需要改的部分,最后并在真实DOM中进行回流和重绘,减少过多DOM节点的回流和重绘
- 3)真实Dom频繁的回流和重绘效率非常低
虚拟DOM 的工作过程?
- 1:首先根据数据创建虚拟dom,它反映真实DOM的结构,然后由虚拟dom创建真实的dom树,真实dom树生成之后,在渲染到页面上。
- 2:如果数据发生改变,创建新的虚拟dom树,比较两个树的区别,调整虚拟dom的内部状态
- 3:在虚拟dom收集到足够的改变时,再一次性应用到真实dom上,渲染到页面。
优点:
- 保证性能下限
- 无须手动操作dom
- 跨平台
缺点:
- 无进行极致优化
- 首次渲染大量DOM时,由于多了一层dom计算会比innerHTMl插入慢
21. 回流和重绘
- 回流(reflow):当render tree中的元素的宽高、布局、显示、隐藏或元素内部文字结结构
发生改变时,会影响自身及其父元素、甚至追溯到更多的祖先元素发生改变,则会导致元素内部、
周围甚至整个页面的重新渲染,页面发生重构,回流就产生了。 - 重绘(repaint):元素的结构(宽高、布局、显示隐藏、内部文字大小)未发生改变,只是
元素的外观样式发生改变,比如背景颜色、内部文字颜色、边框颜色等。此时会引起浏览器重绘,
显然重绘的速度快于回流。
区别
- 回流必将引起重绘,而重绘不一定会引起回流。例如:只有颜色改变的时候就只会发生重绘而不会引起回流。
- 当页面布局和几何属性改变时就需要回流。例如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变。
- 每个页面至少需要一次回流,就是在页面第一次加载的时候,这时就是回流,因为要构建render tree。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,这就是重绘。
22. 跨域问题
什么是跨域?
在了解跨域之前,首先要知道什么是同源策略(same-origin policy)。简单来讲同源策略就是浏览器为了保证用户信息的安全,防止恶意的网站窃取数据,禁止不同域之间的JS进行交互。对于浏览器而言只要域名、协议、端口其中一个不同就会引发同源策略,从而限制他们之间如下的交互行为:
- 1.Cookie、LocalStorage和IndexDB无法读取;
- 2.DOM无法获得;
- 3.AJAX请求不能发送。
跨域的严格一点的定义是:只要协议,域名,端口有任何一个的不同,就被当作是跨域。
解决方案
- jsonp跨域 script标签引入js文件不受跨域影响
- document.domain + iframe 此方案仅限主域相同,子域不同的应用场景。
- location.hash + iframe跨域 父页面改变iframe的src属性,location.hash的值改变,不会刷新页面(还是同一个页面),在子页面可以通过window.localtion.hash获取值。
- window.name + iframe跨域
- postMessage跨域postMessage是HTML5提出的,可以实现跨文档消息传输。
用法
getMessageHTML.postMessage(data, origin);
data: html5规范支持的任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin: 协议+主机+端口号,也可以设置为"*“,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/"。 - 跨域资源共享(CORS)
只要在服务端设置Access-Control-Allow-Origin就可以实现跨域请求,若是cookie请求,前后端都需要设置。
由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,并非当前页的cookie。
CORS是目前主流的跨域解决方案。 - WebSocket协议跨域
WebSocket协议是HTML5的新协议。能够实现浏览器与服务器全双工通信,同时允许跨域,是服务端推送技术的一种很好的实现。 - nginx代理跨域
原理:同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不存在跨越问题。
实现:通过nginx配置代理服务器(域名与test1相同,端口不同)做跳板机,反向代理访问test2接口,且可以修改cookie中test信息,方便当前域cookie写入,实现跨域登录。
23. 三次握手和四次挥手
TCP基础入门
- 1、TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接。
- 2、客户端在收发数据前要使用 connect() 函数和服务器建立连接。建立连接的目的是保证IP地址、端口、物理链路等正确无误,为数据的传输开辟通道。
- 3、TCP建立连接时要传输三个数据包,俗称三次握手(Three-way Handshaking)
①首先 Client 端发送连接请求报文(第一次握手是由客户端向服务器发送的SYN数据包。)
②Server 端接受连接后回复 ACK 报文,并为这次连接分配资源。(第二次握手是服务器在接收到客户端的SYN数据包后发送给客户端的回应数据包。)
③Client 端接收到 ACK 报文后也向 Server 段发生 ACK 报文,并分配资源,这样 TCP 连接就建立了(第三次握手 客户端收到服务器的SYN数据包后,会发送一个数据包来应答。)
为什么需要三次握手而不是两次或者四次
- 1、防止出现历史连接
- 2、避免资源浪费
挥手
- 第一次挥手
由客户端发起断开请求,断开的是客户端向服务器写数据的方向连接,服务器还是能继续向客户端写入数据的 - 第二次挥手
当服务器接收到客户端的FIN数据包后,会返回确认应答包 - 第三次挥手
第三次挥手是服务器向客户端释放连接发送的FIN数据包 - 第四次挥手
第四次挥手是客户端向服务器发送的数据包
为什么四次挥手
- TCP是一个全双工通信协议。客户端可以向服务器发送数据,服务器也能向客户端发送数据。
- 当客户端断开连接的时候,申请断开的是客户端向服务器发送数据的通道,当客户端向服务器发送数据的通道关闭之后,客户端还不能退出,因为客户端还可能有数据要收取。
- 这个时候服务器可能还有数据还未发送完给客户端。服务器向客户端发送数据的通道还不能关闭。等待服务器发送数据完毕之后,才会发送FIN数据包,并且要等待客户端的回应,因为FIN数据包可能会失效,所以客户端还需要再次向服务器发送ACK确认应答包。当服务器收到客户端的ACK时,才会真正关闭。
- 这就需要四次挥手。

被折叠的 条评论
为什么被折叠?



