面试之js

1.js的组成

js包括三部分ECMAscript、DOM、BOM

ECMAscript描述了js的语法和基本对象(内置对象)

BOM(浏览器对象模型)是与浏览器交互的方法和接口(常用的对象有window、location、history)

DOM(文档对象)是处理网页内容的方法和接口,javascript对网页进行的所有操作都是通过DOM进行的

2.js的对象

js对象分为三类,内置对象(内部对象、本地对象)、宿主对象、自定义对象。

内置对象有 (ECMAScript提供的需要实例化(new)才能使用的对象:)Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、(ECMAScript提供的不需要实例化就能使用的对象:)Global (全局对象)和 Math。

宿主对象 浏览器提供的对象。所有的BOM和DOM都是宿主对象。

自定义对象  开发人员自己定义的对象

3.js的数据类型

js基本对象有 Number、Boolean、String、Null、Undefined、Symbol、BigInt

Symbol定义一个唯一的值,有重名问题时,定义的后面的数会覆盖前面的数,只输出一个值。用Symbol定义就算重名也不会被覆盖。

BigInt是一种数字类型的数据,他可以表示任意精度格式的整数,使用BigInt可以安全的存储和操作大整数,即使这个数已经超出了Number(-2^53, 2^53)能够表示的安全整数范围。

js引用数据类型有Object、Array、Function、Date、RegExp

基本类型和引用类型的区别

①基本类型的值是不可变的;引用类型的值是可变的

②基本类型的比较是值的比较;引用类型的比较是引用的比较

③基本类型的值是存放在栈内存中的;引用类型的值是同时放在栈内存和堆内存中的,栈内存种保存的是变量标识符和指向堆内存的指针,堆内存中存放的是变量的值

补充:栈是先进后出,栈内存由编译器自动释放。堆是一个优先队列(队列是先进先出),堆内存一般由开发者释放,开发者不释放的话,程序结束后可能由垃圾回收机制回收。

4.怎么判断不同的js数据类型

1.typeof

语法:typeof(xxx)返回一个字符串

typeof只能判断Nmeber、String、Boolean、Undefined、Function的值

Null及其他引用类型都为Object

2.instanceof

语法:A instanceof B  返回一个布尔值

他只能判断A是否是B的一个实例,而不能判断A具体是哪一个类型

3.constructor

语法:a.constructor

当一个函数被定义时,js引擎会为其添加一个prototype原型,然后在prototype上添加一个constructor属性,并让其指向该函数的引用。null和undefined是无效的对象,不会有constructor存在。函数的constructor是不稳定的,当开发者重写prototype后,原有的constructor引用会丢失,默认值为object。

4.object.prototype.toString.call()

为什么需要call?

由于Object.prototype.toString()本身允许被修改,像Array、Boolean、Number的toString就被重写过,所以需要调用Object.prototype.toString.call(arg)来判断arg的类型,call将arg的上下文指向Object,所以arg执行了Object的toString方法。

5.undefined和null的区别

null:表示没有对象,即此处不应该有值

用法:

①主动释放一个变量引用的对象,表示一个变量不在指向任何对象的地址(闭包中释放内存)

②作为函数的参数,表示该函数的参数不是对象

③作为原型链的终点

undefined:表示缺少值,即此处应该有一个值,但是没有被定义

用法:

①函数没有返回值

②函数应该传入的参数没有传入

③变量被声明了,但是没有赋值

④对象没有赋值的属性

6.数组对象有哪些常用的方法

(1)修改器方法:

pop()          在数组尾部删除一个元素,返回被删除的元素

push()         在数组末尾插入一个元素,返回数组的新长度

shift()           在数组头部删除一个元素,返回被删除的元素

unshift()       在数组头部插入一个元素,返回新的数组长度

sort()            给数组元素进行排序,并返回当前数组   sort(a,b)  不传入参数以字典顺序为标准(一般来说都是ascll编码的排序)  a-b升序 b-a降序

reverse()      颠倒数组中元素,返回一个新的反向数组

splice()          在任意位置给数组删除或增加任意个元素,返回的是删除后的数组元素   删除或增加功能由第二个参数控制,第二个参数不为0删除或者替换,为0增加

修改器方法都会改变原数组

(2)访问方法

concat()              返回一个有当前数组和其他若干个数组或非数组的值组成的新数组

slice()                  抽取当前数组中的一段元素组成一个新数组       slice()没有参数就是对数组的浅拷贝;slice(2)表示从索引为2开始截取直到最后一个元素;slice(2,3)从索引为2开始索引为3结束;slice(-2)从倒数第二个元素开始截到最后

indexOf()              返回数组中第一个与指定值相等的元素的索引,如果找不到则返回-1

lastIndexOf()         从数组的最后一个元素进行查找

join()                     所有的数组元素转化成一个字符串

访问方法不会改变原数组

(3)迭代方法

forEach()          为数组的每个元素都执行一次回调函数,总是返回undefined,无法用break打断,一旦开始无法停止,会不会对原数组发生改变取决于传入的参数类型,基本类型不会,引用类型会

map()                返回一个由回调函数的返回值组成的新数组,可以return出来,不会对原数组产生变化

filter()                将所有在过滤函数中返回true的数组元素放进一个新数组并返回

every()              如果数组中的每个元素都满足测试函数则返回true,否则返回false

some()              如果数组中的有一个元素满足测试函数则返回true,否则返回false

reduce()            接受一个函数作为累加器  有4个参数,prev:函数传进来的初始值或上一次回调的返回值;current:数组中当前处理的元素值;currentIndex:当前元素索引;arr:当前元素所属的数组本身。如果第一个元素没有初始值,他表示前一个元素;有初始值则为初始值。

除了forEach都不会改变原数组

7.js创建对象的方式

①对象字面量

var obj={}

②Object构造函数

var obj=new Object()

③工厂模式

function person(age,name){

var obj=new Object()

obj.name=name

obj.age=age

obj.say=function(){

conlose.log('唱歌')

}

return obj

}

优点:抽象了创建具体对象的过程

缺点:无法解决对象的识别问题

④构造函数模式

function person(age,name){

this.name=name;

this.age=age;

this.say=function(){

conlose.log('唱歌')

}

}

var person=new Person(‘hello’,18)

优点:解决了对象的识别问题

缺点:同样的方法在每个实例上都要重新创建,造成内存空间的浪费

⑤原型模式

function person(){

preson.prototype.name='lilei'

person.prototype.say=function(){

conlose.log('唱歌')

}

}

preson.prototype.friend='hanmeimei'

优点:解决了方法重复创建的问题

缺点:实现了属性与方法的共享,可以动态的添加对象的实例或方法,但是不能创建实例自己的属性和方法,不能向构造函数传参

⑥构造函数和原型结合

function person(age,name){

this.name=name;

this.age=age;

}

person.prototype.say=function(){

conlose.log('唱歌')

}

var person=new Person(‘hello’,18)

优点:可以向构造函数传参,也不用重复创建函数

缺点:构造函数和原型分离,封装性不够

8.对象的深浅拷贝

(1)浅拷贝

浅拷贝就是对对象进行浅层次的复制,只复制一层对象的属性,并不包括对象里面的引用类型数据


其他方法:

①object.assign(target,source)

②Array.prototype.slice()

③扩展操作符...

(2)深拷贝

对对象深层次的复制


其他方法:

JSON.parse(JSON.stringify(source))

(3)深浅拷贝的区别

浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化;深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变。

9.js实现继承的方式

(1)原型链继承

优点:简单、易实现

缺点:对象实例共享所有继承的属性和方法,创建子类实例时无法传参。

(2)构造函数继承

优点:解决了原型链继承不能传参的问题和父类属性共享的问题

缺点:无法实现函数的复用,只能继承父类的属性和方法不能继承原型链上的属性和方法

(3)组合继承

优点:解决了原型链继承和构造函数继承的影响

缺点:会调用两次父类构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部

(4)寄生式继承

优点:写法简单,不需要单独创建构造函数

缺点:通过寄生式继承给对象添加函数会导致函数难以重用。

(5)寄生组合式继承

优点:高效率只调用一次父构造函数,并且因此避免了在子原型上面创建不必要,多余的属性。与此同时,原型链还能保持不变

缺点:代码复杂

(6)es6的class继承

原理ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

10.作用域和作用域链

作用域:作用域是在代码执行的过程中代码中某些特定部分的变量、函数和对象的可访问性。作用域决定了代码区块中变量和其他资源的可见性。

作用域链:当我们在某个函数内部作用域中查找某个变量时,如果没有找到,就去他的父级作用域中查找,如果父级作用域中也没有找到,那么就会接着一层一层向上查找,直到找到全局作用域,没有找到就宣布失败。这种一层一层的作用域嵌套关系就是作用域链。

意义:变量隔离。即不同的作用域下同名变量不会有冲突。

11.bind、call、apply

bind、call、apply都是用来改变this指向的

call和apply的区别是call传入的是字符串序列,而apply传入的是数组

bind和它们的区别是它的返回值是一个函数,只有在调用它的时候才会执行

(1)call的其他用法

①判断数据类型 object.prototype.toString.call()

②类数组转数组 Array.prototype.slice.call()

(2)apply的其他用法

求数组的最大或者最小值 Math.max/min.apply(null,args)

(3)bind的其他用法

①偏函数的实现

使用bind()让一个函数拥有初始化参数,调用的时候传入其他参数即可

②在定时器中使用

比如和setTimeout一起使用,一般情况下setTimeout()的this指向window或global对象。当使用类的方法时需要this指向类实例,就可以使用bind()将this绑定到回调函数来管理实例。

12.数组去重

①双层for循环


②indexOf去重


③sort去重


④set去重


13.对象数组去重

①reduce方法


②filter方法


14.给定数组取最大值

①sort方法


②Math.max函数

Math.max(...arr)

③reduce函数


15.函数的防抖和节流

(1)防抖

事件被触发n秒后再执行回调函数,如果n秒内又被触发,则重新计时。应用场景:文本框输入


(2)节流

在规定时间内事件响应函数只能被触发一次,如果多次触发函数,只有一次生效。应用场景:点击按钮提交事件


16.数组拍平

(1)flat


(2)while循环


(3)先转化成字符串,在转化成数组


17.new操作符具体干了什么

①创建一个空对象

②将构造函数的原型赋值给新对象(也就是将对象的_proto_属性指向构造函数的prototype属性)

③执行构造函数,将构造函数中的this指向该对象

④返回一个新对象

18.图片懒加载

当图片出现在可视区域时,获取图片真实的地址并赋值给图片即可

window.innerHeight 浏览器可视区域的高度

document.body.scrollTop 浏览器滚动过的距离

offsetTop元素顶部的距离到文档顶部的高度


19.宏任务和微任务

常见的宏任务:setTimeout、setInterval、ajax请求、script整体代码、ui渲染

常见的微任务:Promise Promise.then/.catch/.finally Async/await node.js里的process.nextTick(下次循环时在调用传入的回调函数) Object.observe(监听一个对象的变化,当对象变化时立即调用回调函数来告知我们) MutationObserve(监听dom树的变化)

宏任务由事件触发线程执行、微任务由js引擎线程维护

20.js事件循环机制

js是一门单线程语言,为了协调事件、用户交互、脚本、ui渲染、和网络处理等行为,用户引擎必须使用evenloop(事件循环)。所有的任务可以分为同步任务和异步任务。同步任务就是立即执行的任务,在js主线程中执行。异步任务会通过任务队列的机制来进行协调。任务队列又分为宏任务和微任务。在执行栈清空后先执行所有可执行的微任务,当所有可执行的微任务执行完毕后,再去执行宏任务,把当前宏任务的微任务执行完毕后才会去执行下一个宏任务。

21.进程和线程

进程是系统分配的内存,是独立的一块内存,各个进程间相互独立,线程是进程的一部分,一个进程由多个线程组成,多个线程在进程中协作完成任务,同一进程下的各个线程之间共享程序空间。

22.发布订阅模式

发布订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到状态改变的通知。订阅者把自己想订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者注册到调度中心的处理代码

23.事件捕获和事件冒泡

事件冒泡:事件会从最内层的元素开始发生,一直向上传播,直到document对象。

事件捕获:与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

element.addEventListener(event, function, useCapture)

第一个参数是需要绑定的事件,第二个参数是触发事件后要执行的函数。而第三个参数默认值是false,表示在事件冒泡的阶段调用事件处理函数,如果参数为true,则表示在事件捕获阶段调用处理函数。

如果一个dom节点即注册了事件捕获,又注册了事件冒泡,会先执行捕获阶段后执行冒泡。

用event.stopPropagation阻止冒泡和捕获的进一步传播

用event.preventDefault阻止默认行为

24.字符串和数组的相互变化

字符串转数组:①arr.split(',')   ②Array.form(str) 前提是有length属性

数组转字符串:①toString()     ②arr.join(',')

25.object.is()与比较操作符'==='、'=='的区别

使用‘==’进行判断时,如果两边的类型不一致,会进行强制类型转化然后再进行比较

使用‘===’进行判断时,类型不一致不会强制转化

使用object.is()进行判断时,一般和三等号判断结果相同,他处理了一些特殊情况,比如-0和+0不再相等,两个NAN是相等的

26.什么是js的包装类型

在javascript中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时javascript会在后台隐式的将基本类型的值转化为对象

可以通过object()显式地将基本类型的值转化为包装对象 使用valueof()方法将包装类型转化为基本对象

27.判断对象是否是空对象

①json.stringify(obj)=={}

②object.keys(obj).length==0

28.箭头函数和普通函数的区别

①外形不同:箭头函数以箭头定义,普通函数不用

②箭头函数都是匿名函数,普通函数既有匿名函数又有具名函数

③箭头函数不能用作构造函数,不能使用new,普通函数可以用构造函数来创建对象实例

④箭头函数本身不创建this,在声明时捕获上下文的this供自己使用;普通函数的this总指向调用它的对象,如果用做构造函数则指向新创建的对象实例

29.对json的理解

json是一种基于文本的轻量级的数据交换方式,它可以被任何的编程语言读取和作为数据格式传递。在项目开发中,使用json来作为前后端数据交换的方式。在前端通过将一个符合json格式的数据结构序列转化为json字符串,然后将它传递给后端,后端通过json格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的传递

在js中提供了两个函数来实现js数据和json格式数据的转化

json.parse()         json格式的字符串转换为js数据结构

json.stringify         js数据结构转换为json格式的字符串

30.对ajax的理解

ajax指的是通过javascript的异步通信,从服务器获取xml文档从中提取数据,再更新当前网页的对应部分,而不用刷新服务器。


31.commonJS、AMD、CMD

commonJS、AMD、CMD是js模块化开发的标准

commonJS主要针对于服务器端,服务器端一般采用同步加载文件,也就是需要某个模块,服务器就停下来,等到模块加载完后再执行

AMD、CMD是针对于浏览器端,浏览器端要保证效率,所以采用异步加载文件,这就需要一个预处理,提前将所需要的模块文件并行加载好

AMD和CMD的区别:虽然都是并行加载js文件,但AMD是预加载,在并行加载js文件的同时还会解析执行该模块;CMD是懒加载,虽然会一开始就并行加载js文件,但是不会执行而是在需要的时候执行

ES6 module与commonjs的区别:①commonjs是对模块的浅拷贝,es6 module是对模块的引用,即es6 module只存只读,不能改变其值,也就是指针指向不能变。②commonjs模块是运行时加载而es6 module模块是编译时输出接口。③commonjs模块的require()是同步加载模块,而es6模块的import命令是异步加载。

32.ajax和axios的区别

①axios是一个基于promise的http库,而ajax是对原生xhr的封装

②ajax技术实现了局部数据的刷新,而axios实现了对ajax的封装

33.axios的传参

1.get/delete 


2.post/put


34.原型链的终点

Object.prototype._proto_===null

35.事件委托

事件委托指的是不在事情的发生地上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素dom的类型来做出不同的回应。

好处:

①减少内存消耗

一个列表有大量列表项,我们需要在点击列表项的时候响应一个事件,那么把这个事件绑定到父级,可以减少大量内存消耗,提高效率

②动态绑定事件

新添加的子元素也会有监听函数,也可以有事件触发机制

36.原型和原型链

1.原型

所有的引用类型都有一个_proto_属性,属性值是一个普通对象。

所有的构造函数都有一个prototype属性,属性值是一个普通对象。

所有引用类型的的_proto_属性都指向其构造函数的prototype属性,这就是一个原型对象

2.原型链

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有则会去找他的_proto_隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的_proto_中查找,这样一层一层向上查找就会形成一个链式结构,我们称之为原型链。

原型和原型链可以用来创建对象和实现继承

37.cookie、localstorage、sessionstorage的区别

①cookie数据始终在http同源请求中携带,即cookie在浏览器和服务器之间来回传递,而localstorage和sessionstorage不会自动把数据发送给服务器,仅保存在本地。

②cookie数据不能超过4k,而localstorage和sessionstorage可以达到5m或更大

③cookie在设置的有效期之内有效,localstorage永久有效,用于持久存储数据,sessionstorage仅在当前浏览器窗口关闭之前有效

④sessionstorage不在不同的浏览器窗口中共享;而cookie和localstorage可以在所有同源窗口共享

⑤webStorage API接口使用更加方便

38.cookie、session、token的区别

cookie和session都是记录服务器和客户端会话状态的机制

seesion是基于cookie的,session保存在服务器端,而sessionID保存在客户端的cookie中。当用户第一次向服务器发送请求时,服务器会根据用户提交的相关信息,创建对应的session,并将session唯一的标识信息sessionID发送给浏览器,浏览器接收到sessionID信息后,将信息存储在cookie中。第二次向服务器发送请求时,请求会自动判断该域名下是否存在cookie信息,如果存在则将cookie信息也发送给服务器,服务器会从cookie中获取sessionID,再根据sessionID查找相应的session信息。如果没有找到,则说明用户没有登陆或者登录已失效;如果找到,则说明登陆成功,用户可以进行下一步操作。

session和cookie的区别:

①session是存储在服务器端的,cookie是存储在客户端的

②session可存储的数据远远高于cookie

③cookie只支持字符串数据,session可存任意格式的数据

token令牌:客户端使用用户名和密码请求登录,服务端收到请求验证用户名和密码,验证成功后,服务端会签发一个token发送给客户端,客户端收到token后会把它存储起来,比如cookie或者localStorage里,客户端每次向服务端发送请求的时候需要携带服务端签发的token,服务端收到请求后去验证携带的token,如果验证成功,就像客户端返回请求的数据。每一次请求都需要携带token,需要把token放在http的header中,基于token的用户认证是一种服务端无状态的认证方式,服务端不用存放token数据,用解析token的计算时间换取session的存储空间,从而减轻服务器的压力,它可以避免同源策略。

session和token的区别:

session是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而token是令牌,token使服务端无状态化,不会存储会话信息。

39.什么是闭包,闭包有什么用?

(1)什么是闭包?

闭包是指有权访问另外一个函数作用域中的变量的函数。闭包就是函数局部变量的集合,只是这些局部变量在函数后会继续存在。当一个函数内定义另一个函数就会产生闭包。

(2)有什么用?

(全局变量不会被回收,局部变量在函数执行完成以后,函数内部的东西就会被销毁,除非被被另一个作用域所引用就不会被回收。)

模仿块级作用域:所谓块级作用域就是指在循环中定义的变量,一旦循环结束,变量也随之销毁,它的作用范围只在这一小块。而在JavaScript中没有这样的块级作用域,由于JavaScript不会告诉你变量是否已经被声明,所以容易造成命名冲突,如果在全局环境定义的变量,就会污染全局环境,因此可以利用闭包的特性来模仿块级作用域。

结果缓存:如果有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,找不到则进行计算,然后更新缓存并返回值。闭包就可以做到这点,因为它不会释放外部的引用,从而使函数内部的值得以保留

封装私有变量:我们可以把函数当作一个范围,函数内部的变量就是私有变量,在外部无法引用,但是我们可以通过闭包的特点来访问私有变量。

(3)闭包的优点是可以避免全局变量的污染,实现属性私有化。缺点是闭包会常驻内存,在不用的时候需要删除,否则会导致内存溢出,增加内存使用量。

40.js获取节点的api

①getElementById

根据id返回元素,返回值是一个完整的标签,不存在返回null

②getElementByName

通过指定的name元素来获取元素,返回值是一个NodeList(类数组对象)

③getElementByClassName

根据元素的class返回一个即时的htmlCollection(返回值是一个数组)

④querySelector/querySeletorAll

querySelector返回的是第一个匹配的元素,如果没有返回null

querySeletorAll返回一个nodelist

41.数组排序

①冒泡排序

时间复杂度o(n2)  空间复杂度o(1)

冒泡排序最少1趟,最多n-1趟,最少比较n-1次,最多n(n-1)/2次

②选择排序

时间复杂度o(n2) 空间复杂度o(1)

选择排序一定是n-1趟排序,比较的次数永远是n(n-1)/2

③快速排序

时间复杂度o(nlogn)  空间o(logn)

④插入排序

时间复杂度o(n2) 空间复杂度o(1)

⑤归并排序

时间复杂度o(nlogn)  空间复杂度o(n)

4.2跨域和跨域的解决方案

跨域是指浏览器不能执行其他网站的脚本,这是由于浏览器的同源策略造成的,是浏览器对javascript实施的安全限制。浏览器从一个域名的网页去请求另一个域名的资源时,出现域名、端口、协议的任一不同,都属于跨域。

跨域的解决方案:
①通过jsonp进行跨域(只能实现get请求)

jsonp方法的本质是引用一个<script>标签,在客户端脚本中注册一个回调函数,然后把回调函数的名字传给服务器,服务端得到请求的数据后,用回调函数把输出返回的内容包起来,这样,服务器生成的json数据就能被客户端正确接收。

②postMessage跨域

③跨域资源共享(cros)

普通跨域请求,只服务端设置Access-Control-Allow-Origin即可,前端无须设置

若要携带cookie请求,前端需要设置xhr.withCredentials=true

④nginx代理跨域

nginx具体配置

location / {

proxy_pass  跨域地址

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容