前言
Hello 大家好,这是一篇近期关于面试总结的文章,目前是在校大四学生,文章形式主要以问题和答案的方式展现,可能会有不全面的的地方,希望大家批评指正,一方面是记录自己的知识点,另一方面是分享出来给需要的小伙伴!!!
所有的知识点都慢慢整理到 Github 快来给我Star呀😊~
因为近期一直在忙着找实习和毕业的事情,没有时间整理出来。希望大家看完能够重温一下基础。
1. var、let 及 const 区别
-
函数提升优先于变量提升,函数提升会把整个函数放到顶部,而变量提升只是把声明方到顶部
-
var
存在提升,可以在声明之前使用 值为undefined
。let、const 不可以是因为会形成暂时性死区 -
let
和const
作用基本一致,前者不允许在同一个作用域重复声明同一个变量,后者声明一个基本类型的时候为常量,不可修改;声明对象可以修改
2. 说一下内置类型
JS
中有其中内置类型,可以分为两大类型:基本类型(栈)和对象(堆)- 基本类型有六种:
null
,undefined
,boolean
,number
,string
,symbol
- 对象(
Object
)是引用类型,在使用过程会涉及到浅拷贝和深拷贝的问题
typeof 对于基本类型,除了
null
都可以正常显示
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
--------------------------------------------
typeof [] // 'object'
typeof {} // 'object'
3. 实现 instanceof 原理
instanceof 可以正确判断对象类型,判断方式以原型链的方式查找
function myInstanceof(left,right){
let prototype = right.prototype;
left = left.__proto__;
while(true){
if(left === null || left === undefined)
return false
if(prototype === left)
return true
left = left.__proto__
}
}
4. 原型及原型链
- 每个函数都有自己的
prototype
- 每个对象都有
__proto__
属性,指向了创建该对象的构造函数的原型。其实这个属性指向了prototype
,所以使用_proto_
来访问 - 对象可以通过
__proto__
来寻找不属于该对象的属性,__proto__
将对象连接起来组成了原型链
5. this指向
改变
this
指向可以通过 call、apply、bind. 传参形式分别是 参数队列,数组,函数箭头函数其实没有
this
,这个函数中的this
只取决于他外面的第一个不是箭头函数的函数的this
function foo() {
console.log(this.a)
}
var a = 1
foo()
var obj = {
a: 2,
foo: foo
}
obj.foo()
// 以上两者情况 `this` 只依赖于调用函数前的对象,优先级是第二个情况大于第一个情况
// 以下情况是优先级最高的,`this` 只会绑定在 `c` 上,不会被任何方式修改 `this` 指向
var c = new foo()
c.a = 3
console.log(c.a)
// 还有种就是利用 call,apply,bind 改变 this,这个优先级仅次于 new
6. 说一下new的原理
- 生成一个新的对象
- 链接到原型上
- 绑定
this
- 返回一个新的对象
// 实现 new
function create(Con, ...args){
let obj = {} //生成新对象
obj.__proto__ = Con.prototype //链接原型
let res = Con.apply(obj,args)
return res instanceof Object ? res : obj //确保返回的是对象
}
7. 闭包
闭包的定义很简单:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
function A() {
let a = 1
function B() {
console.log(a)
}
return B
}
经典面试题
for ( var i=1; i<=5; i++) {
setTimeout( function timer() { // setTimeout是异步 进入队列等待同步任务执行结束
console.log( i );
}, i*1000 );
}
// 第一种解决方法 闭包
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}
// 第二种解决方法 let 形成块级作用域
for ( let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
// 相当于
{ // 形成块级作用域
let i = 0
{
let is = i
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
i++
{
let ii = i
}
i++
{
let ii = i
}
...
}
8. 深浅拷贝
- 浅拷贝
第一种可以通过 Object.assign来实现,第二种可以通过扩展运算符 ( ... )
let a = {
age:21
}
let b = Object.assign({},a) // 等同于 let b = {...a}
a.age = 18
console.log(b.age) // 1
- 深拷贝
深拷贝主要解决引用地址相同的问题
// 第一种可以通过 JSON.parse(JSON.stringify(object))
// 该方法有局限性 不能解决嵌套的引用对象
let a = {
age: 18,
name:{
first:'wysoka'
}
}
let b = JSON.parse(JSON.stringify(a))
a.name.first = 'web'
console.log(b.name.first) // wysoka
// 第二种方法 自定义方法
function deepcopy(obj){
let res = obj instanceof Array ? [] : {}
for(const [k,v] of Object.entries(obj)){
res[k] = typeof v == 'object' ? copy(v) : v
}
return res;
}
9. 防抖、节流
防抖:在一段时间内把最后一次的操作为准,在规定时间内,重复操作重新计时
// 通俗易懂版
function debounce(fn,time){
var t = null;
return function(){
clearTimeout(t);
t = setTimeout(fn,time);
}
}
节流:好比国企单位到点就下班,在规定的时间内,到时间就输出,在浏览器当中的刷新频率
60HZ
相当于起到节流的作用
// 通俗易懂版
function throttle(callback,duration){
var lasttime = 0;
return function(){
var now = new Date().getTime();
if(now - lasttime > 1000){
callback();
lasttime = now;
}
}
}
10. 数组去重的几种方法
- 利用 ES6 去重
function unique(arr){
return Array.from(new Set(arr)) // 等同于 [...new Set(arr)]
}
- 两层 for 循环
for (let i=0, len=arr.length; i<len; i++) {
for (let j=i+1; j<len; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
len--;
j--;
}
}
}
- indexOf / includes
function removal(a,b){
let arr = a.concat(b);
return arr.filter((item,index)=>{
//return arr.indexOf(item) === index
//return arr.includes(item)
})
}
11. 事件触发分为三个阶段
- 捕获阶段、处于目标阶段、冒泡阶段
- 冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发
stopPropagation()
- 捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发
preventDefault()
DOM
事件流:同时支持两种事件模型:捕获型事件和冒泡型事件