JavaScript面试题

.

1.数据类型有哪几种, 检测方法是什么??

基本数据类型
​ ES5-------Number/Boolean/String/Undefined/Null
​ ES6新增—symbol
引用数据类型
​ Object

检测方法4种

 1、Object.prototype.toString.call()
     **作用: 可以检测所有数据类型**
     **所有数据类型都可以检测,而且非常正确**
     语法: Object.prototype.toString.call( 'xxx'/11/[ ] )
     返回值: [object Xxx], Xxx 就是对象的类型
 2、constructor
    **作用: 可以检测基本数据类型和引用数据类型**
    **弊端: 把类的原型进行重写, 很有可能把之前的constructor覆盖 检测出来的结果就会不准确**
    语法: ("xx")/([ ])/(function(){ }).constructor === String/Array/Function	
    返回值: true/false
3、instanceOf
    **原理: 判断对象类型,基于原型链去判断(obj instanceof Object)**
    **左边对象的原型链proto上是否有右边构造函数的proptotype属性**
    **作用: 判断左边的对象是否是右边构造函数的实例**
    **弊端: 用于引用类型的检测, 对于基本数据类型不生效**
    语法: " "/[ ]/true instanceOf String/Array/Boolean
    返回值: true/false
4、typeOf
     **作用: 用于检测基本数据类型和函数**
     **弊端: 引用数据类型(Arrary/function/object)只会返回Object, 不起作用**
      语法: typeOf " "/[ ]/xx
     返回值: "string"/"boolean"/"object" (无法区分)

2.原型/原型链,请简述prototype、proto constructor三者的关系

每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一 个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对 象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找… 这个操作被委托在整个原型链上

1、prototype:每一个函数都有一个prototype这个属性,而这个属性指向一个对象,这个对象我们叫做原型对象
作用: 节约内存扩展属性和方法可以实现类之间的继承
2 、 proto :每一个对象都有一个_proto_属性_proto_ 指向创建自己的那个构造函数的原型对象对象 可以直接访问 proto 里面的属性和方法
3、constructor:指向创建自己的那个构造函数 ,是原型上的方法

总结: 当我们创建一个构造函数的时候这个构造函数自带了一个prototype属性,而这个属性指向一个 对象,也就是原型对象。 这个原型对象里面有一个constructor构造器,它的作用是指向创建自己的构造函数。除此之外 prototype还可以存放公共的属性和方法。 当我们实例化一个对象的时候(被new调用的时候),这个对象自带了一个 proto 属性,这 个proto 指向创建自己的构造函数的原型对象。可以使用这个原型对象里面的属性和方法

3、普通构造函数constructor与class的区别??

传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。

ES6引入了Class(类)这个概念,通过class关键字可以 定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言
constructor: constructor()方法是类的默认方法,通过new命令生成对象实例 时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的 constructor()方法会被默认添加。

[https://www.jianshu.com/p/86267fab4878

4.跨域出现的原因/解决方法??

原因:由于浏览器的同源策略,即属于不同域的⻚面之间不能相互访问各自的⻚面内容。

哪些情况下产生跨域
1、域名不同,2、 端口号不同,3、协议不同(http/https),4、域名和域名对应ip,5、主域名相同(127.0.01 和 localhost) 多域名匹配一个ip地址 6、子域名不同(一级和二级域名)

解决方法

1、后端代理 - 后端不存在跨域(后端代码脱离浏览器,后端是服务器端)
利用后端(自己公司的后端)去获取接口数据,将数据传给前端
2、jsonp原理** (json with padding) 数据填充的方式
利用浏览器的"漏洞" src不受同源策略的影响,可以请求任何链接 。动态创建script标签,将事先写好的函数名传给服务器,供服务器使用
​ (1)script标签src属性不存在跨域
​ (2)get方式–传递函数名 --弊端
​ (3)callback回调函数(传递函数名)
3、反向代理 proxy webpack配置
“proxy”: {
​ “/index.php”: {
​ “target”: “http://qinqin.net”,
​ “changeOrigin”: true
​ }
}
4.CORS解决跨域(xhr2) nginx
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出 XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
需要服务器(提供接口的源码里面)添加下面两句话。
header(‘Access-Control-Allow-Origin:*’);
header(‘Access-Control-Allow-Method:POST,GET’);
jsonp是一种非正式传输协议,用于解决跨域问 题流程: 1、创建一个全局函数 2、创建一个script标签 3、给script添加src 4、给src添加回调函数test(callback=test) callback是传给后端的一个参数 5、将script放到⻚面上 6、script请求完成,将自己从⻚面上删除

https://www.cnblogs.com/sdcs/p/8484905.html

5.说一下什么叫闭包原理/优点/缺点/使用场景??

闭包原理:定义在一个函数内部的函数(函数嵌套函数),闭包就是将函数内部和函数外部连接起来的一座桥梁。
打破了作用域链的规则 闭包就是能够读取其他函数内部变量的函数

用途**:a:可以读取函数内部的局部变量 b:让这些变量始终保持在内存当中 **

3注意:由于闭包会使得函数中的变量都被保存在内存当中,内存会消耗很大,所以不能够滥用闭包,否则会造成网⻚性能的问题 闭包
**优点:**1、使用闭包是不会污染全局环境,2、方便进行模块化开发,3、减少形参个数,延长了形参的生命周期,

坏处 1、就是不恰当使用会造成内存泄漏

闭包的不适用于返回闭包的 函数是个特别大的函数,很多高级应用都要依靠闭包实现.

使用场景:1、通过循环给页面上多个dom节点绑定事件 。2、封装私有变量(计数器) 3、:延续局部变量的寿命,4、高阶组件 5、函数防抖
模块化的就是以闭包为基础构建的;

/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
   
    let timer = null
    //借助闭包
    return function() {
   
        if(timer){
   
            clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
            timer = setTimeOut(fn,delay) 
        }else{
   
            timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
        }
    }
}

5.1 内存泄漏

1、意外的全局变量 
2、被遗忘的定时器 
3、没有清除的dom应用 ,故要及时清除,
4、滥用闭包
清除内存泄漏方法有两种,一是标记清除,二便是引用计数清除。 

5.2 垃圾回收机制

JS的垃圾回收机制是为了以防内存泄漏:标记清除(mark and sweep)、引用计数(reference counting)。
标记清除:垃圾收集器给内存中的所有变量都加上标记,然后去掉环境中的变量以及被环境中的变量引用的变量的标记。在此之后再被加上的标记的变量即为需要回收的变量,因为环境中的变量已经无法访问到这些变量。
引用计数:这种方式常常会引起内存泄漏,低版本的IE使用这种方式。机制就是跟踪一个值的引用次数,当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加1,当这个变量指向其他一个时该值的引用次数便减一。当该值引用次数为0时就会被回收。

6.promise/generator/async…await??

Promise 是es6新增的,异步编程的一种解决方案,用来取代回调函数和事件,比传统的解决方案——回调函数和事件——更合理和更强大。
​ 有三种状态:pending(进行中)、fulfilled(resolve已成功)和rejected(已失败)。
promise的特点
​ (1)对象的状态不受外界影响。Promise对象代表一个异步操作。
​ (2)一旦状态设定,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为resolve和从pending变为rejected。只要这两种情况发生,状态就凝固了
promise的的方法
promise实例方法:Promise.prototype.then Promise.prototype.catch*
​ 一:resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
​ 二:reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
​ 三:Promise.prototype.finally
​ finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise的 静态方法 Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例,接受一个数组作为参数,只有数组里面的每个状态都变成resolve,则新的 Promise 实例状态才会变成resolve.
Promise.race():将Promise对象数组中最先执行完成的内容通过后面then传出

promise的基本使用

	1、通过new promise创建一个promise对象,里面有一个参数,参数是一个回调函数,回调函数中 有2个参数,resolve,reject resolve()当异步执行成功的时候调用的方法,reject()当异步失败的 时候调用的方法。
	2、除此之外promise有一个then方法,当成功的时候执行第一个回调函数,当失败 的时候执行第二 个回调函数。第二个回调函数也可以通过promise对象.catch调用 
	3、Promise.all():当所有的异步代码都执行完毕以后才会执行.then中的操作 
	4、Promise.race():只要有一个promise执行完毕后就会执行.then操作

https://www.cnblogs.com/moveup/p/9746748.html

promise的三种状态与链式调用: pending - 进行中 fulfilled - 成功 rejected - 失败
https://www.jianshu.com/p/dc61ea153874

async异步能干什么?就是用来修饰函数,使该函数异步执行,不阻碍后续函数的执行 同时我们注意到,async修饰的函数也带有then catch方法,因此,经async修饰的函数也 是一个promise await只能放在async中,且只能修饰promise对象 1. promise的诞生是为了简化函数嵌套调用流程,也便于后续维护 2. async/await定义了异步函数,并在其内部可通过await等待promise对象,阻塞后 续的执行

**await关键字必须在async函数里面 await会阻塞当前直到完成 async返回reject的方法,当抛出异常等同于reject async / await与 Promise的主要区别是 Promise代码完全都是Promise的API(then、catch等等),操作本身的语义反 而不容易看出来, async / await函数的实现最简洁,最符合语义,几乎没有语义不相关的代码 async / await 函数就是 Generator 函数的语法糖

async/await函数的优势

1. 使用async函数可以让代码简洁很多,不需要像Promise一样需要些then,不需要 写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌 套代码 
2. 使用aync/await的话,catch能处理JSON.parse错误 promise中不能处理 
3. 条件语句也和错误捕获是一样的,在 Async 中也可以像平时一般使用条件语句

[promise的状态处理的原理] :https://www.jianshu.com/p/44a971659696

generator:https://www.jianshu.com/p/83da0901166f

14.async await

await后面有个接口 接口要2S才能完成 接口2S才会执行

await是同步还是异步:await同步 async异步

async和await有两个关键字,一个写在函数外面,一个写在函数里面,函数外面是异步的 函数里面是同步的 调用函数的那一行其实是异步的,下一行

函数里面转成阻塞的

async await promise改写

async函数中

let a=await promise 的a函数

let b=await promise 的b函数

promise.all改写 Promise.allSettled Promise.any

promise实现promise.all的方法

async 使用的时候报错,如何捕获try…catch

var test3 = async function () {
    try {
      await p1();
      await p2();
      p3();
    } catch (e) {
      console.log('p1失败了', e)
    }
  }

15.$.ajax中async:false的阻塞和await这种阻塞有什么区别

没区别

7、前端堆/栈|深/浅拷贝及方法

1、**堆**--引用类型地址传递
	堆:动态分配的内存,大小不定,不会自动释放。                                                    

		存放引用类型的值 先进后出FILO
引用类型: Object(Arrary, Date, Math,Function, Object)
	访问方法: JS不能直接访问内存中的值, 只能操作对象的地址, 所以产生深/浅拷贝问题**

 2、**栈**--基本类型值传递 

	自动分配内存空间,系统自动释放,

	存放基本类型的值和引用类型的地址 先进先出FIFO
    基本类型: Undefined、Null、Boolean、Number 和 String, Symbol(ES6新增)
    访问方法: 按值访问, 直接操作内存中的值

对象浅拷贝在react中用到哪些地方,为什么

为什么在react中要使用浅拷贝

redux中要求:状态是只读的,唯一且不可修改的,reducer必须是一个纯函数

-因为redux中数据不可更改,所以redux中的数据应该要拷贝 返回一个新值

深/浅拷贝针对的是 引用类型

浅拷贝–新旧互相影响,改变的是地址
​ 新值===原值, 只能拷贝一层

数组方法 ` slice截取`, `concat拼接`, `filter过滤` , `map`,
对象方法 ` Object.assign({ },obj)`, `Object.create(obj)`
展开运算符`{…obj}` [...arr] 

深拷贝–新旧互不影响,改变的是值
​ 新值=/=原值 , 可以拷贝多层

1、JSON序列化** **JSON.parse( JSON.stringify(obj) )** 对象-->字符串-->对象

这个方式的弊端:
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,不可枚举的不能被复制
可枚举 :可枚举性(enumerable)用来控制所描述的属性,是否将被包括在for...in循环之中。具体来说,如果一个属性的enumerable为false,下面三个操作不会取到该属性。

- for..in循环
- Object.keys方法
- JSON.stringify方法

2、**原生实现**
   递归+浅拷贝
3、 **工具实现**【 第三方封装库 】
   loadsh _.cloneDeep(obj)
   缺点
   4、Immutable
   Object.create()

13.es6新特性

let   let声明的变量有块作用域   不能重复声明变量,

const    常量声明,一旦声明,常量的值不能改

const定义一个对象,可以修改里面的属性,因为对象是引用类型  改变的是引用地址

解构赋值

扩展运算符   ...   

模板字符串

箭头函数     简洁,同时函数体内this对象,就是定义时所在的对象,而不是使用时所在的对象。This不会改变了    一定是匿名函数    不要在最外层定义箭头函数

Symbol类型   独一无二的值

Generators生成器函数 *   函数体内部使用yield表达式将进程分片暂停   next获得结果

class   extends   类和继承

promise 异步编程的一种解决方法  三个状态pending-resolve-reject

                状态从pending-resolve  成功返回一个promise对象,通过then传递

                状态从pending-reject   失败走reject   

promise.all 并发    promise.race速发

12,set和map

不说set 数据结构Set new set()存储数据 set.size得到存储的数据长度

has()判断某个值是否存在set中 foreach遍历set

不说map : new map map.set map.get map.delete

都是用来存储数据用的,但是存储的数据格式不同
set 直接存储 任意类型数据
map 存储数据的时候,必须以key,value的形式,
set 使用forEach 遍历的时候,key和value值是一样的
而map 遍历的时候,key就是存进去的对象的key,value就是存在的值

for循环这种写法比较麻烦,因此数组提供内置的forEach方法。

forEach没有返回值,无法中途跳出forEach循环,break命令或return命令都不能奏效。

for...in循环主要是为遍历对象而设计的,不适用于遍历数组**

for...of循环相比上面几种做法,有一些显著的优点。
有着同for...in一样的简洁语法,但是没有for...in那些缺点。
不同于forEach方法,它可以与break、continue和return配合使用。
提供了遍历所有数据结构的统一操作接口。

6.宏任务和微任务

同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。

当指定的事情完成时,Event Table会将这个函数移入Event Queue。

主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。

上述过程会不断重复,也就是常说的Event Loop(事件循环)。

先执行同步代码 微任务放在微任务队列中 宏任务放在宏任务队列中

宏任务和微任务是任务队列是两个任务队列中

同步执行完,执行微任务,再执行宏任务

宏任务 setTimout setInterval

微任务 promise process.nextTick

7.异步操作

回调函数 异步回调 函数作为参数传递给另外有一个函数 将函数内部的值通过回调函数传出来 解决同步的一种方式 多层嵌套 造成回调地狱
事件监听

发布/订阅 系统中有一个信号中心,当某个任务执行完成后向信号中心发布一个消息,其他任务就可以通过这个信号中心去订阅这个信号,从而知道什么时候直接可以开始执行 发布订阅模式和事件监听类似,可以查看消息中心有多少信号,每个信号有多少订阅者

promise
generator(ES6) 异步任务 通过yield关键字可以让任务在需要的地方暂停,每一步的值可以通过next获取

async/await(ES7) await得到的就是async异步返回值,底层原理还是promise中的resolve方法

10.本地存储— 三种存储

首先总的来说,三者都是用于持久化数据存储的手段,都是存储在浏览器端,且同源.

localStorage和sessionStorage都是Web存储,大小5M左右,完全存储在客户端,它们是因为本地存储数据而存在.

cookies也是存储在浏览器端的,大小不超过4k,由服务器端存储在客户端。

localStorage属于永久性存储,数据存储量大,,而sessionStorage属于当会话结束的时候,存储的值会被清空,而cookie是通过设置过期时间expires来存储的。

10.表单只能输入数字

正则 type number

10.es2016-es2020

https://zhuanlan.zhihu.com/p/133658121

你可能感兴趣的:(面试)