1.ES6新增了哪些方法
1、includes()用于判断数组是否包含给定的值 返回一个布尔值
2、find()用于找出第一个符合条件的数组成员
3、findindex()返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
4、set数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值
5、、let声明变量、const声明常量(这里就要问你var、let、const的区别了)
6、解构赋值 ...
set 和map 的区别!!!以前被问没看过,懵逼过,所以要记住
- Map是键值对,Set是值的集合,键和值可以是任何的值;
- Map可以通过get方法获取值,而set不能因为它只有值,set只能用has来判断,返回一个布尔值;
- Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储
2.promiseApi
Promise构建出来的实例存在以下方法:
- then() 是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数
- catch() 用于指定发生错误时的回调函数
- finally() 用于指定不管 Promise 对象最后状态如何,都会执行的操作
Promise构造函数存在以下方法:
- all() 用于将多个 Promise实例,包装成一个新的 Promise实例
- race() 同样是将多个 Promise 实例,包装成一个新的 Promise 实例
- allSettled()
- resolve()
- reject()
- try()
3.Var、 let 、const 区别?
- 变量提升
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined
let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错
js
- 暂时性死区
var不存在暂时性死区
let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
- 块级作用域
var不存在块级作用域
let和const存在块级作用域
- 重复声明
var允许重复声明变量
let和const在同一作用域不允许重复声明变量
- 修改声明的变量
var和let可以
const声明一个只读的常量。一旦声明,常量的值就不能改变
使用
能用const的情况尽量使用const,其他情况下大多数使用let,避免使用var
4.== 和 ===区别
相等操作符(==)会做类型转换,再进行值的比较,全等运算符(===)不会做类型转换
let result1 = ("55" === 55); // false,不相等,因为数据类型不同
let result2 = (55 === 55); // true,相等,因为数据类型相同值也相同
null 和 undefined 比较,相等操作符(==)为true,全等为false
let result1 = (null == undefined ); // true
let result2 = (null === undefined); // false
5.数组常用方法
增
- push() 接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度
- unshift() 开头添加
- concat() 首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组
删 :
- pop() 删除数组的最后一项,同时减少数组的length 值,返回被删除的项
- shift() 删除数组的第一项,同时减少数组的length 值,返回被删除的项
- splice() 传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
- slice() 创建一个包含原有数组中一个或多个元素的新数组,不会影响原始数组
改
- 常用splice
传入三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,对原数组产生影响
查
- indexOf() 返回要查找的元素在数组中的位置,如果没找到则返回 -1
- includes() 返回要查找的元素在数组中的位置,找到返回true,否则false
- find() 返回第一个匹配的元素
排序方法
- reverse() 将数组元素方向反转
- sort(首元素地址(必填), 尾元素地址的下一个地址(必填), 比较函数(非必填));
// 如果直接sort(数组名),则从小到大排序(即升序),以下为倒叙
var arr4 = [30,10,111,35,1899,50,45];
arr4.sort(function(a,b){
return b - a;
})
console.log(arr4);//输出 [1899, 111, 50, 45, 35, 30, 10]
转换方法
- join() 方法接收一个参数,即字符串分隔符,返回包含所有项的字符串
迭代方法
- some() 对数组每一项都运行传入的函数,如果有一项函数返回 true ,则这个方法返回 true
- every() 对数组每一项都运行传入的函数,如果对每一项函数都返回 true ,则这个方法返回 true
- forEach() 对数组每一项都运行传入的函数,没有返回值
- filter() 对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回
- map() 对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组.
去重方法
去重方法
- 利用ES6 Set去重
function unique (arr) {
return Array.from(new Set(arr))
}
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, "NaN", 0, "a", {}, {}]
- 利用for嵌套for,然后splice去重
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
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", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}没有去重,两个null直接消失了
- 利用indexOf去重
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array .indexOf(arr[i]) === -1) {
array .push(arr[i])
}
}
return array;
}
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, NaN, "NaN", 0, "a", {…}, {…}] //NaN、{}没有去重
- 利用includes
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array =[];
for(var i = 0; i < arr.length; i++) {
if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
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, "NaN", 0, "a", {…}, {…}] //{}没有去重
6.bind、call、apply 区别
- call和apply会调用函数,且会改变函数内部的this指向
- call和apply传递的参数不一样,call传递参数aru1,aru2.形式 而apply必须是数组形式[arg]
- bind 不会调用函数,可以改变函数内部指向
应用场景:
- call经常做继承
- apply经常和数组有关系,比如借助于数学对象实现数组的max、min
- bind不调用函数,但改变this指向,比如改变定时器内部的this指向
apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即 A 对象应用 B 对象的方法。 call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2); 即 A 对象调用 B 对象的方法。 bind 除了返回是函数以外,它的参数和 call 一样。
7.本地存储的方式有哪些?区别及应用场景?
javaScript本地缓存的方法我们主要讲述以下四种:
- cookie
- sessionStorage
- localStorage
- indexedDB
区别
- 存储大小:cookie数据大小不能超过4k,sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
- 有效时间:localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据; sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
- 数据与服务器之间的交互方式,cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端; sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存
应用场景
- 标记用户与跟踪用户行为的情况,推荐使用cookie
- 适合长期保存在本地的数据(令牌),推荐使用localStorage
- 敏感账号一次性登录,推荐使用sessionStorage
- 存储大量数据的情况、在线文档(富文本编辑器)保存编辑历史的情况,推荐使用indexedDB
8.说说你对闭包的理解?闭包使用场景
闭包就是函数中包含另一个函数,可以让你在函数外部读取到内部的变量(就是在函数内部再定义一个函数),让这些变量的值始终保持在内存中,可以达到延长变量生命周期的效果,过多使用会导致内存泄漏的问题(在创建私有变量和想延长变量的生命周期时会用到闭包)
9.深拷贝浅拷贝的区别?
浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址
在JavaScript中,存在浅拷贝的现象有:
- Object.assign
- Array.prototype.slice()
- Array.prototype.concat()
- 使用拓展运算符实现的复制
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
常见的深拷贝方式有:
- _.cloneDeep()
- jQuery.extend()
- JSON.stringify()
- 手写循环递归
10.JavaScript中的数据类型?
string、number、Boolean、undefined、null、object
11.什么是防抖和节流?
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖
电梯第一个人进来后,15秒后准时运送一次,这是节流
12.如何解决数字精度丢失的问题?
- 理论上用有限的空间来存储无限的小数是不可能保证精确的,但我们可以处理一下得到我们期望的结果
当你拿到 1.4000000000000001 这样的数据要展示时,建议使用 toPrecision 凑整并 parseFloat 转成数字后再显示,如下:
parseFloat(1.4000000000000001.toPrecision(12)) === 1.4 // True
封装成方法就是:
function strip(num, precision = 12) {
return +parseFloat(num.toPrecision(precision));
}
最后还可以使用第三方库,如Math.js、BigDecimal.js
13.JavaScript 中内存泄漏的几种情况?
- 使用闭包
14.原型,原型链 ? 有什么特点?
JavaScript 常被描述为一种基于原型的语言——每个对象拥有一个原型对象
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾
原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法
14.如何实现上拉加载,下拉刷新?
开源社区有很多优秀的解决方案,如iscroll、better-scroll、pulltorefresh.js库等等
15.说说你对作用域链的理解
- 作用域就是变量与函数的可访问范围
- 一般情况下,变量取值到创建这个变量的函数的作用域中取值。 但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链
16.typeof 与 instanceof 区别
- typeof与instanceof都是判断数据类型的方法,区别如下:
typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值
instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
而typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断
17.ajax、axios、jsonp的理解
- jsonp是一种可以解决跨域问题的方式,就是通过动态创建script标签用src引入外部文件实现跨域,script加载实际上就是一个get请求,并不能实现post请求。(其他实现跨域的方法有:iframe,window.name,postMessage,CORS…)
- ajax是一种技术,ajax技术包含了get和post请求的,但是它仅仅是一种获取数据的技术,不能直接实现跨域,只有后台服务器配置好Access-Control-Allow-Origin,才可以实现请求的跨域。
- axios是通过promise实现对ajax技术的一种封装,axios是ajax,ajax不止axios。
- 总结:
juery的$.ajax实现get请求能跨域是因为jsonp或者因为原生ajax和服务器的配合,post请求能跨域就只能是因为原生ajax和服务器的配合。
19.ajax的请求过程
// ajax 提交 post 请求的数据
// 1. 创建核心对象
var xhr = new XMLHttpRequest();
// 2. 准备建立连接
xhr.open("POST", "register.php", true);
// 3. 发送请求
// 如果要POST提交数据,则需要设置请求头
// 有的面试官会问为什么要设置请求头? 知道请求正文是以什么格式
// Content-Type: application/x-www-form-urlencoded,请求正文是类似 get 请求 url 的请求参数
// Content-Type: application/json,请求正文是一个 json 格式的字符串
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// 发送数据
xhr.send(querystring);
// 4. 处理响应
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { // 请求处理完毕,响应就绪
if (xhr.status === 200) { // 请求成功
var data = xhr.responseText;
console.log(data);
}
}
}
20.ajax请求的时候get 和post方式的区别
- get请求不安全,post安全 ;
- get请求数据有大小限制,post无限制 ;
- get请求参数会在url中显示,容易被他人窃取,post在请求体中
- post需要设置请求头。
21.什么是事件委托以及优缺点
js事件委托就是利用冒泡的原理,把本应该添加到某个元素上的事件委托给他的父级,从而减少DOM交互达到网页优化。
- 优点:
1.可以大量节省内存占用,减少事件注册。比如ul上代理所有li的click事件就很不错。 2.可以实现当新增子对象时,无需再对其进行事件绑定,对于动态内容部分尤为合适
- 缺点:
事件代理的常用应用应该仅限于上述需求,如果把所有事件都用事件代理,可能会出现事件误判。即本不该被触发的事件被绑定上了事件。