前端常见面试题

文章目录

    • 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. 数据类型有哪些?如何判断数据类型?

数据类型

    1. 基本数据类型:string,number,Boolean,null,undefined;
    1. 引用数据类型object(Object,Array),function,regex 正则 ,Date
    1. ES6新增 symbol

如何判断数据类型

  • typeof
    优点:能够快速区分基本数据类型 + function
    缺点:不能将Object、Array和Null区分,都返回object
    console.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); // object
    
  • instanceof
    优点:能够区分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); // true
    
  • Object.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.强制缓存
    强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程
    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时,才会真正关闭。
  • 这就需要四次挥手。

你可能感兴趣的:(前端)