2020-11-06 js面试题集合

2020-11-06 js面试题集合

1、节流与防抖函数
节流:在频繁触发事件的情况下,在指定的时间间隔内只执行一次函数

function throttle(fn, delay) {
  let last = 0;
  return function (...args) {
    let now = Date.now();
    if (now - last > delay) {
      fn.apply(this, args);
      last = now;
    }
  };
}

防抖:在频繁触发事件的情况下,只有任务触发的时间间隔大于指定的时间间隔时,函数才会执行

function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    let now = !timer;
    timer = setTimeout(() => {
      timer = null;
    }, delay);
    if (now) {
      fn.apply(this, args);
    }
  };
}

2、说下闭包
闭包是那些能够访问自由变量的函数,自由变量是指在函数中使用,但既不是函数参数又不是函数的局部变量的变量
闭包=函数+函数能够访问的自由变量

单例模式:

var Singleton = (function () {
  var instance;
  var CreateSingleton = function (name) {
    this.name = name;
    if (instance) {
      return instance;
    }
    this.getName();
    return (instance = this);
  };
  CreateSingleton.prototype.getName = function () {
    console.log(this.name);
  };
  return CreateSingleton;
})();

3、JS的数据类型
分为基本数据类型和引用数据类型
基本数据类型有string,number,boolean,null,undefined,symbol
引用数据类型有object,function,array等
他们的区别:
基本数据类型,是按值访问,值保存在栈中
引用数据类型,是保存存储的地址指针,值存储在堆内存中,指针存储在栈中,操作的是对象的引用

4、ES6的常用特性
promise、async/await、let、const、块级作用域、箭头函数、Set/Map

5、事件循环机制(Event Loop)
任务分为同步任务和异步任务,异步任务又分为宏任务和微任务,在一次任务循环中,先执行同步任务,遇到异步任务会放入Event Queue中注册回调函数等待,同步任务执行完后,先检查微任务队列,执行完所有微任务后,结束一个循环,在执行宏任务队列中的下一个任务,依次循环
macrotasks:setTimeout,setInterval,setImmediate,I/O,UI,rendering等
microtasks:process.nextTick,Promise,MutationObserver等

6、mouseover和mouseenter区别
mouseover:鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程,对应的移除事件是mouseout
mouseenter:鼠标移入元素本身(不包含子元素)触发事件,不会冒泡,对应的移除事件是mouseleave

7、原型和原型链
在ES6之前,JS是没有类和继承的概念,JS继承是通过原型来实现继承的,在JS中的构造函数默认带有一个prototype属性,这是一个对象,同时这个属性带有一个constructor属性,指向该构造函数,同时每个实例都会有一个__proto__属性指向该prototype对象,即原型对象;一方面任何一个对象都可以当其他对象的原型,另外一方面,原型对象也有自己的原型,因此会形成一个原型链,最终指向Object.prototype

8、JS判断类型的方法
typeof():string,number,boolean,undefined返回原始值,object和null返回object,function返回function
instanceof:A instanceof B,判断B是否在A的原型链上,除了null外任意对象都是Object的实例
Object.prototype.toString.call():返回一个对象的字符串形式

9、判断数组的方式
instanceof
Object.prototype.toString.call()
constructor
Array.isArray()

10、数组去重
1)ES6的Set去重,Array.from(new Set(array))
2)indexOf去重

const a = [1, 2, 5, 4, 2, 3, 1];
const newArr = [];
a.forEach(item => {
  if (newArr.indexOf(item) === -1) {
    newArr.push(item);
  }
});

3)Object键值对去重

function unique(arr) {
  const hasTable = {};
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!hasTable[arr[i]]) {
      hasTable[arr[i]] = true;
      res.push(arr[i]);
    }
  }
  return res;
}

11、对象数组去重方法

const a = [
  { name: "xx", age: 25 },
  { name: "ff", age: 26 },
  { age: 25, name: "xx" },
  { name: "xi", age: 24 }
];
function unique(arr) {
  const obj = {};
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    obj[arr[i].name] = arr[i];
  }
  for (const item in obj) {
    res.push(obj[item]);
  }
  return res;
}

12、let与const的区别
不存在变量提升
不能重复声明
生成块级作用域,只在它所在的代码块有效
暂时性死区,变量绑定的所在区域,不受外部影响
const声明一个只读的常量,一旦声明必须初始化,常量的值就不再变化

13、箭头函数和构造函数与普通函数的区别
构造函数与普通函数:
构造函数使用new命令,普通函数不用
构造函数内部使用this,普通函数一般不用
构造函数默认没有return返回值,普通函数一般都有
构造函数首字母大写,普通函数小写

箭头函数:
内部this指向的是定义时所在的对象,而不是使用时所在的对象
不可以当做构造函数,不能使用new命令
不可以使用arguments,不过可以使用rest参数代替
不可以使用yield命令,不能作为generator函数

14、new命令的过程
1)创建一个空对象,作为将要返回的对象实例
2)将空对象的原型指向构造函数的prototype属性
3)将空对象赋值给函数内部的this关键字
4)开始执行构造函数内部的代码

function create() {
  // 1、创建一个空对象
  var obj = new Object();
  // 2、获得构造函数并删除第一个参数
  var Con = [].shift.call(arguments);
  // 3、修改obj的原型
  Object.setPrototypeOf(obj, Con.prototype);
  // 4、绑定this,执行方法
  var ret = Con.apply(obj, [].slice.call(arguments));
  // 5、返回对象
  return ret instanceof Object ? ret : obj;
}

15、事件捕获、事件冒泡和事件委托(代理)
事件冒泡:是从目标元素到外层元素的过程,点击父元素或触发子元素的事件
事件捕获:是从外层元素到目标元素的过程,点击子元素会触发父元素的事件
事件委托:利用事件冒泡的原理,指定一个父元素绑定事件,减少事件处理程序

16、深拷贝与浅拷贝
浅拷贝如Object.assign()方法、数组的concat、slice等,如果是基本类型,就会拷贝一份,互不影响;如果是引用类型,则只是拷贝了数组或对象的引用,实际改变会发生影响
深拷贝
1)JSON.parse(JSON.stringify())这个方法有以下一个缺陷
会忽略undefined
会忽略symbol
不能处理函数
不能解决循环引用
不能正确处理Date类型
不能处理正则
2)简单的深拷贝函数实现

function cloneDeep(obj, map = new WeakMap()) {
  // 判断是否对象
  if (typeof obj !== "object" || obj === null) return obj;
  // 判断循环引用
  if (map.has(obj)) return map.get(obj);
  // 判断Date
  if (obj instanceof Date) return new Date(obj);
  // 判断正则
  if (obj instanceof RegExp) return new RegExp(obj);
  // 数组和对象能够当存储
  const result = Array.isArray(obj) ? [] : {};
  // 循环存储引用对象
  map.set(obj, result);
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === "object" && obj[key] !== null) {
      result[key] = cloneDeep(obj[key], map);
    } else {
      result[key] = obj[key];
    }
  });
  return result;
}

17、说下你对promise的理解
promise是异步编程 的一种方案,比传统的回调函数更合理和强大,简单来说可以把它看做是个容器,里面保存着某个未来才会结束的事件,通常是一个异步操作的结果
promise有两个特点:一是对象的状态不受外界影响,有三种状态pending,fulfilled,rejected,只有异步操作的结果可以决定当前是哪种状态;二是一旦状态改变,就不会再变,任何时候都可以得到这个结果,状态改变只有两种可能,从pending到fulfilled和pending到rejected
当然也有些缺点:首先无法取消promise,一旦新建就会立即执行,无法中途取消;其次,如果不设置回调函数,promise内部抛出的错误无法反应到外部;最后,当处于pending状态时,无法得知目前进展到哪个阶段

18、async/await:
原理就是generator语法糖+内置执行器
函数返回一个promise对象
await命令是内部then命令的语法糖
内部return语句返回的值会成为then方法回调函数的参数
必须等到所有的await后面的语句完成后才会发生状态的改变,除非遇到return或者抛出错误

19、数组的一些常用方法
1)Array.from():将可迭代对象和类数组对象转化为真实数组,返回一个新数组
可迭代对象:实现[Symbol.iterator]方法属性的对象,原生的Javascript对象没有此属性,无法使用for…of迭代方法
类数组对象:具有.length属性的对象如arguments对象
2)Array.isArray():判断传入的值是不是数组,返回true或false
3)concat(arr1,arr2…):合并两个或多个数组,返回一个新数组,不会改变原数组
4)filter(callback(element,index,array),thisArg):创建一个新数组,其包含所有通过测试函数的所有元素

// 使用reduce实现filter,返回的符合条件的元素
Array.prototype.mapUsingFilter = function (callback, thisArg) {
    return this.reduce(function (accumulator, element, index, array) {
      const result = callback.call(thisArg, element, index, array);
      if (result) {
        accumulator.push(element);
      }
      return accumulator;
    }, []);
  };

// filter的原生实现
Array.prototype.myFilter = function (callback, thisArg = undefined) {
  if (this == null) {
    throw Error("this不能是null或者undefined");
  }
  if (typeof callback !== "function") {
    throw new Error("error");
  }
  const result = [];
  for (let i = 0, len = this.length; i < len; i++) {
    callback.call(thisArg, this[i]) && result.push(this[i]);
  }
  return result;
};

5)flat(depth=1):数组扁平化,返回一个新数组不改变原数组

/**
 * 数组扁平化:使用reduce递归迭代
 */

function flatDeep(arr, d = 1) {
  return d > 0
    ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
    : arr.slice();
}

6)forEach(callback(currentValue,index,array),thisArg):对数组中的每个元素执行一次给定的函数, 未初始化的值不作处理,不改变原数组,返回undefined

// 用forEach实现flat
function flatten(arr) {
  const result = [];
  arr.forEach(i => {
    if (Array.isArray(i)) {
      result.push(...flatten(i));
    } else {
      result.push(i);
    }
  });
  return result;
}

7)join(separator):把一个数组或类数组转化为以separator连接的字符串,默认为,号连接
8)map(callback(currentValue,index,array),thisArg):创建一个新数组,其结果是该数组每个元素调用一次提供的函数返回的值

/**
 * map的原生实现
 */
Array.prototype.myMap = function (callback, thisArg) {
  if (this == null) {
    throw Error("this不能是null或者undefined");
  }
  if (typeof callback !== "function") {
    throw Error("callback必须是函数");
  }
  const result = [];
  for (let i = 0; i < this.length; i++) {
    result.push(callback.call(thisArg, this[i]));
  }
  return result;
};

// 用reduce实现map
Array.prototype.mapUsingReduce = function (callback, thisArg) {
    return this.reduce(function (accumulator, currentValue, index, array) {
      accumulator[index] = callback.call(thisArg, currentValue, index, array);
      return accumulator;
    }, []);
  };

9)push():将一个或多个元素添加到数组的末尾,返回数组的长度
10)pop():从数组中删除最后一个元素,并返回该值,此方法改变数组的长度
11)reduce(callback(acc,cur,index,array),initialValue):对数组中的每一个元素执行一次升序函数,将其结果汇总成一个单个返回值,如果没有初始值,则将第一个元素作为初始值
12)reverse():将数组中的元素位置颠倒后,返回该数组,此方法会改变原数组
13)shift():删除数组的第一个元素,并返回该元素值,没有则返回undefined更改数组的长度
14)unshift():将一个或多个元素添加到数组的开头,并返回数组的新长度
15)slice(begin,end):返回一个新数组,由begin和end决定的原数组的浅拷贝,原数组不改变
16)sort():使用原地算法对数组进行排序,默认采用字符串排序算法
17)splice(start,deleteCount,item1…):通过删除或替换现有元素或者原地添加新的元素来修改数组,并返回修改的内容以数组的形式,此方法改变原数组;如果deleteCount不传则代表删除整个数组

20、字符串的一些常用方法
1)charAt(index):从字符串中返回指定的字符,默认位置为0
2)includes(searchString,position=0):判断一个字符串是否在另一个字符串中
3)indexOf():查找字符串出现的第一次位置的索引值,没有返回-1
4)match(regexp):返回一个正则匹配的结果,如果正则带有g标志,则返回所有匹配的结果;否则返回第一个完整匹配及其相关的捕获组Array

持续更新…

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值