JS基础面试题

40道JS基础面试题

Featured image

JS基础

1、js的数据类型有哪些

答: 简单数据类型: number string boolean undefined null symbol
	复杂数据类型: object  function  array

2、返回false的情况有哪些

答: 0  ""   null  false  NaN  undefined  不成立的表达式

3、两个等号和三个等号的区别

== 表示是相等只比较值在比较时会将两边的值转成相同类型的值然后比较
   === 表示是全等不仅比较值也比较类型

4、null和undefined的区别

null 表示空值 没有获取到typeof null 返回"object"
   undefined 表示未定义声明没有值typeof undefined 返回"undefined"

5、JSON字符串和JS对象怎么相互转换(序列化)

答: JS对象转JSON字符串: JSON.stringify(对象)
    JSON字符串转JS对象: JSON.parse(字符串)

6、伪数组和真数组的区别

伪数组
1拥有length属性
2不具有数组的方法
3伪数组是一个Object真数组是Array
4伪数组的长度不可变真数组的长度是可变的

7、那些情况会得到伪数组

1参数 arguments
2DOM 对象列表比如通过 document.getElementsByTags 得到的列表)、childNodes也是伪数组
3jQuery 对象比如 $("div")

8、伪数组怎么转换为真数组

1let newArr = Array.protype.slice.call(伪数组)
2let newArr = Array.from(伪数组),ES6的新语法
3let newArr = [...伪数组]使用扩展运算符,也是ES6的语法

9、let、const、var的区别

1var声明变量存在提升提升当前作用域最顶端),let和const是不存在变量提升的情况
2var没有块级作用let和const存在块级作用域
3var允许重复声明let和const在同一作用域不允许重复声明
4var和let声明变量可以修改const是常量不能改变

10、for in 和 for of 的区别

1forin是遍历数组对象的key
2forof是遍历数组的value
例如
let arr = ["a","b"];
1for (let key in arr) {
console.log(key);  //0 1
}
2for (let value of arr) {
console.log(value);  //a b
}

11、说下typeof返回的数据类型

回答:
①:简单数据类型直接返回对应的英文,比如:number string boolean undefined symbol等,特殊情况, null 返回 object
②:复杂数据类型返回 object, 函数返回的是 function

12、请说说你对作用域链的理解?

回答
①:JavaScript中的作用域分为全局作用域、局部作用域、块作用域。多个嵌套的作用域形成作用域链。
②:作用域链描述了变量的查找机制,当在某个作用域中查找变量时,如果当前 作用域没有找到,则向上层作用域查找,上层没有,继续向上层查找,一直找到全局作用域。找到则使用该变量,没有找到则报错。

13、简单数据类型与复杂数据类型在内存上存储有什么区别?

回答:
①:基本类型主要为以下6种:Number、String、Boolean、Undefined、null、symbol

②:引用类型 Object、Array、Function

三、webapi

14、localStorage、sessionStorage和cookie的区别

答: 共同点: 都是保存在浏览器端,且同源的。
	区别: 
	1. 请求不同: 
		cookie 数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。
				sessionStorage 和 localStorage不会自动把数据发给服务器,仅在本地保存。
	2. 存储大小限制也不同: 
		cookie 数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。
		sessionStorage 和 localStorage虽然也有存储大小的限制,但比cookie大得多,sessionStorage约5M、localStorage约5M 。
	3. 数据有效期不同: 
		 sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持; 
		 localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;
		 cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。 
	4. 作用域不同:
		sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;
		localStorage在所有同源窗口中都是共享的;
		cookie也是在所有同源窗口中都是共享的。

15、请说下什么是重绘和重排(回流)?他们的区别是什么?

回答:
重排: 也叫做回流,当DOM元素影响了元素的几何属性(例如宽和高),浏览器需要重新计算元素的几何属性,同样其它元素的几何属性也会和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程称为“重排”。
重绘: 完成重排后,浏览器会重新绘制受影响的部分到屏幕上中,该过程称为“重绘”。
总结:
当我们改变DOM的大小,增加删除都会导致重排,当给DOM元素改变颜色的时候,会导致重绘,重排一定会重绘,重绘不会重排。重排会影响性能,所以我们尽快能的减少重排的操作(这句话是重点)

16、事件流分为哪两个阶段?

事件流是指事件传播的顺序,由事件捕获 => 目标事件 => 事件冒泡
所以事件流分为捕获和冒泡两个阶段。

17、事件委托的实现原理?如果获取当前触发的dom节点?

回答:
①:事件委托主要利用事件冒泡特性来实现的。
②:事件委托(事件委派)主要有2个使用场景:

使用 e.target 获取当前触发事件的元素。

18、如何阻止冒泡和默认行为

: 阻止冒泡和捕获  e.stopPropagation()
    阻止默认行为   e.preventDefault()   return false
    注意addEventListener注册的事件在高浏览器版本中return false将没有效果必须要用事件对象

19、原生注册事件的方式有哪些?区别是什么

: 注册方式
		  1. on + 事件名称
		  2. addEventListener
		区别: 
			1. 使用on注册事件,同一个元素只能注册一个同类型事件,否则会覆盖
			2. addEventListener可以注册同一事件多次,不会被覆盖

四、JS高级

20、说说你对this的理解

回答:
this是个关键字,它的指向和函数的调用方式有关, 初步的理解就是,this指向所在函数的调用者。
1、 以函数形式调用时, this 永远都是 window
2、 以方法的形式调用时, this 是调用方法的对象
3、 以构造函数的形式调用时, this 是新创建的那个对象
4、 使用 call 和 apply 调用时, this 是指定的那个对象
5、 箭头函数: 箭头函数的 this 按照普通变量对待。比如把他当做 x 变量即可,然后按照作用域链找就行了。
6、全局中的this是window
7、事件处理函数,如果是普通的function函数,则this指向事件源。

21、new操作符做了什么

答:  1. 创建一个新对象
	2. 函数内部的this指向这个对象
	3. 执行函数体
	4. 自动返回这个函数

22、什么是深拷贝什么是浅拷贝

答: 浅拷贝: 拷贝对象的一层属性,如果对象里面还有对象,拷贝的是地址, 两者之间修改会有影响,适用于对象里面属性的值是简单数据类型的.
    深拷贝: 拷贝对象的多层属性,如果对象里面还有对象,会继续拷贝,使用递归去实现.

23、如何实现深拷贝和浅拷贝

浅拷贝:
Object.assign()
{ …obj }
lodash 库的 clone() 方法
手写循环
深拷贝:
lodash 库的 cloneDeep() 方法
JSON.parse(JSON.stringify(被拷贝的对象)) – 有缺陷,不能拷贝函数
手写循环 + 递归

24、如何用递归实现深拷贝?

function copy(oldD) {
    let newD  // 新数据
    // 需要判断oldD 何方数据类型?
    // ************************************数组
    if(oldD instanceof Array){
      newD = []
      for (let i = 0; i < oldD.length; i++) {
        let val = oldD[i]  // 每一项数据
        val = copy(val) // val
        newD.push(val)
      }
    }
    // ***********************************对象
    else if(oldD instanceof Object){
      newD = {}
      for (let key in oldD) {
        let val = oldD[key]  // 每一项数据,有可能又是简单数组
        val = copy(val)  // val不能直接赋值,需要经过copy
        newD[key] = val  // 添加键值对
      }
    }
    
    // ***********************************简单数据
    else {
      newD = oldD
    }
    return newD 
  }

25、描述下垃圾回收机制?

首先:垃圾回收机制内部算法:引用计数 和 标记清除
接下来:

26、对闭包是怎么理解的?

答:

27、什么是原型对象和原型链

答:
原型对象:每个构造函数都有prototype属性,这个属性的值是个对象,称之为原型对象!
原型链:对象都有__proto__属性,这个属性指向它的原型对象,原型对象也是对象,也有__proto__属性,指向原型对象的原型对象,这样一层一层形成的链式结构称为原型链.

28、原型链有什么意义?

总得来说:给实例化对象上调用方法或使用某个属性,提供查询机制!
具体查询:
先在实例化对象本身上去找该方法!
如果没有,去实例化对象的__proto__(原型对象上去找)
如果当前原型对象上还没有该方法,继续往原型对象上的原型对象上去找
如果还是没有找到,报错 对象.xxx is not function

29、call、apply和bind的区别

答:
1、call和apply方法都可以调用函数,方法内的第一个参数可以修改this的指向
2、call方法可以有多个参数,除了第一个参数,其他参数作为实参传递给函数;apply方法最多有2个参数,第二个参数是个数组或伪数组,数组里面的每一项作为实参传递给函数
3. bind方法不能调用函数,它会创建一个副本函数,并且该新函数的this指向已经被改变!

30、怎么理解函数的防抖和节流

防抖:就是指触发事件后,在n秒后对应的函数会执行!如果在n秒中又触发了事件,则会重新计算函数执行时间。
节流:就是指连续触发事件,如果上一次触发的对应的执行函数没有执行完成,下一次触发是不会引起对应代码执行的

31、es6-es10新增常用方法

: 
es6:
1letconst
2解构赋值  let { a, b } = { a: 1, b: 2 }
3箭头函数
4字符串模板
5扩展运算符
6数组方法mapfilter等等
7class关键字
8promise
9函数参数默认值 fn(a = 1) {}
10对象属性简写 let a = 1; let obj = {a}
11模块化import--引入exprot default--导出

es7:
1includes()方法用来判断一个数组是否包含一个指定的值根据情况如果包含则返回true否则返回false

es8:
1async/await

es9
1Promise.finally() 允许你指定最终的逻辑

es10:
1数组Array的flat()和flatmap()
   flat:方法最基本的作用就是数组降维
      var arr1 = [1, 2, [3, 4]];
            arr1.flat(); 
            // [1, 2, 3, 4]

        var arr3 = [1, 2, [3, 4, [5, 6]]];
        arr3.flat(2);
        // [1, 2, 3, 4, 5, 6]

        //使用 Infinity 作为深度,展开任意深度的嵌套数组
        arr3.flat(Infinity); 
        // [1, 2, 3, 4, 5, 6]
   flatmap:方法首先使用映射函数映射(遍历)每个元素然后将结果压缩成一个新数组

32、怎么理解同步和异步

	1javascript是单线程单线程就意味着所有任务需要排队前一个任务结束才会执行后一个任务如果前一个任务耗时很长后一个任务就不得不一直等着于是就有一个概念——任务队列
	2同步任务顾名思义就是立即执行的任务同步任务一般会直接进入到主线程中执行而异步任务就是异步执行的任务比如ajax网络请求setTimeout 定时函数等都属于异步任务异步任务会通过任务队列( Event Queue )的机制来进行协调

33、js的运行机制是什么

js是单线程执行的页面加载时会自上而下执行主线程上的同步任务当主线程代码执行完毕时才开始执行在任务队列中的异步任务具体如下  
    1.所有同步任务都在主线程上执行形成一个执行栈
    2.主线程之外还存在一个"任务队列(eventloop队列或者消息队列)"只要异步任务有了运行结果就在"任务队列"之中放置一个事件
    3.一旦"执行栈"中的所有同步任务执行完毕系统就会读取"任务队列"看看里面有哪些事件哪些对应的异步任务于是结束等待状态进入执行栈开始执行
    4.主线程不断重复上面的第三步

34、怎么理解面向对象

1面向对象是一种软件开发的思想和面向过程是相对应的就是把程序看作一个对象将属性和方法封装其中以提高代码的灵活性复用性可扩展性
  2面向对象有三大特性封装继承多态
       封装把相关的信息无论数据或方法存储在对象中的能力
       继承由另一个类或多个类得来类的属性和方法的能力
       多态编写能以多种方法运行的函数或方法的能力
   3js中对象是一个无序的数据集合或者也可以说是属性和方法的集合可以动态的添加属性可方法
   4js是基于对象但是也使用了嵌入了面向对象的思想可以实现继承和封装这样也可以提供代码的灵活性和复用性

35、异步函数有哪些

JavaScript 中常见的异步函数有定时器事件和 ajax 

36、数组如何进行降维(扁平化)

1利用Array.some方法判断数组中是否还存在数组es6展开运算符连接数组
       let arr = [1,2,[3,4]]
        while (arr.some(item => Array.isArray(item))) {
            arr = [].concat(...arr);
        }
2使用数组的concat方法
    let arr = [1,2,[3,4]]
     let result = []
     result = Array.prototype.concat.apply([], arr)

3 使用数组的concat方法和扩展运算符
    var arr = [1,2,[3,4]]
    var result = []
    result = [].concat(...arr)
        
4es6中的flat函数也可以实现数组的扁平化
    let arr = [1,2,['a','b',['','',[1,2,3,[11,21,31]]]],3];
    let result = arr.flat( Infinity )
    注意flat方法的infinity属性可以实现多层数组的降维

37、什么是作用域链

	1作用域分全局作用域和局部作用域
    2在访问一个变量时首先在当前作用域中找如果找不到再到外层作用域中找这样一层一层的查找就形成了作用域链
    3如果没有找到会返回undefined

38、数组去重的方式

1第一种方式利用indexof方法
  let arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
  let newArr = []
  for (let i = 0; i < arr.length; i++) {
     if (newArr.indexOf(arr[i]) === -1) {
       newArr.push(arr[i])
     }
  }

2第二种方式sort()方法
   let arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
   arr.sort()
   let newArr = [arr[0]]
   for (let i = 1; i < arr.length; i++) {
     if (arr[i] !== newArr[newArr.length - 1]) {
       newArr.push(arr[i])
     }
   }

3第四种方式利用ES6 Set去重
 let arr = [2, 8, 5, 0, 5, 2, 6, 7, 2, 8]
 let newArr = new Set(arr)

39、斐波那契数列

  // 斐波那契数列
  function fiB(n) {
    if (n == 1 || n == 2) {
        return 1
      }
        return fn(n - 1) + fn(n - 2)
   }
    console.log(fiB(10))

40、你能说说怎么理解事件循环机制的?

回答:
1、JavaScript 是一门单线程语言. 单线程可能会出现阻塞的情况,所js分了同步任务和异步任务。
2、同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入 任务队列。
3、主线程内的任务执行完毕为空,会去 任务队列 读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。