最常见的js面试题

面试最常见的十五道js面试题

1. 数据类型有哪些?如何判断数据类型?
2. 字符串、数组 有哪些方法?手写去重4、排序
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区别、状态码

1、数据类型有哪些?如何判断数据类型?

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

typeof

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
优点:能够快速区分基本数据类型 + function
缺点:不能将Object、Array和Null区分,都返回object

instanceof

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

优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象

缺点:Number,Boolean,String基本数据类型不能判断

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.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 事件循环机制。

JS 的 事件执行机制 先同步 —> 所有异步 微任务 —> 异步宏任务
异步 微任务:promise 回调(then , .catch)
异步 宏任务: setTimeOut SetInterval 注意: 有哪些是 一创建就立即执行的

扩展
这是一个两分钟的简短视频网址、听懂后极好理解:2分钟了解 JavaScript Event Loop | 面试必备_哔哩哔哩_bilibili

温馨提示 : 上面点击网址就会出现

1、为什么js是单线程

js作为主要运行在浏览器的脚本语言,js主要用途之一是操作DOM。

在js高程中举过一个栗子,如果js同时有两个线程,同时对同一个dom进行操作,这时浏览器应该听哪个线程的,如何判断优先级?

为了避免这种问题,js必须是一门单线程语言,并且在未来这个特点也不会改变。

2、执行栈与任务列表

因为js是单线程语言,当遇到异步任务(如ajax操作等)时,不可能一直等待异步完成,再继续往下执行,在这期间浏览器是空闲状态,显而易见这会导致巨大的资源浪费。

执行栈

当执行某个函数、用户点击一次鼠标,Ajax完成,一个图片加载完成等事件发生时,只要指定过回调函数,这些事件发生时就会进入执行栈队列中,等待主线程读取,遵循先进先出原则。

主线程

要明确的一点是,主线程跟执行栈是不同概念,主线程规定现在执行执行栈中的哪个事件。

主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码。

当遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中,我们称之为任务队列(Task Queue)。

当主线程将执行栈中所有的代码执行完之后,主线程将会去查看任务队列是否有任务。如果有,那么主线程会依次执行那些任务队列中的回调函数。

js 异步执行的运行机制。
  1. 所有任务都在主线程上执行,形成一个执行栈。

  2. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

  3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。

  4. 主线程不断重复上面的第三步

宏任务与微任务:

异步任务分为 宏任务(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

JS执行机制

js 是一个单线程 执行语言 在单线程执行过程中 ​ 又把任务分成 同步任务 和异步任务 ​
同步和异步任务分别进入不同的执行环境, ​ 同步的进入主线程,即主执行栈, ​ 异步的进入 异步队列。 ​
主线程内的任务执行完毕为空, ​ 会去 异步队列 读取对应的任务,推入主线程执行。

   上述过程的不断重复就是我们说的 Event Loop (事件循环)。
   异步队列中又分为 微任务  和  宏任务 
   先执行微任务   再执行宏任务
   microtask   微任务  主要包含:Promise、process.nextTick(Node.js 环境)
   (macro)task 宏任务 主要包含:、setTimeout、setInterval ,ajax 
   Event loop 计算机系统的一种运行机制,是一个程序结构,用于等待和发送消息和事件

就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event
Loop线程"(可以译为"消息线程")
JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题

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指向一次。

ar 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方法

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

采纳以参数列表的形式传入,而apply以参数数组的形式传入。

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.面向对象、原型、原型链、继承

面向对象的基本特征:

  1. 封装: 所用的方法和类都是一种封装,使用时直接调用

  2. 继承: 子类继承父类/一个类继承(复制)另一个类的属性、方法

  3. 多态: 方法(接口)重写/方法的个性化

原型:

​  每一个函数 中 都有一个 prototype 对象 ,这个对象叫原型对象;(原型)

​   function a(){

   ​}

​   console.log( a.prototype );

原型链:

每一个对象中 都有__ proto__ 指向构造函数的 prototype (原型/原型对象) ,一直到null 为止,形成的作用域链叫原型链。

var obj=new Object();//Object() 构造函数 有 prototype(原型);

console.log( obj.__ proto__      == Object.prototype );//对象中   __proto__  指向构造函数的 prototype 

​     //输出为 true      

console.log( obj.__proto__.__proto__  ); // 输出null

继承的几种方式

1、原型链继承
2、类式继承
3、组合继承

原型链继承的优缺点

​ // 优点 可以继承 父类的所有 (自身+原型)

​ // 缺点 不可以 动态的给 父类 构造函数 传参数

父类

   function Father(name,sex){this.name=name;this.sex=sex;

   }

   Father.prototype.say=function(){

​       console.log("会说陕西话");

   }

** 子类**

 function Son(){

   }

   Son.prototype= new Father( "王","男" );

   var one = new Son();

   console.log(one,one.name,one.sex);

   one.say();

类式继承 语法、优缺点

借用构造函数实现继承 – 通过改变this 指向 实现继承

语法: 子类构造函数内部 父类.call(this, 参数1,参数2);

优点: 可以给 父类 构造函数传参

缺点: 不能继承 父类 原型

父类

   function Father(name,sex){
   
   this.name=name;
   
   this.sex=sex;
   
   }
   
   Father.prototype.say=function(){
   
   console.log("会说陕西话");
   
   }

子类

  function Son(a,b){

   // console.log(a,b); 

   Father.call(this,a,b);

   }
   
  var one =new Son("高","男");  //实例化
   
  console.log(one);

组合继承

原型链继承 与 类式继承 的结合

原型链继承 继承父类的所有

类式继承 可以动态的给父类传参数

父类

   function Father(name, sex) {
   
       this.name = name;
   
       this.sex = sex;
   
   }
   
   Father.prototype.say = function () {
   
       console.log("会说陕西话");
   
   }

组合继承

   function Son(a,b){

   Father.call(this,a,b); //类式继承

   }

   Son.prototype= Father.prototype //原型链继承

   var one =new Son("gao","男");

   console.log(one);
   
   one.say();

8.什么是闭包,使用场景及优缺点?

闭包 含义:

可以访问 一个函数 内部变量 的函数 叫闭包函数,闭包函数简称闭包
代码:

  function a(){var b=123;// 闭包函数return function(){

​                console.log(b); // b 闭包变量}}a()();     //  a(); 调用 a 函数  a()(); 调用闭包函数   此处输出结果 打印 123

写法/表现形式:

函数嵌套函数,内部函数可以访问外部函数的变量

本质:

闭包是将函数内部和函数外部连接起来的桥梁。

使用场景

​ 1.采用函数引用方式的setTimeout调用

​ 2.小范围代替全局变量

​ 3.访问私有变量的特权方法

​ 4.创建特权方法用于访问控制

优点:

​ 1、 可以访问 一个函数内部变量

​ 2、 闭包变量 长期驻扎内存

缺点:

​因为 闭包变量 长期驻扎内存 不会触发垃圾回收机制,可能 内存占用过大 导致 内存泄漏 扩展:垃圾回收机制、闭包变量

/*

​ 垃圾回收机制:

​ 1、标记清除

​ 2、引用计数

​ */

闭包变量: 访问 一个函数 内部的变量, 这个变量叫闭包变量

9.什么是深拷贝、浅拷贝?

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
浅拷贝只复制指向某个对象的指针/地址,而不复制对象本身,新旧对象还是共享同一块内存,相互影响。

浅拷贝:如果 是基本类型,拷贝的就是基本类型的值;如果 是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了,就会影响到另一个对象。

深拷贝:在计算机中开辟一块新的内存地址,递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去一一赋值 ,相互不影响

扩展:

基本数据类型:名值存储在栈内存中 引用数据类型:-名存在栈内存中,值存在于堆内存中,栈内存会提供一个引用的地址指向堆内存中值

深拷贝

  JSON.parse(JSON.stringify())
let arr = [1, 3, {
       username: ' kobe'
   }];
let arr4 = JSON.parse(JSON.stringify(arr));
   arr4[2].username = 'duncan'; 
   console.log(arr, arr4)

原理:

用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数

手写递归方法

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制, 就是深度拷贝

//定义检测数据类型的功能函数

10、this指向

(1)、函数的调用方式决定了 this 的指向不同:
1).普通函数调用,此时 this 指向 window
function fn() {
       console.log(this);   // window
     }
     fn();  //  window.fn(),此处默认省略window
2)构造函数调用, 此时 this 指向 实例对象
   function Person(age, name) {
         this.age = age;
         this.name = name
         console.log(this)  // 此处 this 分别指向 Person 的实例对象 p1 p2
     }
    var p1 = new Person(18, 'zs')
    var p2 = new Person(18, 'ww)
3)对象方法调用, 此时 this 指向 该方法所属的对象
var obj = {
       fn: function () {
         console.log(this); // obj
       }
     }
    obj.fn();
4)通过事件绑定的方法, 此时 this 指向 绑定事件的对象
<body>
    <button id="btn">hh</button>

<script>
    var oBtn = document.getElementById("btn");
    oBtn.onclick = function() {
        console.log(this); // btn
    }
</script>

</body>
5)定时器函数, 此时 this 指向 window
setInterval(function () {
       console.log(this); // window
     }, 1000);

6)箭头函数中的this指向,箭头函数中没有自己的this,它的this是继承而来,默认指向在定义它时所处的对象(宿主对象)。

7)当函数被当作监听事件处理函数时,其this指向触发该事件的元素(针对于addEventListener 事件)

(2)、更改this指向的三个方法

call() 方法 (.call(obj,2,3))
apply() 方法(.apply(obj.[2,3]))
call接受的参数为一个一个的,但是apply接受的参数只能为一个严格的数组
bind()方法 (.bind(obj))

相同点: 都是原生js方法,改变函数中的this指向;都可以传递参数,第一个参数为把this指向哪里去,即目标元素

不同点

返回值的区别:

1.bind的返回值是一个函数,需要再次调用函数,而call和apply是立即调用。

参数使用的区别:

2.传参的不同 call() bind()是直接传参 而apply 是要将参数放到数组中

11、什么是防抖、节流?

说白了,防抖节流就是使用定时器来实现我们的目的。

防抖(debounce):

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
典型的案例就是输入框搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,则重新计时。

节流(throttle):

规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只有一次生效。
典型的案例就是鼠标不断点击触发,规定在n秒内多次点击只生效一次。

为什么要掌握防抖和节流

函数节流(throttle)与函数防抖(debounce)都是可以限制函数的执行频次,根据不同的场景来对执行频率进行限制,避免了函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。

想深入了解的:

参考文章:防抖与节流

12、es6新增哪些特性?箭头函数和普通函数区别?什么是promise?

1)、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)

2)、箭头函数和普通函数区别?

(1) 箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2) 箭头函数不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3) 箭头函数不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数替。
(4) 箭头函数不可以使用yield命令,因此箭头函数不能用作 Generator 函数
(5) 箭头函数使用call()和apply()调用

3)、什么是promise?

简单来说可以把promise当作一个装着异步操作结果的容器。从语法上说,Promise
是一个对象,从它可以获取异步操作的消息。Promise 提供统一的
API,各种异步操作都可以用同样的方法进行处理。它将异步函数以同步的方式书写,也解决了回调地狱问题

特点:

(1)对象状态不受外界影响
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果

缺点:

(1)无法取消promise,一旦新建它就会立即执行,无法中途取消
(2)如果不设置回调函数,promise内部抛出的错误,不会反应到外部
(3)无法得知目前进展到哪一个阶段(刚刚开始还是即将结束)

三个状态:进行中、已成功、以失败。

13、cookie、sessionStorage、localstorage

cookie、sessionStorage、localstorage的含义(了解即可)

cookie

Cookie 是小甜饼的意思。顾名思义,cookie确实非常小,它的大小限制为4KB左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie中存入一段辨别用户身份的数据来实现的。

localStorage

localStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫userData的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你方案是种不错的选择

sessionStorage

sessionStorage 与 localStorage 的接口类似,但保存数据的生命周期与 localStorage不同。直译过来是“会话”。它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage中的数据就会被清空。

三者的异同

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 前提下)

 var xhr = new XMLHttpRequest();  // 创建异步请求对象

    xhr.open("GET", "./1.json", true)// 打开连接

        // 请求方式 、请求地址、是否异步

xhr.setRequestHeader ("content-type", "application/x-www-form-urlencoded" )

    xhr.send(); //发送请求  //如果是post  xhr.send( 参数 )

       xhr.onreadystatechange = function () {   // 监听状态改变  

      //  ajax 状态  4成功    http 状态 200 成功

      if (xhr.readyState == 4 && xhr.status == 200) {

        // json

        /*

         console.log(  xhr.responseText  );  

         var obj= JSON.parse( xhr.responseText );  //  获取响应数据

        */

        //  xml

        console.log(xhr.responseXML);

        var dom = xhr.responseXML;

        console.log(dom.getElementsByTagName("name")[0]);

        console.log(dom.getElementsByTagName("name")[0].tagName);

      }

    }

在这里插入图片描述

15、http和https区别?http请求方式、请求报文、响应报文、get、post区别、状态码

解:

1)、 http和https区别

HTTP的URL由 http://起始且默认使用端口80,

而HTTPS的URL由 https://起始且默认使用端口443

HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的 SSL 加密传输协议

HTTP的连接很简单,是无状态的,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全

2)、http请求方式

HTTP1.1目前支持7种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。

GET ⽅法

发送⼀个请求来取得服务器上的某⼀资源

POST ⽅法

向 URL 指定的资源提交数据或附加新的数据

PUT ⽅法

跟 POST ⽅法很像,也是想服务器提交数据。但是,它们之间有不同。 PUT 指定了资 源在服务器上的位置⽽ POST 没有

HEAD ⽅法

只请求⻚⾯的⾸部

DELETE ⽅法

删除服务器上的某资源

OPTIONS ⽅法

它⽤于获取当前 URL 所⽀持的⽅法。如果请求成功,会有⼀个 Allow 的头包含类 似 “GET,POST” 这样的信息

TRACE ⽅法

TRACE ⽅法被⽤于激发⼀个远程的,应⽤层的请求消息回路

CONNECT ⽅法

把请求连接转换到透明的 TCP/IP 通道

3)、http请求报文、http响应报文

(1) HTTP报文由从客户机到服务器的请求和从服务器到客户机的响应构成。请求报文格式如下: 请求行 - 通用信息头 - 请求头 - 实体头-报文主体 请求行以方法字段开始,后面分别是 URL 字段和 HTTP 协议版本字段,并以 CRLF 结尾。SP 是分隔符。除了在最后的CRLF 序列中 CF 和 LF 是必需的之外,其他都可以不要
(2) HTTP响应也由三个部分组成,分别是:状态行、消息报头、响应正文。

4)、get 与post 的区别

区别一:语义上的区别:

1、Get向服务器请求数据。依照HTTP协议,get 是用来请求数据。


2、Post向服务器发数据。依照HTTP协议,Post的语义是向服务器添加数据,也就是说按照Post的语义,该操作是会修改服务器上的数据的。

区别二:服务器请求的区别:

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包的完整性。

在这里插入图片描述

6)HTTP状态码

1xx表示客户端应该继续发送请求

2xx表示成功的请求

200表示OK,正常返回信息

201表示请求成功且服务器创建了新的资源

202表示服务器已经接受了请求,但还未处理

3xx表示重定向

301表示永久重定向,请求的网页已经永久移动到新位置

302表示临时重定向

304表示自从上一次请求以来,页面的内容没有改变过

4xx表示客户端错误

401表示服务器无法理解请求的格式

402表示请求未授权

403表示禁止访问

404表示请求的资源不存在,一般是路径写错了

5xx表示服务器错误

500表示最常见的服务器错误

503表示服务器暂时无法处理请求,一段时间后可能恢复正常

  • 28
    点赞
  • 176
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值