JS面试题总结

219 阅读5分钟

1. 延迟加载JS有哪些方式?

defer:等html全部解析完成,才会执行js代码,顺次执行js脚本。
async:async是和html解析同步的,不是顺序执行js脚本(谁先加载完谁先执行)

2. JS数据类型有哪些?

基本数据类型:string、number、boolean、undefined、null、symbol。
引用数据类型:object。
NaN是一个数值类型,但是不是一个具体的数字。

3. null和undefined的区别?

  1. 作者在设计js的时候先设计的null(借鉴了java的语言)。
  2. null会被隐式转换成0,很不容易发现错误。
  3. 先有null后有undefined,出来undefined是为了填补之前的坑。 具体区别:Javascript的最初版本是这样区分的:null是一个表示“无”的对象(空对象指针),转为数值为0;undefined是一个表示“无”的原始值,转为数值时为NaN。

4. ==和===有什么不同?

  1. ==:比较的是值,通过valueOf转换,valueOf()方法通常由JavaScript在后台自动调用,并不显式地出现在代码中。
  2. ===:除了比较值,还比较类型。

5. Js微任务和宏任务:

  1. js是单线程语言。
  2. js代码执行流程:同步执行完=》事件循环。(同步的任务都执行完了,才会执行事件循环的内容)
  3. 事件循环中包含微任务和宏任务。
    微任务:Promise.then catch finally $nextTick()……
    宏任务:setTimeout,setInterval,ajax,fetch……
    要执行宏任务的前提是清空了所有的微任务。 流程:同步==》事件循环【微任务和宏任务】==》微任务==》宏任务==》微任务……

6. Js作用域考题:

  1. 除了函数外,Js是没有块级作用域。
  2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
  3. 注意声明变量是用var还是没有写(window.)。
  4. Js有变量提升的机制(变量悬挂声明)。
  5. 优先级:变量提升 < 传参 < 函数体 < 首行赋值。 6.1 变量提升:
function fn(){ 
    console.log(a) 
    var a = 1
} 
fn() //输出为 undefined

6.2 传参:

function fn(a){ 
    console.log(a) 
    var a = 1 
} 
fn(2) //输出为2

从而可以得出,变量提升的优先级低于函数传参。

6.3 函数体:

function fn(a){ 
    console.log(a) 
    var a = 1 
    function a(){ 
    return 2 
    } 
} 
fn(3) //此时输出为 a函数体

从而可以得出,内部函数体优先级高于函数传参。

6.4 首行赋值:

function fn(a){ 
    var a = 4
    console.log(a) 
    var a = 1 
    function a(){ 
        return 2
    } 
} 
fn(3) //输出为4

7. js的new操作符到底做了什么?

  1. 创建了一个空的js对象(即{})。
  2. 将空对象的原型__prototype__指向构造函数的原型。
  3. 改变this指向,并将剩余的参数传入。
  4. 对构造函数有返回值的判断。
function Person(name){
  this.name = name;
  return 1; // return undefined/NaN/'string'/null
}
let me = new Person('快乐每一天');
console.log(me); // { name:'快乐每一天' }
function Person(name){
  this.name = name;
  return { age:12 };
}
let me = new Person('快乐每一天');
console.log(me); // { age:12 }

在new的时候,会对构造函数的返回值做一些判断:
1、如果返回值是基础数据类型,则忽略返回值;
2、如果返回值是引用数据类型,则使用return 的返回,也就是new操作符无效;

8. Js对象考题:

  1. 对象是通过new操作符构建出来的,所以对象之间不相等。(除了引用外)
  2. 对象注意:引用类型(共同一个地址)。
  3. 对象的key都是字符串类型。
  4. 对象如何查找属性、方法:
    先在对象本身找===》构造函数中找===》对象原型中找(_proto_)===》构造函数原型中找(prototype)===》对象上一层原型中查找。
[1,2,3] === [1,2,3] //false
var a = {}
var b = {
    key:"a"
}
var c = {
    key:"c"
}

a[b] = '123';
a[c] = '456';

console.log(a[b]);  //456

9. Js作用域+this指向+原型考题:

function Foo(){
    getName = function(){ //注意是全局的window
        console.log(1) 
    }
    return this;
}

Foo.getName = function(){
    console.log(2)
}
Foo.prototype.getName = function(){
    console.log(3)
}
var getName = function(){
    console.log(4)
}
function getName(){
    console.log(5)
}

Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo().getName(); // 3

10. Js判断变量是不是数组的方法:

  1. 方法一:isArray
var arr = [1,2,3];
console.log( Array.isArray( arr ) )
  1. 方法二:instanceof
var arr = [1,2,3];
console.log( arr instanceof Array )
  1. 方法三:原型prototype
var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr)) // [object Array]
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 )
  1. 方法四:isPrototypeOf()
var arr = [1,2,3];
console.log( Array.prototype.isPrototypeOf(arr) )
  1. 方法五:constructor
var arr = [1,2,3];
console.log( arr.constructor.toString().indexOf('Array') > -1 )

11. 闭包:

1. 闭包是什么?

① 函数嵌套函数     ② 函数内部可以引用外部的参数和变量     ③ 参数和变量不会被垃圾回收机制回收

2. 闭包可以解决什么?

① 内部函数可以访问到外部函数的局部变量。 ② 避免全局变量的污染。

3. 闭包的缺点?

① 变量会贮存在内存中,造成内存消耗问题。 解决:把闭包的函数设置为null。
② ie浏览器会造成内存泄漏的问题。

12. 原型链:

1. 原型可以解决什么问题?

对象共享属性和共享方法。

2. 谁有原型?

① 函数拥有:prototype。 ② 对象拥有:_ _proto _ _。

3. 对象查找属性或方法的顺序?

先在对象本身找 --> 构造函数本身找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找。

4. 原型链?

① 是什么?就是把原型串联起来。
② 原型链的最顶端是null。

13.说一下call、apply、bind的区别:

共同点:功能一致

① 可以改变this指向。
② 语法:函数.call()、函数.apply()、函数.bind()

区别

① call、apply可以立即执行。bind不会立即执行,因为bind返回的是一个函数需要加入()执行。
② 参数不同:apply第二个参数是数组。call和bind有多个参数需要挨个写。

14.图片预加载:

我们可以利用浏览器的缓存机制,多个页面访问同一张地址的图片,只会请求图片一次。
可以在A页面偷偷加载B页面的图片。

//创建单个图片加载的方法-包成一个promise 
const imgPreloader = url => {
  return new Promise((resolve, reject) => {
    let image = new Image();
    image.src = url
    image.onload = () => {
      resolve('图片加载成功');
    };
    image.onerror = () => {
      reject('图片加载出错');
    };
  });
}

/**
 * 遍历图片路径,利用promise.all进行并行响应
 * @param imgs 图片路径数组
 * @returns {Promise<unknown[]>}
 */
const allImgPreloader = imgs => {
  let promiseArr = []
  imgs.forEach(src => {
    console.log(28, src)
    promiseArr.push(imgPreloader(src))
  })
  return Promise.all(promiseArr)
}

export default allImgPreloader