面试题总结 - js基础知识

前言:众所周知,HTML,CSS,JS是学习前端所必备的。js的基础学好了,框架类的vue,react等都会接受的很快,因此js是前端很总要的一个部分,这篇文章将会结合面试题,对js的知识点进行总结

          号外号外,这是一篇长长的博客,所以耐心耐心,希望能有收获哦,加油~~~

 

1. 数据类型和转换

1.1 常见面试题

(1)typeof可以判断哪些数据类型

(2)什么时候使用 == ? 什么时候使用 === ?

(3)值类型和引用类型的区别是什么?

(4)手写深拷贝

1.2 知识点

1.2.1 有哪些数据类型?

              - Number,String ,Boolean, Object(Array,Fuction...), Undefined, Null,Symbol

1.2.2 数据类型怎么分类,有什么区别?

  (1) 数据类型分为 - 值类型和引用类型

   值类型 - undefined, Number, String, Boolean,Symbol

   引用类型 - Object, Null

  (2) 区别

      根据内存空间和cpu的耗时,值类型和引用类型在存储,拷贝等方面都不同

     ①存储地址

      首先,存储空间分为栈和堆,一般情况,栈从上往下排列,堆从下往上排列,两种数据类型在栈堆中的表现各不相同,通过例子来查看

      值类型的存储:

例1

let a = 100;
let b = a;
a = 200
console.log(b)

   打印出来的b显然是100,原因如下图:

   值类型直接将值存储在栈中,在赋值过程中,开辟新的存储空间b,将a的值赋值给b,因此在去修改a与b无关了,a和b有各自的存储空间。

   一般情况下 -  值类型,占用空间小

面试题总结 - js基础知识_第1张图片

    引用类型的存储:

例2

let a = {age: 20};
let b = a;
a.age = 21;
console.log(b)

     这个时候的b的age已经被改变,为21,原因如下图:

    引用类型,是在栈中开辟了一个堆的存储地址,a的存储value假设是0x12(一个16进制), 这个地址在堆中就是age为12,引用类型是将栈的value进行赋值,也就是b的value其实也是ox12,指向了同一个堆的地址,因此a的改变会影响b的改变。也就是,a与b有各自的存储空间,但是他们的数据,其实在堆中都是同一空间

   引用类型 - 占用空间可能很大,像json数据,内容空间和cpu的耗时来区分

面试题总结 - js基础知识_第2张图片

面试题总结 - js基础知识_第3张图片

  注意 - Null的指针其实是指向了一个空地址、因此他也是一个引用类型

  ②拷贝

   拷贝有深拷贝和浅拷贝 

  因为之前已经写过一次拷贝了,所以就不重复写了

  https://blog.csdn.net/qq_42625428/article/details/103515192

  深拷贝就是拷贝引用类型,像例2一样,赋值后并不想数据相互影响,这个时候就需要深拷贝

1.2.3 typeof

 typeof   - 判断所有的值类型,判断是否为引用类型,是返回object,能判断函数,是函数,返回function

面试题总结 - js基础知识_第4张图片

1.2.4 变量计算 - 类型转换

(1)字符串拼接

let a = 100 + 10 //110
let b = 100 + '10' // '10010'
let c = true + '10' // 'true10'

(2) == 

100 == '100'  //true
0 == ''  //true
0 == false //true
false == '' //true
null == undefined //true  

所以,当要进行明确判断的时候,都用=== 

面试题总结 - js基础知识_第5张图片

(3)if和逻辑计算

 truly变量   !!变量  === true的变量  - 两次取反为true的变量

falsely变量  !!变量 == false的变量  - l两次取反为false的变量

面试题总结 - js基础知识_第6张图片

if判断的是turely变量和falsely变量

逻辑计算,也是判断的truely变量和falsely变量

1.3 面试题答案

(1)typeof可以判断哪些数据类型

     typeof可以判断所有的值类型,可以判断是否为引用类型,是则返回object,也可以判断函数,是函数,返回function

(2)什么时候使用 == ? 什么时候使用 === ?

   == 存在隐式数据类型转换,因此除了 == null之外,其他的都用 === 最好啦

(3)值类型和引用类型的区别是什么?

  值类型和引用类型在存储和拷贝过程中都有区别

 存储,值类型是直接将数据存储在栈中

           引用类型是将数据的存储在堆中,将存储数据的地址值存储在栈中

拷贝过程:要完全拷贝,对于引用类型需要进行深拷贝

(4)手写深拷贝

function deepClone(obj) {
  if(typeof obj !== 'object' || obj == null) {
    return obj;
  }
 
  let result
 
  if(obj instanceOf Arrary){
     result = [];
  }else {
     result = {};
  }
 
  for(let key in obj){
     if(obj.hasOwnProperty(key)){
        result[key] = deepClone(obj[key])
     }
  }
 
  return result;
 
}

当然,深拷贝不止这一种方法,这里只是写一下通过递归进行深拷贝

 

2.原型和原型链

1.1 常见面试题

(1)如何准备判断一个变量是不是数组?

(2)class的原型本质,怎么理解?

1.2 知识点

1.2.1 class和继承

(1) class

 class是一个面向对象的一个实现,class本质类似于一个模板,可以去构建(constructor)一些东西(方法,属性)

//定义类
class Student{
    constructor(name, number){
        this.name = name;  //this - 指向实例对象
        this.number = number;
    }
    sayHi() {
        console.log(`姓名:${this.name},学号:${number}`);
    }
}

通过类声明 多个对象/实例
const lixian = new Student('李现', 20);
console.log(lixian.name, lixian.number);
lixian.sayHi();

const li = new Student('李', 22);
console.log(li.name, li.number);
li.sayHi();

(2)继承

继承,是定义一个父类(具有公共特性,如人,都有名字,都有身份证,都能吃饭等等),子类(具有自己的属性,比如学生,需要学习,有学号)继承父类。

继承主要是通过extends关键字,然后利用super继承属性

//定义父类
class People{
    constructor(name, id){
        this.name = name;  //this - 指向实例对象
        this.id= id;
    }
    eat() {
        console.log(`${this.name}吃东西了`);
    }
}


//子类继承并含有特有的属性和方法
//学生类
class Student extends People {
    constructor(name, id, number){
        super(name, id);
        this.number = number;
    }

    study() {
        console.log('study了')
    }
}
//老师类
class Teacher extends People {
    constructor(name, id, subject){
        super(name, id);
        this.subject= subject;
    }

    teach() {
        console.log('上课了')
    }
}

//实例化
const s1 = new Student('夏洛', 510524199710125420, 201544901114);
console.log(s1.name, s1.id, s1.number);
s1.eat();
s1.study();

注意的是,class只是es的一个规范,具体怎么实现还是引擎说了算

1.2.2 判断类型instanceof

instanceof是基于原型链进行判断的,判断是否属于一个类,是否属于一个构造函数

基本上面的例子

lixian instanceof People //true
s1 instanceof Student  //true
s1 instanceof People  //true
s1 instanceof Object  //ture

怎么理解instanceof, a instanof b ,是看a是否是b构建出来的,当然,b也可以是构建a的父类(people),Object是所有类的父类,最顶层,是js引擎自己定义的,因此是否是ture,其实是基于原型链进行查找,就可以看下一个知识点 - 原型和原型链

1.2.3 原型和原型链

 首先

typeof Student  //function
typeof People  //function

 因此,类其实是一个方法,只是是一个特殊的方法

原型和原型链,需要知道的是隐式原型和显示原型,因为之前也非常详细的写过了,所以就暂时不继续写了

关于细节,请看这个 - 》https://blog.csdn.net/qq_42625428/article/details/107806845

当然,对于懒的孩子,我直接将原型和原型链比较重要的几句话,粘贴过来了

    1.每个函数(通常指构造函数,如Person)都有一个属性:prototype

    2.prototype是一个对象, 里面有constructor , __proto__ 隐式原型

    3.构造函数的原型对象指向当前构造函数 Person.prototype.constructor === Person  

    4.实例对象的隐式原型指向构造函数的显示原型 

               p1.__proto__ === Person.prototype 

    5.在构造函数显示原型中添加的方法,所有的实例对象共享 

    6.访问属性和方法,是基于原型连进行访问 在当前的实例对象去找--》构造函数的原型 ==》.... =》 Object的原型 

如果感觉一看这几句话就懂了,就不需要再去看总结的原型和原型链了

这里是一个Student和实例,夏洛的一个图,看了原型和原型啦,在看这个图,就很清楚了

class Student{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    sayHi(){
        console.log(this.name, this.age)
    }
}

const xialuo = new Student('夏洛', 100)
xialuo.sayHi();  //'夏洛', 100
xialuo.__proto__.sayHi(); //undefined

面试题总结 - js基础知识_第7张图片

可能会疑惑,实例对象xialuo执行sayHi有值,并且他就是通过隐式原型去找,为什么会是undefined,这里大家可以看下一个知识点this之后再回来理解

首先,类Student中sayHi的this,指向的实例对象也就是xialuo

          因此很好理解xialuo.sayHi() => xialuo就是实例对象,可以理解为执行其实是,xialuo,__proto__.sayHi.call('xialuo')

          而xialuo.__proto__ => 他并不是Student的实例对象

1.3 面试题答案

(1)如何准备判断一个变量是不是数组?

   a instanceof Arrary

(2)class的原型本质,怎么理解?

 class是es6的新特性之一,通过class关键字来定义一个类,可以理解class为一个模板,它的本质就是一个函数,可以通过constructor构造方法来构造一些属性,直接定义方法,定义子类进行继承。

 

3 作用域和闭包

 3.1 常见面试题

 (1)this在不同应用场景下如何取值

 (2)手写bind函数

 (3)闭包在实际开发总的应用场景

 (4)创建10个a标签,点击弹出对应的序号

3.2 知识点

3.2.1 作用域和自由变量

作用域:变量的合法使用范围,如下面一个框就是一个作用域

面试题总结 - js基础知识_第8张图片

作用域分类 - 全局作用域,函数作用域,块级作用域(es6新增的)

    全局作用域 - 整个js文件都可以使用的变量

    函数作用域 - 在函数中使用的变量

    块级作用域 - es6中的,一般是指一个{}区域内的变量,声明块级作用域的变量 - 用 let

自由变量:作用域内如果没有该变量,从下往上找,如a2,在函数作用域中没有a2,往上一级找,找到为止

3.2.2 闭包

作用域应用的特殊情况,一般有两种情况 - 函数作为参数被传递,函数作为返回值返回,简单解释闭包 - 嵌套函数使用外部的变量

直接看两个题

函数作为返回值

function create () {
    let a = 100;
    return function () {
        console.log(a)
    }
}

const fn = create()
const a = 200;
fn() 

函数作为参数传递


function print(fn) {
    const a = 200;
    fn();
}
const a = 100;
function fn(){
    console.log(a)
}
print(fn)

注意 - 自由变量是在定义的位置向上级作用域查找,而不是在执行的位置,因此上面两道题都是100

如果没有搞懂闭包,可以看下这篇博客,详细的在了解一下闭包  https://blog.csdn.net/qq_42625428/article/details/84300823

3.2.3 this

this的指向在不同情况下,指代的不同,有下列几种情况

(1)作为普通函数

(2)使用call, apply , bind

(3)作为对象方法调用

(4)在class方法中调用

(5)箭头函数

 this是什么值,是在函数执行的时候确定的

 下面通过例子讲解上面5中情况

 (1)作为普通函数,call.bind,apply

function fn(){
    console.log(this)
}

fn(); //window

fn.call({x: 100})  //{x: 100}

const fn2 = fn.bind({x: 200})
fn2();  //{x: 200}

定义的函数 - 在执行时候,this是指向window

而call,apply,bind是可以改变this指向,this指向调用的对象 ,他们的区别是什么?

apply和call基本是一致的,区别在与传参 - apply把需要传递给fn的参数放到一个数组(或者类数组)中传递进去,而call是单独传递的,第一个参数是更改this的对象

bind和他们的区别是 - bind需要自己调用一次,他是等待执行而不是立即执行,如上的例子,bind是返回了一个新函数,需要在执行新函数fn2

比如 

function test(a,b){
    console.log(this)
}


test.call({x:100}, 1 , 2)
test.apply({x: 100}, [1,2])

(2)对象的this和箭头函数的this

对象中的this是指向对象本身的,而箭头函数的this是指向上一级作用域的this

面试题总结 - js基础知识_第9张图片

(3)类中的htis - 类中的this是指向实例对象

面试题总结 - js基础知识_第10张图片

所以,到现在,记住了this的指向吗,不同情况的this指向不同,要根据确切的情况去判别

 3.3 面试题答案

 (1)this在不同应用场景下如何取值

    作为普通函数   - 指向window

    使用call, apply , bind  -  指向传入的对象

    作为对象方法调用  - 指向对象本身

    在class方法中调用 - 指向实例对象 

    箭头函数 - 指向上级作用域的this

 (2)手写bind函数

Function.prototype.bind1 = function() {
   //将所有参数的伪数组变为真数组
   const args = Array.prototype.slice.call(arguments);

   //取出第一个数组
   cosnt t = args.shift();

   fn.bind()中的fn
   const self = this;

   //返回一个函数
   return function () {
        return self.apply(t, args)
    }
}

 (3)闭包在实际开发总的应用场景,举例说明

(1)隐藏数据

(2)简单做一个缓存工具 - 闭包会缓存值 - 具有特定功能的js文件,将所有的数据和功能封装在一个函数内部,只向外暴露n个对象或者方法

function toCache(){
    const data = {} //定义的数据被隐藏,不被外界访问,只能在执行函数去保存修改值
    return {
        set: function(key, value){
            data[key] = value;
        },
        get: function(key){
            return date[key]
        }
    }
}

const s = toCache();
s.set('a', 100) //存储
s.get('a') //获取值

 (4)创建10个a标签,点击弹出对应的序号

for(let i = 0; i < 10; i++){
    let a = document.createElement('a');
    a.innerHTML = i + '
'; a.addEventListener('click', (e) => { e.preventDefault(); alert(i) }) document.boy.appendChild(a) }

为什么用var i不能,存在作用域为题,使用let是将每一个循环作为一个块级作用域,可以理解为保存了每个值。如果不是很能理解,可以查看闭包模块的知识点- https://blog.csdn.net/qq_42625428/article/details/84300823

 

4. 异步和单线程

4.1 常见面试题

(1)同步和异步的区别是什么?

(2手写Promise加载一张图片?

(3)前端使用异步的场景有哪些?

(4)题目 - 输出顺序

console.log(1)
setTimeout(funciton(){
console.log(2)
},1000)
console.log(3)
setTimeout(funciton(){
console.log(4)
},0)
console.log(5)

4.2 知识点

4.2.1 单线程和异步

首先js是单线程语言,只能同时执行一件事。js和DOM渲染共用一个线程,因为js可以修改DOM结构,思考一下,如果不用一个线程,如果是在渲染DOM,但是js又修改了DOM那不是就没有意义了。那为什么需要异步??

遇到等待(网络请求,定时任务)不能卡住 - 需要异步 - 解决单线程问题,基于callback(在动动小脑瓜,如果是在请求一个图片或者其他资源的时候,浏览器卡住了,不能点击,不能做任何操作,是不是体验很差,而且等待时长多么的漫长,所以异步存在解决了这个问题)

毕竟等待的过程中,cpu是空闲状态,空闲不是浪费吗,所以异步多么的重要

异步 - 不会阻塞后面代码的执行

console.log(1)
setTimeout(() => {
consloe.log(2)
}, 1000)
consloe.log(3)

setTimeout就是典型的异步执行,代码的输出顺序是132 

同步- 阻塞后面的代码

consoe.log(1)
alert(2)
consloe.log(3)

alert也是典型的同步操作,只有输出了2才能输出3,执行顺序123

4.2.2 应用场景

什么时候需要异步,也就是需要等待的情况

主要是 - 网络请求,像ajax请求

           - 定时任务,像setTimeout

4.2.3 回调地狱(callback hell) 和 Promise

回调地狱也是之前已经总结过的,所以不太懂的可以看 https://blog.csdn.net/qq_42625428/article/details/103637357

总的来说 - 回调地狱就是出现了嵌套的网络请求,而Promise的出现解决了这一问题

关于promise没有进行详细的解释,需要掌握它的用法

4.3 面试题答案

(1)同步和异步的区别是什么?

 异步不会阻塞代码的执行,同步会阻塞代码的执行

(2手写Promise加载一张图片?

这里的例子写的是加载两个图片哦

function loadImg(srv){
     返回一个promise,promise参数是一个函数,函数有两个参数,resolve,reject,也是一个函数
    return new Pomise((resolve, reject) => {
        const img = document.createElement('img')
        img.onload = () => {
            resolve(img) //resolve的数据用then接收
        }
        img.onerror = () => {
            reject('加载失败')  //用catch接收
        }
        img.src = src
    })
}

loadImg('图片地址').then((img) => {
        console.log(img)
       return img  //return的数据,用then继续接收
 }).then((img) => {
        consloe.log(img)
        return loadImg('图片地址')  //return 的是promise对象
 }).then((img) =>{
    consloe.log(img)  //img是加载图片的resolve的数据
 }).catch((err) => {
        console.log(err)
 })

(3)前端使用异步的场景有哪些?

网络请求,像ajax请求

           - 定时任务,像setTimeout

(4)题目 - 输出顺序

面试题总结 - js基础知识_第11张图片

输入顺序 - 13542

 

 js Web Api

js的规范是ECMA标准规定的,而Web API是操作网页api是W3C规定的

1. DOM操作 - Document Object Model(文档对象模型)

    现在的框架,vue,react等其实已经封装了DOM操作,所以在使用框架的时候,其实很多都不需要在自己去操作DOM了,但是作为一个前端工程师,其实DOM操作是最基本的。

1.1 常见面试题

 (1)DOM是哪种数据结构

 (2)DOM操作的常见api

 (3)属性attr和property的区别

 (4)一次性插入多个DOM节点,考虑一下性能

1.2 知识点

1.2.1 DOM的本质

介绍之前,先了解另外一种mxl语言,xml也是由标签组成,与html不同的是,它的标签是可以自定义的,而html的标签必须按照标准写。

那什么是DOM? DOM的本质是HTML语言解析的一棵树。

1.2.2 DOM节点操作

(1)获取节点

document.getElementById('id名')
document.getElementsByTagName('标签名')
document.getElementsByClassName('类名')


document.querySelector('选择器')  //返回第一个满足css选择器条件的元素
document.querySelectorAll('选择器')  //返回所有满足css选择器条件的元素

  注意的是,除了id名和querySelector以外,其他的都是返回的一个满足条件的元素的伪数组

(2)修改节点

①property - 一种通过js进行修改获取属性的一种形式

const p = document.querySelector('p')

p.style.width = '100px'; //设置属性
cosole.log(p.style.width)  //获取属性

②attribute - 利用api直接修改获取修改属性

const p = document.getElementById('p')

p.setAtrribute('data-name', 'name')  //

sss

p.setAtrribute('style', 'font-size: 15px') console.log(p.getAtrribute('data-name'))

③区别

property修改的并不会体现在HTML结构中,attribute会体现在HTML结构中,值得注意的是,两者都会引起DOM的重新渲染

对DOM节点的操作的属性有很多,上面只是讲解了常用部分,更多可以了解官方apihttps://www.runoob.com/jsref/dom-obj-all.html

1.2.3 DOM结构操作

(1)新增/插入节点 - appendChild

const div = document.getElementById('div')

const p = document.createElement("p")  //创建p
p.innerHTML = 'this is p'

div.appendChild(p)  //将p插入div,注意如果是对现有的元素进行append会产生移动

(2)获取子节点,父节点  - parentNode, ChildNodes

const p = document.getElmentById('p')

cosole.log(p.parentNode) //获取父节点

const div = document.getElementBy('div')
console.log(div.childNodes)  //获取子节点

这里需要注意的是,获取子节点的时候空格换行,文本节点也会存在哦~

(3)删除节点 - removeChild

const div = document.getElementById('div')
const p = document.getElementById('p')

div.removeChild(p)

同理,DOM的增删改查有很多种方法,这里只是列举了一些常见的方法,如果想要了解更多,可以查看文档api - https://www.runoob.com/jsref/dom-obj-attributes.html

1.2.4 DOM的性能 

DOM操作占用的cpu很多,频繁操作很容易卡顿。因此在vue,react这些框架中,其实就有自己的diff算法,去更新DOM,提高性能。

提升DOM的性能,我们能做些什么?

对DOM查询做缓存,将频繁操作改为一次性操作(插入多次,改成插入一次等)

1.3 面试题答案

 (1)DOM是哪种数据结构

DOM是一种数的结构

 (2)DOM操作的常见api

参考上面1.2.3

 (3)属性attr和property的区别

property修改的并不会体现在HTML结构中,attribute会体现在HTML结构中,值得注意的是,两者都会引起DOM的重新渲染

 (4)一次性插入多个DOM节点,考虑一下性能

const frag = document.createDocumentFragment() //创建文档碎片,其实是一个虚拟DOM
const div = document.getElementById('div')

for(let i = 0; i < 10; i++){
     const p = document.createElement('p')
     p.innerHtml = 'this p 
' frag.appendChild(p) } div.appendChild(frag) //真正插入真实DOM

2 .BOM操作

2.1 常见面试题

(1)如何识别浏览器的类型

(2)分析拆解url的各个部分

2.2 知识点

2.2.1 navigator

查看当前浏览器的信息

navigator.userAgent

谷歌浏览器的信息

2.2.2 screen

屏幕的高度,宽度

console.log(screen.width)
console.log(screen.height)

2.2.3 location

location有很多网址信息,可以去选择自己想要的

面试题总结 - js基础知识_第12张图片

2.2.4 history

history主要是对方法的使用,前进啊,后退啊,history.back()等等

面试题总结 - js基础知识_第13张图片

3.1 面试题答案

(1)如何识别浏览器的类型

navigator.userAgent

(2)分析拆解url的各个部分

location

3.事件绑定

事件绑定的一些api https://www.runoob.com/jsref/dom-obj-event.html

3.1 常见面试题

(1)编写一个通用的事件监听函数

(2)描述实践冒泡的流程

(3)无限下拉的图片列表,如何监听每个图片的点击?

3.2 知识点

3.2.1 事件绑定

let box = document.getElementById('box')

//监听事件绑定
box.addEventListener('click', () => {})

//直接绑定
box.onclick = function(){}

3.2.2 事件冒泡

事件冒泡:子级的事件会触发父级的(假设点击事件,父级也有点击事件,则会触发自己的点击事件,也会触发父亲的点击事件)

举个栗子

//结构

子元素

//js代码 let father = document.getElementById('father') let child = document.getElementById('child') father.addEventListener('click', () => { cosole.log('father') }) child .addEventListener('click', () => { cosole.log('child') })

当点击p,会打印child,father,这就是事件冒泡

事件冒泡的存在会在一些场景下影响原有逻辑,所以常常我们会阻止事件冒泡 - e.stopPropagation()

child .addEventListener('click', (e) => {
    //阻止事件冒泡
    e.stopPropagation()
    cosole.log('child')
})

阻止事件冒泡后,只会打印child

3.2.3 事件代理

一般是在瀑布流的时候,将子元素的事件绑定在父元素身上



let box = document.getElementById("box")
box.addEventListener('cilck', e => {
   e.prevenDfault() //阻止默认行为
   if(e.target.nodeName === A){
        alert(e.target.innerHTML)
   }
       

})

事件代理的好处是什么 - 代码简洁,减少浏览器内存占用(只绑定一次事件)

其实在jquery中,想想是不是对未来元素绑定事件也是绑定在父元素中的~

3.3 面试题答案

(1)编写一个通用的事件监听函数

//满足普通绑定和事件代理绑定,并且this永远指向触发元素
function bindEvent(el, type, selector, fn){
    if(fn == null){
        fn = selector
        selector = null
    }
    el.addEventListener(type, event => {
        const target = event.target
        if(selector){
             //触发元素是否与选择器相同,代理绑定
            if(target.matches(selecotr)){
                fn.call(target, event)
            }
        }else {
            fn.call(target, event)
        }
    })
}

(2)描述实践冒泡的流程

事件冒泡是基于DOM的树形结构,事件会顺着触发元素往上冒泡

(3)无限下拉的图片列表,如何监听每个图片的点击?

利用事件代理-如上例子

4.ajax

4.1 常见面试题

(1) 手写一个简易的ajax

(2) 跨域的常见方法

4.2 知识点

关于ajax的知识点,之前写过一篇基础使用和封装以及jquery的ajax: https://blog.csdn.net/qq_42625428/article/details/103214802

4.2.1 XMLHttpRequest

这里写一个基本的ajax的请求

get


//创建一个实例
const xhr = new XMLHttpRequerst()
//写入请求地址
xhr.open('GET', 'URL地址',  true) //异步请求,false是同步
//监听状态
xhr.onreadystatechange = function() {
      if(xhr.readyState === 4){
            if(xhr.status === 200){
                console.log(xhr.responseText)
            }
      }
}
xhr.send()

post

var xhr = new XMLHttpRequest();
    xhr.open("post",'./server/mydate.json');
    xhr.setRequestHeader('Content-Typ','application/x-www-form-urlencoded');  //设置请求头
    xhr.send('username=name&age=12'); //传入参数
    xhr.onreadystatechange = function(){
    if(xhr.readyState === 4 && xhr.status === 200){
          console.log(xhr.responseText);
        }
    }

区别:

1.post 比 get在传递的时候多了一个头部结构

2.传递参数的方式不同   get: 传递在地址的后面   www.baidu.com?参数=值&参数=值

                                       post: 传递在send()方法的里面   xhr.send('参数=值&参数=值')

3.get因为是地址传递 - 铭文传递 -不安全 - 传递的数据量小

4.2.2 状态码

2xx - 表示成功处理请求,如200

3xx - 需要重定向 301 302 304

4xx - 客户端请求错误,如404 403

5xx - 服务器端错误 500 501

4.2.3 跨域 - 同源策略和其解决方案

跨域的知识点,之前总结了一篇超级详细的博客,所以就不重复写了:https://blog.csdn.net/qq_42625428/article/details/108099755

4.2.4 ajax的工具

工具都是直接用就行了,还是比较简单,只是在这总结一下工具,具体的可以直接看官网

(1)jquery的ajax

(2)fetch - 

官网: https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch

面试题总结 - js基础知识_第14张图片

(3)axios

官网 - http://www.axios-js.com/zh-cn/docs/

对axios的总结 - https://blog.csdn.net/qq_42625428/article/details/103870313

4.3 面试题答案

(1) 手写一个简易的ajax


let obj = {
  url: './server/mydate.json',
  type: 'get',
  data: {
        name: 'aaa',
        age: 18
        }
  }
//结合一下promise
function ajax(obj) {
   return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequrest()
      //初始化参数
            let params = '';
            if (obj.data != ''){
                for (key in obj.data) {
                    params += `${key}=${obj.data[key]}&`;
                }
                params = params.substring(0, params.length - 1);
            }
            
            //接收传递类型并转为小写
            let type = obj.type.toLowerCase();
 
            xhr.open(type ,type == 'get' ? `${obj.url}?${params}` : obj.url);
            // post  - 设置请求头
            if (type === 'post') {
                xhr.setRequestHeader('Content-Typ', 'application/x-www-form-urlencoded');
            }
 
            type == 'get' ? xhr.send() : xhr.send(params);
 
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    let result = xhr.responseText;
                    resolve(result)
                }else {
                    reject('错误')
                }
            }
    })
}

ajax(obj).then(res => {}).catch(err => {})

(2) 跨域的常见方法

jsonp,cors,proxy等等

 

5.存储

5.1 常见面试题

(1)描述一下cookie,localStorage,sessionStorage的区别

5.2 知识点

web端的存储技术的总结 - https://blog.csdn.net/qq_42625428/article/details/103264536

5.2.1 cookie

本身是用于浏览器和server通讯,是被借用到本地存储。现在一般是用于身份标识

前端设置修改:

document.cookie = "a=200;b=100"

每一次是追加,不是覆盖,每次刷新,cookie是赋值后,不断刷新是继续存在的,因此可以利用来做本地存储

cookie只能存储4kb,并且是http请求的时候,cookie都会发送到服务端,增加了请求数据量

随意存储还是尽量不要用cookie

5.2.2  localStorage和sessionStorage

.这两个h5专门为存储设计的,最大可以存储5M

设置和获取

localStorage.setItem(key,value)
lcoalSotrage.getItem(key)

sessionStorage.setItem(key,value)
sessionSotrage.getItem(key)

区别也很简单 - sessionStorage是临时存储,关闭浏览器清空

                      - localStorage是永久存储,只能手动删除

5.3 面试题答案

(1)描述一下cookie,localStorage,sessionStorage的区别?

  容量: cookie只有4kb, 临时存储和永久存储5M

              cookie会被http请求发送到后端

              临时存储,关闭浏览器清空,localStorage只能手动清空

3.浏览器相关

前端的运行环境 - 浏览器(server端有nodejs) - 下载网页代码,渲染页面,并在次期间执行js,要保证代码在浏览器种稳定且高效

1 页面加载过程

1.1 常见面试题

(1)从输入url到渲染出页面的整个过程

(2)window.onload和DOMContentLoad的区别

1.2 知识点

1.2.1 加载资源的形式

前端加载资源 - 一般有htmld代码,媒体文件(图片,视频等等),js,css

1.2.2 加载资源的过程

DNS解析: 域名 -》 IP地址   (将域名转换为ip地址)

浏览器根据IP地址向服务器发起http服务(操作系统在发,三次握手等等)

服务器接收,处理http请求,并返回给浏览器

1.2.3 渲染页面的过程

根据html代码生成DOM Tree

根据css生成CSSOM(层叠样式表对象模型)

将DOM Tree 和 cssOM整合成renderDOM

根据Render Tree 渲染页面

遇到script标签,暂停渲染,优先加载并执行js代码(浏览器是单线程的 .js代码可能会改变css)

直到把Render Tree渲染完成

面试题总结 - js基础知识_第15张图片

1.3 面试题答案

(1)从输入url到渲染出页面的整个过程?

①dns解析域名为ip地址

②浏览器根据ip地址向服务器发起http请求

③建立连接,服务器处理请求并返回

④浏览器对返回解析,解析DOM树,解析css,构建render Tree

⑤遇到script标签,暂停渲染加载执行js,在进行渲染,直至全部渲染完成

(2)window.onload和DOMContentLoad的区别?

window,.onload是资源(图片等)全部加载完成才会执行

DOMContentLoad是DOM加载结束就执行

2 安全 

常见的web前端攻击方式有哪些?

2.1 XSS跨站请求攻击

什么是XSS攻击?

xss攻击通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。

举个栗子一篇博客网站,发表了一篇博客,其中嵌入了

当访问,就获取到cookie,cookie里面可能就含有你的登录信息

怎么预防?

替换特殊字符,如:将 < 变为 < , > 变为 gt;,这个替换谁做?前端可以在显示的时候做,后端在存储的时候做


    

xxx

xxx

<scipt>alert(document.cookie); </scipt>

自己做一般可以正则匹配,也可以去下载工具 - npm install xss

2.2 XSRF攻击

什么是XSRF攻击?

假设你在购物,看中了某个商品id100,付费接口是xxx.com/pay?id=100,但是你没有验证,现在攻击者也看中了一个商品id200,攻击者向你发送了一封邮件,邮件隐藏了(会带过去你的信息),你一看了这个邮件,你就付钱了

怎么预防?

使用post接口

增加验证,例如密码,短信验证码,指纹等等

 

三.实际应用

1.git

git的服务器,常见的github , coding,net等等,一般公司都有自己的服务器

git的相关知识点: https://blog.csdn.net/qq_42625428/article/details/104955488

这里就列举一些常用命令

git add .  
git checkout xxx  
git commit -m 'xxx'  
git push  
git pull
git branch
git checkout -b xxx
git merge xxx

2.调试工具

chrome调试工具 -》 Elements, Console, debugger, NetWork, Application

3.抓包

移动端h5页,查看网络请求,需要用工具抓包

windows一般使用fiddler,手机和电脑连同一个局域网,将手机代理到电脑上,手机浏览网页,就可以抓包,查看网络请求,网址代理,https

https://blog.csdn.net/csdnnews/article/details/88735774?utm_medium=distribute.pc_relevant_bbs_down.none-task--2~all~sobaiduend~default-1.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task--2~all~sobaiduend~default-1.nonecase

4.webpack和babel

webpakc打包工具,现在用的很多,像vue,react等等,都会使用到webpack,是前端模块化管理和打包工具,可以将许多松散的模块按照依赖和规则打包成前端资源, 还可以将模块按需加载。 通过loader的转换,任何形式的资源都可以视作模块。如, commonJS模块图片资源等

打包原理也很简单 - 根据入口文件-生成依赖图 - 转义形成css,js文件

首先ES6模块化,浏览器不支持,并且es6的语法,浏览器并不完全支持,所以babel用于转义,babel是提供一些插件为webpack使用

webpackl能压缩代码,整合代码,让网页加载更快

首先要有node环境,安装各个插件

html-webpack-plugin  html编译插件

webpack-dev-server  启动服务插件

 @babel/core   babel的核心组件组合

npm init
npm install webpack webpack-cli html-webpack-plugin webpack-dev-server -D
nom install @babel/core @babel/preset-env babel-loader -D

新建.bebal的配置文件(与package.json同级) - .babelrc(一个json文件)。写如下配置,之后再webpack.config.js中编写module模块

{
    "presets": ["@babel/preset-env"]
}

新建文件webpack的配置文件 webpack.config.js(与package.json同级)

cosnt path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin') //引入插件

module.exports = {
    mode: 'development', //模式 - 开发环境
    entry: path.join(__dirname, 'src', 'index.js'),//入口path拼接上src,index.js就是入口文件
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist')   //__dirname 项目的绝对路径
    },
    module: { //模块
         rules: [  //定义规则数组
             {
                test: /\.js$/, //以js结尾的
                loader: ['babel-loader'],  //bable-loader插件,将js的es6语法转义es5
                include: path.join(__dirname, 'src') ,  //哪些文件需要这个规则
                exclude: /node_modules/  //不需要转义的文件
             }
        ]
    },
    plugins: [  //插件配置
        new HtmlWebpackPlugin ({
            template: path.join(__dirname, 'src', 'index.html') , //模板
            filename: 'index.html' //根据模板产出的文件名
        })
    ],
    devServe: { //开启服务
        port: 3000,
        contentBase:  path.join(__dirname, 'dist')  //当前目录
    }

}

在pages.json中修改一下打包

{
    "scripts": {
        "build": "webpack --config webpack.config.js", //打包命令
        "dev": "webpack-dev-server"  //服务
    }
}

上面是devlopment是环境,下面在配置一下生产环境

新建文件webpack.prod.js

cosnt path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin') //引入插件

module.exports = {
    mode: 'production', //模式 - 生产环境下的代码会被压缩,体积小
    entry: path.join(__dirname, 'src', 'index.js'),//入口path拼接上src,index.js就是入口文件
    output: {
        filename: 'bundle.[contenthash].js', //contenthash,标识,代码内容变了,会随之改变
        path: path.join(__dirname, 'dist')   //__dirname 项目的绝对路径
    },
    module: { //模块
         rules: [  //定义规则数组
             {
                test: /\.js$/, //以js结尾的
                loader: ['babel-loader'],  //bable-loader插件,将js的es6语法转义es5
                include: path.join(__dirname, 'src') ,  //哪些文件需要这个规则
                exclude: /node_modules/  //不需要转义的文件
             }
        ]
    },
    plugins: [  //插件配置
        new HtmlWebpackPlugin ({
            template: path.join(__dirname, 'src', 'index.html') , //模板
            filename: 'index.html' //根据模板产出的文件名
        })
    ],

}
{
    "scripts": {
        "build": "webpack --config webpack.prod.js", //打包命令
    }
}

先讲一下contenthash 

contenthash会根据内容改变而改变,url和文件不变,会自动触发http缓存,返回304,就不会再更新这些url和文件不变的文件。

那么上线,不会重新下载,浏览器会去下载新的hash改变的文件

这里在顺便讲一下模块化

es6的模块化利用export暴露,import引入,不同的是,export default在一个文件只能有一个

export default {}  //对应 import xxx from ''
export const a = {}     //  import {a} from ''

5 性能优化

性能优化原则 - 多使用内存,缓存,减少cpu计算量,减少网络加载耗时(用空间换时间),从而让加载更快,让渲染更快

5.1 方法

让加载更快

减少资源体积:压缩代码(像webapack打包)

减少访问次数:合并代码(http请求很耗时,可以一次访问9kb(而不是三次请求3个3kb,), css的雪碧图),SSR服务器端渲染,缓存

使用更快的网络:cdn(就近访问服务器)

 让渲染更快

css放在head,js放在body下,尽早执行js,用DOMContentLoad触发,懒加载

对DOM查询进行缓存(DOM操作很消耗性能),合并一起插入DOM结构

节流与防抖

5.2 知识点

SSR

服务器端渲染 - 将网页和数据一起加载渲染、

非SSR(前后端分离):先加载网页,在加载数据

图片懒加载

移步一下 - https://blog.csdn.net/qq_42625428/article/details/89601507 (写的超级详细,懒加载的原理和实现)

缓存DOM查询,多个DOM一次性插入

缓存DOM查询,在之前已经提过了,前面还有一道面试题,看过前面肯定有印象

防抖和节流

防抖和节流也是,写在了另外一篇 - https://blog.csdn.net/qq_42625428/article/details/110800225 (什么是防抖和节流,他们的区别和实现)

 

常考题

(1)var和let const的区别

var会存在变量提升,let和const没有变量提升,是es6的语法

var和let定义的变量可以修改,cosnt是定义了就不能更改

let和const是块级作用域

详细了解 - https://blog.csdn.net/qq_42625428/article/details/90265505

(2)列举强制类型转换和隐式类型转换

强制类型转换 - parseInt,parseFloat,toString,Boolean等方法

隐式转换 -  ==,  做条件判断时,逻辑运算, +(拼接时候)

(3)手写深度比较,模拟lodash isEqual

利用递归,其实深度比较还可以直接转为值类型,JSON.stringify





    
    
    Document



    


(4)split()和join()的区别

split是拆分为数组,join是进行连接为字符串

'1-2-3'.split('-') //[1,2,3]
[1,2,3]'.join('-') //'1-2-3'

(5)数组的pop,push,unshift,shift分别是什么?

都会对原数组造成影响

pop是删除最后一个元素,返回的是删除元素

push数组末尾添加一个元素。返回的添加后的数组的长度

unshift是向数组的第一个添加元素,返回的添加后的数组的长度

shift是删除数组的第一个元素,返回的是删除的元素

(6)有哪些是数组的纯函数(不改变原数组,返回的是一个数组)

concat 连接数组

slice 截取数组

map 循环数组

filter 筛选

(7)[10,20,30].map(parseInt)返回的是什么?

[10, 20, 30].map((item, index) => {
    return parseInt(num, index) //Index 0,作为十进制处理,1,2不合法
 })  //[10,NaN,NaN]

(8)ajax的post和get的区别?

get传参参数是在是在地址栏,不安全,传递数据小,一般get用于查询操作

post一般是用于提交曹旭哦。易于防止CRSF

(9)事件代理和委托是什么?

(10)函数call和apply的区别?

前面其实已经提过了,他们只是传递参数不同的区别

fn.call(this, 1,2,3)
fn.apply(this, [1,2,3])

(11)闭包是什么?有什么特性?有什么影响?

闭包 - 嵌套函数使用了外部函数的变量

特性 -  闭包会保存值

影响 - 变量会常驻内存,得不到释放。

前面也详细的写过闭包,所以还不太清楚的,可以往前看看

(12)怎么阻止事件冒泡和默认行为?

e.preventDefault()
e.stopPropagation()

(13)解释jsop的原理,为何他不是真正的ajax?

ajax是XMLHttpRequest

jsop是通过script标签

(14)函数声明和函数表达式的区别?

function fn(){}
var fun = function(){}

函数声明会在代码执行之前预加载,函数表达式不会

(15)new Object()和Object.create()的区别?

{} 等同于new Object(),原型Object,prototype

Object,create(null)没有原型,Object,create({})指定一个原型

(16)this

面试题总结 - js基础知识_第16张图片

(17)作用域和自由变量的场景题

for(let i = 1; i <= 3; i++){
    setTimeout(function() {
        console.log(i)  
    }, 0) 
} 

打印出123

let a = 100;
function test(){
    alert(a)
    a = 10
    alert(a)
}

test()
alert(a)

100,10,10

(18)判断字符串以字母开头,后面字母数字下划线,长度为6-30

const reg = /^[a-zA-Z]\w{5,29}/

(19)手写字符串trim,保证浏览器兼容性

String.prototype.trim = function(){
    return this.replace(/^\s+/, "").replace(/\s+$/, "")
}

(20)如何获取多个数字中的最大值

function max() {
   let arr = Array.prototype.slice.call(arguments);
   let max = arr[0];
   arr.forEach(item => {
        if(item > max){
            max= item
        }
   })
   return max
}

max(10,20,30,40,5)


Math.max(10,52,42,10)

(21)如何使用js实现继承

class继承,前面写过就不重复写了

(22)如何捕获js程序中的异常

try{}catch(err){}  //手动捕获异常


//自动捕获
window.onerror = function(message, source, lineNum, colNum, err) {}
//对于跨域的js,不会有详细的报错信息
//对于压缩的js,还要配合sourceMap反查到未压缩的行,列

(23)获取地址栏的参数

function query(name) {
    const search = location.search.substr(1);
    const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i') //&key=value&
   
    let res = search.match(reg)

    if(res === null){
        return null
    }

    return res[2]

}


function query2(name) {
    const search = location.search
    cosnt p = new URLSearchParams(search); //注意兼容性问题
    return p.get(name)
}

(24)手写数组flatern,考虑多层级

var arr = [1,2, [3,4,50, [10,5]]] //将数组按顺序变为一维数组




function flat(arr) {
  
   cosnt isDeep = arr.some(item => item instanceof Array)

   if(!isDeep){
        return arr
   }
    
   cosnt res = Array.prototype.concat.apply([], arr)

   return flat(res)

} 

(25)数组去重

let arr = [1,5,10,5,1,20]

//第一种
[...new Set(arr)]


//方法遍历
function unique(arr){

  cost res = []
  arr.forEach(item => {
        if(arr.indexOf(item) < 0){
            res.push(item)
        }
   })

   return res
}

(25)介绍一下RAF requestAnimationFrame

  想要动画流畅,更新频率必须要60帧/s,即16.67ms更新一次视图

setTImeout要手动控制频率,而RAD浏览器会自动控制

后台标签或者隐藏在iframe中,RAF会暂停

#div {
    width: 100px;
    height: 100px;
    background: pink;

}

//已经引入jquery //js把宽度从100px变为640px, 增加540px, 60帧/s, 3秒180帧, 每次变化3px cosnt div1 = $("#div1") let curWidth = 100; const maxWidth = 640; //利用setTimeout来执行 function animate(){ curWidth = curWidth + 3; div1.css('width', curWidth) if(curWidth < maxWidth){ setTimeout(animate, 16.7) //自己控制时间 } } //利用requestAnimationFrame

 

写在最后:

    其实代码面试还是很公平的,厉害的人就能进大厂,工资高

   希望每个人都能有好的offer,其实对于答案,一开始很纠结是否要给出答案,因为其实在知识点里面已经包含了答案,纠结了很久,还是写了,觉得答案是一个参考吧,在面试过程中重要的是心态和对知识点的理解,其实问一个题,大多都是想了解你对这个知识点的掌握情况,因此在面试过程中要不仅仅是答题,要做衍生,结合自身的情况,将知识点讲的更透彻。码字不易,点个赞吧,哈哈~

 

 

 

你可能感兴趣的:(web前端,js,js面试题,js常见面试题,js基础知识总结,前端面试题)