不属于vue、react、css、html、wepack、项目功能方面的面试题都放这个文档中总结。
(欢迎补充。)
基本变量和引用类型变量
基本数据类型:number、string、boolean、symbol、null、undefined、BigInt
引用数据类型:array、function、Date、Math、正则RegExp
两者之间的区别:
- 存储位置不同:基本数据类型存储在栈中,引用数据类型在栈中存储地址值、在堆中存储数据
- 传递方式不同:基本变量值传递;引用数型变量,引用传递。
symbol类型的应用
- symbol类型是唯一的,可以用来解决命名冲突问题
- 可以用来作为对象的key值(这时候只能被Obect.getOwnPropertySymbols和Reflect.ownkeys()读取到)
bigInt 类型的应用
- 用来表示超过number类型数字限制(-2^53 - 1 , 2 ^53 - 1)的数
值传递和引用传递
值传递:函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
- 值传递针对基本数据类型,引用传递针对引用数据类型 *
浅拷贝和深拷贝
引用数据类型在栈中存储地址值、在堆中存储数值
深拷贝:在堆中开辟一块新的内存,保存和原对象一模一样的值,因此当原对象改变时,不影响拷贝对象。
浅拷贝:浅拷贝时,只拷贝了一层地址值,所以当拷贝对象改变时,原对象也会改变。
//浅拷贝
let a = [1,2,3,4,5]
let b = a;
b[0] = 999
console.log(a,b)// [999, 2, 3, 4, 5] , [999, 2, 3, 4, 5]
//深拷贝
let a = [1,2,3,4,5]
let b = JSON.parse(JSON.stringify(a));
b[0] = 999
console.log(a,b)// [1, 2, 3, 4, 5] (5) [999, 2, 3, 4, 5]
forEach 和 map的相同和区别
相同点:
- 都可以遍历数组
- 参数相同,第一个参数是数组元素、第二个参数是索引值、第三个参数是原数组
不同点:
- map返回一个新数组、forEach不返回数据
- forEach改变原数组、map不改变原数组
数组的迭代方法
返回新数组的:map、filter
改变原数组的:forEach
非空判断:findIndex(有返回索引,没有返回-1)、some(有返回true,没有返回false)、find(有返回true,没有返回undefined)
every(fn):全部符合条件返回true,有一个不符合返回false
累加器:reduce
//reduce使用
let arr = [1, 1, 1, 1, 1, 5];
let a = arr.reduce((total, currentValue) => {
return total + currentValue;
});
let b = arr.reduce((total, currentValue) => {
console.log(total, currentValue, "ddsajd");
// 10 1 'ddsajd'
// 11 1 'ddsajd'
// 12 1 'ddsajd'
// 13 1 'ddsajd'
// 14 1 'ddsajd'
// 15 5 'ddsajd'
return total + currentValue;
}, 10);
console.log(a, b); //10 , 20
数组的常用方法
数组转字符串:join,toString
//有返回值
let arr = [1,2]
let a = arr.push(4)//返回数组长度3,添加最后一个元素
let b = arr.pop() //返回删除的元素2,弹出最后一个元素
let c = arr.shift()//返回删除的元素1,弹出第一个元素
let d = arr.unshift(4)//返回数组长度3,添加第一个元素
// concat:连接两个数组,返回一个新数组
// let f = arr.slice(0,1) // 截取新数组并返回,左闭右开区间
//改变原数组
//sort:数组排序
//reserve:数组反转
arr.splice(0,1,2) //start,删除的元素个数,要在删除元素的位置添加的元素
数组去重
数组排序
数组扁平化
arr.flat()
对象拼接
object.assign(obj1,obj2)
js中 0.1 + 0.2 = 0.3
不等于
js关于浮点数的处理
- 使用toFixed方法将数字转成一个指定位数的字符串
- 使用Math.round()
- 使用 Decimal.js 库
- 使用 Big.js 库
字符串常用方法(待补)
字符串转数组:split
作用域链
作用域是变量可以合法使用的范围。
作用域分为:块级作用域(const、let)、全局作用域、函数作用域
作用域链是层层嵌套的作用域,形成的由内向外的结构,用来查找变量
变量提升和函数提升
变量提升:js会进行预解析处理,会把变量声明放到最前面执行
函数提升:同理,在函数定义之前,就可以执行该函数
闭包
理解:外部函数内返回一个内部函数,这个内部函数使用外部函数的变量,并且调用了这个外部函数
function outer(){
let a = 111
function inner(){
a++;
console.log(a)
}
return inner
}
let f = outer()
f = null
作用:延长局部变量的生命周期,让函数外部可以使用函数内的变量
缺点:内存泄露
解决办法:赋空值
闭包的使用场景(待补充)
- 函数的防抖节流
- 封装私有变量
防抖和节流
防抖:高频触发事件时,只触发最后一次(王者回城、搜索框搜索输入、文本框实时保存文本)
//手写防抖
function debounce(fn, delay) {
let timer = null;
const _debounce = () => {
if (timer != null) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn();
}, delay);
};
return _debounce
}
节流:高频触发事件时,一段时间只触发一次(王者技能冷却、下拉加载、页面滚动)
//手写节流
function throttle(fn, delay) {
let timer = null;
const _throttle = () => {
if (timer != null) {
return ;
}
timer = setTimeout(() => {
fn();
timer = null
}, delay);
};
return _throttle
}
关于this的指向
函数直接调用:window
new 一个构造函数 : 指向构造出来的对象
obj.fn() :指向对象obj
使用call、apply: 指向call、apply函数的参数
箭头函数:指向外部的this
回调函数:
- 定时器、ajax、promise、数组方法回调:指向window
- vue中的回调函数:指向组件实例
- react:类组件:组件实例;函数组件:undefined
apply、call、bind的用法和区别
都是用来改变this指向的
apply:function.apply ( this, [a,b,c,…])
call:function.call(thisArg, arg1, arg2, …),call和apply的区别就在于apply传参是数组形式,call是字符串形式
bind:bind会改变this指向后,返回一个绑定新this的函数;
原型和原型链
原型:
1.每个函数都有一个显式原型:prototype
2. 隐式原型:每个实例都有一个隐式原型:proto
3. 实例上的隐式原型和原型都指向同一个原型对象
4. 原型对象包含一个construor和一个隐式原型
原型链:从对象的隐式原型开始,一层层链接所有对象
原型链的使用场景(?)
- 继承
- 原型扩展
- 模块化开发
- 原型查找
判断数据类型的几种方式
typeof:判断数组、null、对象时返回的都是 Object。
instanceof:instanceof 是用来 判断数据是否是某个对象的实例,返回一个布尔值。对于基本类型的数据,instanceof是不能直接判断它的类型的,因为实例是一个对象或函数创建的,是引用类型,所以需要通过基本类型对应的 包装对象 来判断。所以对于 null 和 undefined 判断不了。
object.prototype.toString:都能判断。
同步和异步
同步:js是单线程语言,在主线程上的任务只能一项一项任务完成、前一项完不成、后一项只能排队
异步:不进入主线程,而是进入任务队列,宏任务进入宏队列,微任务进入微队列
宏任务和微任务
宏任务:setTimeout 、setInterval 、Ajax 、DOM事件、script(整体代码)
微任务:promise async/await
执行顺序(事件循环-eventloop):script 主线程> script上创建的微任务 > 宏任务中的所有微任务(重复)
new操作符做了什么
//手写new操作符
//Fn为构造函数
function myNew(Fn) {
//JS内部首先会先生成一个对象
let obj = {};
//使空对象的隐式原型指向构造函数的显式原型
obj.__proto__ = Fn.prototype;
// 把函数中的this指向该对象并执行构造函数中的语句
let res = Fn.apply(obj);
console.log(res, "res");
//返回该对象实例
return res instanceof Object ? res : obj;
}
function Person() {
this.name = "小红";
this.hhh = "哈哈哈";
}
var obj1 = myNew(Person);
console.log(obj1);
promise(?)
promise是用来解决回调地狱问题的工具,promise对象有三种状态、pending、fullfilled、rejected。promise.then()返回一个新的promise,结果由then指定的函数执行结果而定。
//手写promise(建议面试前写三遍)
promise引申
- promis.all([p1,p2,p3]) (一荣俱荣,一损俱损)
- promise.race([p1,p2,p3])(有一个成功,就返回成功)
继承方式(?)
事件冒泡和事件委托
元素之间是相互关联的,当一个元素被触发后、会触发到其他的元素。
事件冒泡:当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window。(e.stopPropagation() )
事件委托:事件委托也叫事件代理,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown…)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
——ES6——
es5和es6的区别
es6新增特性
- const、let
- 解构赋值
- 拓展运算符
- 箭头函数
- async、await
- 新数据类型Symbol
- map对象、set对象
- 模板字符串
- 模块化语法
const、let、var的区别
const 和 let 存在块级作用域、var不存在块级作用域
var 可以变量提升、const 和 let不可以
var可以重复声明变量、const 和 let 不可以
const 声明的是常量,不可以重复赋值
箭头函数的特点
- 箭头函数不绑定this,会捕获其所在上下文的this,作为自己的this。
- 语法上更简洁
- 箭头函数本身是匿名函数、本身不可以作为构造函数、不可以使用new关键字
- 不绑定arguments,使用rest参数解决
- 使用call、apply和bind不会改变箭头函数中this的指向
- 没有原型对象
什么情况不使用箭头函数?
async和defer的相同点和区别
浏览器解析HTML时,遇到script时,会停下来下载和执行script标签内的代码,这样便会造成阻塞。
async可以让HTML解析和 script 下载执行同时进行,但是这种情况不知道页面加载和script执行哪一个先完成,所以如果遇到要操作dom的情况,就有可能出错。
defer可以让HTML解析和 script 下载同时进行,但defer多了一个推迟的作用,可以推迟script的执行,直到HTML解析完
fetch API
fetch是原生js发送网络请求的一种方式,本身返回一个promise,这个promise的结果值就是后端的响应值。