js面试题

原型

首先js作者创建原型的主要原因就是为了可以让多个对象可以共享数据。

  • 每一个函数都有一个prototype属性,即显示原型

  • 每一个(函数实例)对象都会有一个proto属性,即隐式原型

    对象的隐式原型的值,就是其对应的构造函数的显示原型

既然说到的原型,那就顺便也讲一下原型链:

原型链又称隐式原型链:当我们访问一个对象的属性时,首先会在当前对象上查找,如果没有就会沿着该对象的proto上去查找,找到了就返回,找不到,就继续沿着原型的原型的去找,一直找到原型链的顶端,也就是Object.prototype,如果还没找到就返回undefined

闭包

简单的来讲就是打通了一条在函数外部访问函数内部作用域的通道。(正常的来讲,在函数外部是访问不到函数内部作用域变量的。)

闭包通常发生在函数嵌套中,且内层函数使用了外层函数的变量,而且最后内层函数被返回到了外部

【优点:】

在函数外部就可以操作函数内部的变量

延长了局部变量的声明周期

【缺点:】

可能会造成内存泄露

【解决办法:】

将暴露在外部的闭包置为null

作用域

作用域:即变量和函数的可访问范围

  • 静态作用域(词法作用域):在词法分析阶段就确定了,取决于源码
  • 动态作用域:根据程序的运行来确定的

js中采用的是静态作用域。

【静态作用域】:

  • 全局作用域
  • 函数作用域
  • 块级作用域(let、const)

因为有了作用域,进而也就有了后面作用域链:

当多个作用域发生嵌套时,在查找某个属性时,如果在当前的作用域找不到,就去它的外层作用域去找,找到了就返回,找不到就继续去外层作用域的作用域去找,一直找到最外层作用域为止。这种查找变量的方式就是通过作用域链来查找的。

普通函数和箭头函数

  • 箭头函数是匿名函数,不能使用new关键字构造调用

  • 箭头函数没有arguments。

    我们可以使用rest参数:...变量名的形式来获取箭头函数的参数

  • 箭头函数没有自己的this,箭头函数中的this是在它创建的时候就确定了,就是箭头函数所在上下文的this

  • 任何方法都改变不了其指向,如 call() , bind() , apply()

  • 箭头函数没有原型属性。

    var a = ()=>{
      return 1;
    }
    function b(){
      return 2;
    }
    console.log(a.prototype);  // undefined
    console.log(b.prototype);   // {constructor: ƒ}
    

promise

https://blog.csdn.net/qq_43952245/article/details/104168074?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161792948016780262572620%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=161792948016780262572620&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_v29-7-104168074.pc_v2_rank_blog_default&utm_term=promise&spm=1018.2226.3001.4450

【异步解决方案:】回调函数、promise(重点)、generator、async和await(重点)

【定义:】

promise是JS中进行异步编程的新的解决方案(旧的方案指得是纯回调的形式,即纯指得是没有promise都是回调函数)(1)、从语法来讲promise是一个构造函数
(2)、从功能上来说promise对象可以封装一个异步操作,并可以获得其结果

【用法:】

Promise构造函数接收一个函数作为参数(执行器函数),该函数的两个参数分别是resolve和reject分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。(它们是两个函数,又JavaScript引擎提供,不是自己部署)
resolve函数的作用,将Promise对象的状态从“未完成”变成“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

【promise缺点:】

  1. 一旦执行,无法中途取消

一旦新建Promise对象,便开始执行,无法中途取消

  1. promise的内部如何执行,检测起来很难

如果当前是处于pending状态,无法得知目前异步任务进展到哪一个阶段,是刚开始执行,还是即将结束

  1. promise的错误无法在外部被捕捉到,只能在内部进行预判断处理

如果不设置回调函数,Promise内部抛出的错误,不会反映到外部

async和await

1、async函数:

用在函数定义的前面,函数的返回值为promise对象(promise对象的结果由函数的返回值决定)

2、await表达式

await右侧的表达式一般为promise对象,但也可以是其他的值

  • 如果表达式是promise对象,则await返回的是promise成功的值
  • 如果表达式是其他的值,直接将此值作为await的返回值

注意:

  • await必须写在async函数中,但是async函数中可以没有await
  • 如果awaitpromise失败了,就会抛出异常,需要try{}catch(){}来进行异常处理

原生dom操作方法

【增】

appendChild
insertBefore

【删】

removeChild

【查】

getElementByid
getElementsByTagName,
querySelector,
querySelectorAll

【设置和获取属性】

setAttribute(“属性名”,”值”)
getAttibute(“属性名”)

var、let、const

var:

  • 存在变零提升,可以在声明之前使用
  • 可以重复声明同一个变量

let、const

  • 不存在变量提升,必须先声明在使用
  • 不可以重复声明
  • 存在块级作用域

const必须要有初始值,且值不可以更改

new操作符

  • 创建一个空对象obj: var obj= {}

  • 设置原型链:obj.__proto__= Func.prototype;

  • Func中的this指向obj,并执行Func的函数体:var result =Func.call(obj);

  • 判断Func的返回值类型:

    if (typeof(result) == "object"){
      func=result;
    }
    else{
        func=obj;;
    }
    

数组的合并

var a = [1,2,3];
var b = [4,5,6];

【方法一】:concat

var c = a.concat(b); //c=[1,2,3,4,5,6];

concat方法连接a、b两个数组后,a、b两个数组的数据不变,同时会返回一个新的数组

【方法二】:for循环

for循环:遍历其中一个数组,把该数组中的所有元素依次添加到另外一个数组中。

for( var i in b)
{
  a.push(b[i]);
}

【方法三】:apply

函数的apply方法有一个特性,那就是func.apply(obj,argv),argv是一个数组。所以我们可以利用这点,直上代码:
a.push.apply(a,b);

调用a.push这个函数实例的apply方法,同时把,b当作参数传入,这样a.push这个方法就会遍历b数组的所有元素,达到合并的效果。

这里可能有点绕,我们可以把b看成[4,5,6],变成这样:

a.push.apply(a,[4,5,6]);

然后上面的操作就等同于:

a.push(4,5,6);

以上3种合并方法并没有考虑过a、b两个数组谁的长度更小。所以好的做法是预先判断a、b两个数组哪个更大,然后使用大数组合并小数组,这样就减少了数组元素操作的次数

数组去重

https://www.cnblogs.com/echolun/p/12207885.html

  • new Set

  • 利用Indexof

    新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。

  • 利用filter

    function unique(arr) {
      return arr.filter(function(item, index, arr) {
        //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
        return arr.indexOf(item, 0) === index;
      });
    }
        var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
            console.log(unique(arr))
    //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]
    

cookie、localStorage、sessionStorage

【cookie是什么?cookie和localStorage的区别在哪?】

call、apply、bind

fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)

1.call、apply与bind都用于改变this绑定,但call、apply在改变this指向的同时还会执行函数,而bind在改变this后是返回一个全新的boundFcuntion绑定函数

2.bind属于硬绑定,返回的 boundFunction 的 this 指向无法再次通过bind、apply或 call 修改;call与apply的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。

3.call与apply功能完全相同,唯一不同的是call方法传递函数调用形参是以散列形式,而apply方法的形参是一个数组。在传参的情况下,call的性能要高于apply,因为apply在执行时还要多一步解析数组。

【手写】

call + apply: https://www.cnblogs.com/echolun/p/12144344.html

bind: https://www.cnblogs.com/echolun/p/12178655.html

如何解决跨域

【同源策略】:是一种安全机制。当前网页的url和发起请求的目标资源的url,他们的协议、域名、端口号完全一致才是同源

同源: 协议、域名、端口号 必须完全相同。

要知道的:跨域是浏览器做出的限制,和后端没关系

  1. JSONP

【jsonp实现原理】:

在网页有一些标签天生具有跨域能力,比如:img、link、iframe、script。 JSONP 就是利用 script 标签的跨域能力来发送请求的。

主要是利用动态创建script标签请求后端接口地址,然后传递callback参数,后端接收callback,后端经过数据处理,返回callback函数调用的形式,而函数调用的参数就是我们返回给前台的数据(注意:此数据最后是要转为字符串形式传递)

jsonp只能发get请求,因为加了一个script标签,相当就是请求一个脚本文件

前端传递的参数callback: 就专门用来处理服务器返回的数据的

  1. 配置代理

分为:前端代理和后端代理

【前端代理】:我在vue中主要是通过vue脚手架中的config中的index文件来配置的,其中有个proxyTable来配置跨域的

  1. CORS

CORS全称叫跨域资源共享,主要是后台工程师设置后端代码来达到前端跨域请求的

现在主流框架都是用代理和CORS跨域实现的

【后台怎么工作的:】

通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。

// 哪些网页可以给服务发送请求
res.setHeader('Access-Control-Allow-Origin', '*')
// 请求所允许使用的方法。(默认是get、post)
res.setHeader('Access-Control-Allow-Methods', '*')
// 设置请求中所携带的头信息(允许客户端在发送请求时可以携带任意的请求头,比如:自定义的请求头信息)
res.setHeader('Access-Control-Allow-Headers', '*')

确定this

  • 不带任何修饰函数引用进行调用(默认绑定)

    严格模式下:函数中的this = undefined

    非严格模式下:函数中的this = window

  • 函数是作为对象的属性调用时,则函数中的this就是该函数(隐式绑定)

  • 函数是通过call或apply调用,则this就是call、apply所制定的对象(显示绑定)

  • 函数是通过new的形式调用,则this就是new出来的那个实例对象(new绑定)

new > 显示绑定 > 隐式绑定 > 默认绑定

深拷贝与浅拷贝

https://blog.csdn.net/jiang7701037/article/details/98738487

深拷贝和浅拷贝主要是针对对象的属性是对象(引用类型)

基本类型赋值时,赋的是数据(所以,不存在深拷贝和浅拷贝的问题)。

对象浅拷贝:可以理解为改变一个对象属性值,另一个对象属性也会发生改变,即互相影响

对象深拷贝:即就是说改变一个对象属性,另一个对象属性值不会发生改变,可以通过多种方法来实现对象深拷贝

【第一种方法】:通过JSON.stringify和JSON.parse来实现

  var obj={name:'16ac'}
  var obj2=JSON.parse(JSON.stringify(obj))

【第二种方法】:通过递归来实现

【第三种方法】:Object.assign()

当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。

http状态码

  • 1xx:临时响应并需要请求者继续执行操作的状态码

  • 2xx:表示成功处理了请求

    200:服务器成功返回了网页

  • 3xx:重定向

    301:永久重定向。服务器返回此响应时,会自动的将请求者转到新位置

    302:暂时重定向。服务器目前从不同的位置响应请求,但请求者应继续使用原来的位置进行以后的请求

    304:未修改。自从上次请求后,请求的网页未修改过。服务器返回此响应时不会返回任何网页内容

  • 4xx:请求出错

    404:请求的地址不存在

  • 5xx:服务器错误

    500:服务器内部错误

    503:服务不可用。服务器目前无法使用

浏览器的缓存有哪些

【一:cookie】:

  • Cookie(或者Cookies),指一般网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)

  • cookie一般通过http请求中在头部一起发送到服务器端。

  • 一条cookie记录主要由键、值、域、过期时间、大小组成,一般用于保存用户的认证信息。cookie最大长度和域名个数由不同浏览器决定

  • 浏览器端可以通过document.cookie来获取cookie,并通过js浏览器端也可以方便地读取/设置cookie的值。

【二:localstorage】:

  • localStorage是html5的一种新的本地缓存方案,目前用的比较多,一般用来存储ajax返回的数据,加快下次页面打开时的渲染速度

  • localstorage大小有限制,不适合存放过多的数据,如果数据存放超过最大限制会报错,并移除最先保存的数据。

【三:sessionStorage】:

sessionStorage和localstorage类似,但是浏览器关闭则会全部删除,api和localstorage相同,实际项目中使用较少

【四:http缓存】:

http缓存是基于HTTP协议的浏览器文件级缓存机制。

即针对文件的重复请求情况下,浏览器可以根据协议头判断从服务器端请求文件还是从本地读取文件

特性CookielocalStoragesessionStorage
数据的生命期一般有服务器生成,可设置失效时间;如果在浏览器端生成,默认关闭浏览器后失效除非被请求,否则永久保存仅在当前会话下有效,关闭页面或浏览器后别清除
存放数据大小4K左右一般为5MB一般为5MB
与服务器端通信每次都会携带在http头,如果使用cookie保存过多数据会带来性能问题尽在客户端保存,不参与和服务器的通信同localstorage
易用性源生的Cookie接口不友好,需要程序员自己封装源生接口可以接受,也可再次封装对Object和Array有更好的支持同localStorage

es6常用的

ES6新增特性常用的主要有:

let/const,箭头函数,模板字符串,解构赋值

模块的导入(import)和导出(export default/export),

Promise,

还有一些数组字符串的新方法,其实有很多,我平时常用的就这些

js设计模式

JS设计模式有很多,但我知道的有单例模式,观察者模式

单例模式:就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。

观察者模式:观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

JS面试对象的理解

将所需要做的功能抽象成一个“对象”,然后反复调用这个对象来完成你想要的功能

js数组常用的方法

至少6个

push、pop、unshift、shift、splice

join、concat

forEach、map、filter、sort

js数组内置遍历方法

【forEach】:这个方法主要是为了取代for循环的。返回值是undefined

【filter】:是一个过滤遍历的方法。如果返回值为true,则返回满足条件为true的新数组

【map】:主要是对数组中需要处理复杂逻辑时用的。

【some】:只要数组中存在一个满足条件的返回值就是true,否则就是false

【every】:数组中每一项都满足条件才返回true,否则就返回false

输入url到页面加载过程

  1. DNS解析

  2. TCP连接

  3. 发送HTTP请求

  4. 服务器处理请求并返回需要的数据

  5. 浏览器解析渲染页面

  6. 连接结束

输入了一个域名,域名要通过DNS解析找到这个域名对应的服务器地址(ip),通过TCP请求链接服务,通过WEB服务器(apache)返回数据,浏览器根据返回数据构建DOM树,通过css渲染引擎及js解析引擎将页面渲染出来,关闭tcp连接

JS事件代理

事件代理,也称事件委托

JS事件代理就是通过给父级元素(例如:ul)绑定事件,不给子级元素(例如:li)绑定事件,然后当点击子级元素时,通过事件冒泡机制在其绑定的父元素上触发事件处理函数

主要目的是为了提升性能,因为我不用给每个子级元素绑定事件,只给父级元素绑定一次就好了

【实现原理】:在原生js里面是通过event对象的targe属性实现

var ul = document.querySelector("ul");
	ul.onclick = function(e){//e指event,事件对象
	var target = e.target || e.srcElement; //target获取触发事件的目标(li)
		if(target.nodeName.toLowerCase() == 'li'){//目标(li)节点名转小写字母,不转的话是大写字母
			alert(target.innerHTML)
		}
}

重绘和回流

https://www.cnblogs.com/echolun/p/10105223.html

【要知道的:】

一个页面从加载到完成,首先是构建DOM树,然后根据DOM节点的几何属性形成render树(渲染树),当渲染树构建完成,页面就根据DOM树开始布局了,渲染树也根据设置的样式对应的渲染这些节点。

在这个过程中,回流与DOM树,渲染树有关,重绘与渲染树有关

【回流】:

比如我们增删DOM节点,修改一个元素的宽高,页面布局发生变化,DOM树结构发生变化,那么肯定要重新构建DOM树,而DOM树与渲染树是紧密相连的,DOM树构建完,渲染树也会随之对页面进行再次渲染,这个过程就叫回流

【重绘】:

当你给一个元素更换颜色,这样的行为是不会影响页面布局的,DOM树不会变化,但颜色变了,渲染树得重新渲染页面,这就是重绘

*回流的代价要远大于重绘。且回流必然会造成重绘,但重绘不一定会造成回流。*

【重绘和回流的区别】:

浏览器常见兼容问题

浏览器的兼容性无非还是样式兼容性(css),交互兼容性(javascript),浏览器 hack 三个方面。

https://www.jb51.net/css/713911.html

https://blog.csdn.net/weixin_38536027/article/details/79375411?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

CSSHack

解决浏览器兼容性问题的主要方法是CSS hack。由于不同的浏览器,比如Internet Explorer 6,Internet Explorer 7,Mozilla Firefox等,对CSS的解析认识不一样,因此会导致生成的页面效果不一样,得不到我们所需要的页面效果。这个时候我们就需要针对不同的浏览器去写不同的CSS,让它能够同时兼容不同的浏览器,能在不同的浏览器中也能得到我们想要的页面效果。这个针对不同的浏览器写不同的CSS code的过程,就叫CSS hack,也叫写CSS hack。

CSS Hack的原理是什么

由于不同的浏览器对CSS的支持及解析结果不一样,还由于CSS中的优先级的关系。我们就可以根据这个来针对不同的浏览器来写不同的CSS。比如 IE6能识别下划线"“和星号” * “,IE7能识别星号” * “,但不能识别下划线”",而firefox两个都不能认识。等等

IE6: 下划线和星号

IE7:下划线

FF:都不识别

ajax

后端进行数据交互

我和后端通过ajax来进行数据交互的,通过统一制定的接口文档,来实现前后端高效开发,如果接口文档不能详细说明,或者接口文档上的参数请求不出数据,我会主动和后端工程师沟通,直到完成跟接口相关的业务开发。当然这其中为了验证一些接口问题,会用到一些辅助工具,比方说,runapi这种在线测试工具

【如果后端数据接口没有准备好,你是如何工作的】

如果后端接口还没有准备好,我会和后端工程师沟通,通过制定接口返回数据的格式,然后前端通过一些mock数据的工具(上家公司使用的是easymock,贼简单)来批量生成假数据,可以让前端和后端同时开发,而无需等待后端数据接口写好再开发

【原生ajax的交互过程(即流程)】

  • 先创建XHR对象

  • open准备发送

    open中有三个参数一是提交方式get和post,二是接口地址,三是同步和异步

  • send发送请求

  • onreadystatechange来监听接收的回调函数

    readyState==4和status==200 成功,可以通过responseText接收返回的数据

【ajax缓存如何解决】

通过在文件名后面添加随机数(也称为文件指纹)来实现

主要原理是浏览器对访问过的文件,首先会检测第二次请求的文件url在浏览器是否缓存过,如果缓存过就使用,否则如果是一个新的文件url,则从服务器重新请求

三次握手

https://blog.csdn.net/qq_43952245/article/details/109304020?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161792497916780357295561%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=161792497916780357295561&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_v29-3-109304020.pc_v2_rank_blog_default&utm_term=j%E9%87%8D%E7%BB%98&spm=1018.2226.3001.4450

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值