JS

面试题整理

1. var let const

  • var - ES5,变量提升,没有块级作用域
  • let const - ES6,有块级作用域

2. typeof 返回哪些类型

  • undefined string number boolean symbol
  • object (注意,typeof null === ‘object’)
  • function

3. 强制类型转换 / 隐式类型转换

  • parseInt parseFloat toString
  • if、逻辑运算、==、+拼接字符串

4. 深度比较,模拟lodash、isEqual

function isObject(obj) {
  return typeof obj === "object" && obj !== null;
}
function isEqual(obj1, obj2) {
  if (!isObject(obj1) || !isObject(obj2)) {
    // 值类型(一般参与比较的不是函数)
    return obj1 === obj2;
  }
  if (obj1 === obj2) {
    return true;
  }

  //两个都是对象或数组
  // 1. 先取出obj1和obj2的keys,比较个数
  const obj1Keys = Object.keys(obj1);
  const obj2Keys = Object.keys(obj2);
  if (obj1Keys.length !== obj2Keys.length) {
    return false;
  }
  // 2. 以obj1为基准,和obj2一次递归比较
  for (let key in obj1) {
    // for in 适用于对象和数组
    // 比较当前key的val 递归
    const res = isEqual(obj1[key], obj2[key]);
    if (!res) {
      return false;
    }
  }
  // 3. 全相等
  return true;
}
const obj1 = {
  a: 100,
  b: {
    x: 100,
    y: 200,
  },
};
const obj2 = {
  a: 100,
  b: {
    x: 200,
    y: 200,
  },
};
console.log(isEqual(obj1, obj2));

5. split() join()

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

6. pop push unshift shift

  • 纯函数:1. 不改变原数组(没有副作用)2. 返回一个数组
    concat map filter slice
const arr1 = arr.filter(num => num > 25);

非纯函数:

  • pop push unshift shift forEach splice

7. slice splice

const arr = [10, 20, 30, 40, 50];
// slice 剪切  纯函数
const arr1 = arr.slice(); // 返回原数组
const arr2 = arr.slice(1, 4); // [20, 30, 40]
const arr3 = arr.slice(2); // [30, 40, 50]
const arr4 = arr.slice(-2); // 截取最后两个 [40, 50]

// splice 剪接  非纯函数
const spliceRes = arr.splice(1, 2, "a", "b", "c"); 
// arr = [10, "a", "b", "c", 40, 50]
// spliceRes1 = [20, 30]
const spliceRes1 = arr.splice(1, 2); 
// arr = [10, 40, 50]
// spliceRes1 = [20, 30]
const spliceRes2 = arr.splice(1, 0, "a", "b", "c");
// arr = [10, "a", "b", "c", 20, 30 40, 50]
// spliceRes2 = []

8. [10,20,30].map(parseInt)返回结果是什么

const res = [10, 20, 30].map(parseInt);

// 拆解
res = [10, 20, 30].map((n, idx) => {
  return parseInt(n, idx);
});
// parseInt 函数返回的是十进制结果
// 第二个参数的确是进制,是表明以该进制解析第一个参数。
//如果第二个参数没有或者为0,应该是按浏览器默认设置去解析(一般都会默认十进制,但有些环境可能会有偏差,比如把0开头的数值字符串解析为八进制数)。
//例如 parseInt(10, 0) 以十进制来说自然就是 10 。
//但是如果是 parseInt(30, 2) 30 不是 二进制的数,所以会返回 NaN 。

//一般做进制转换,都会先用 parseInt 转换为十进制,然后用 toString 转换为其它进制。

9. ajax请求get post区别?

  • get 一般用于查询,拼接在url上,
  • post 一般用于提交,放在请求体内
  • 安全性:post易于防止CSRF

10. call apply

fn.call(this, p1, p2, p3);
fn.apply(this, arguments);

11. 事件代理(委托)是什么?

冒泡

12. 闭包?有什么特性?有什么负面影响?

  • 回顾作用域和自由变量
  • 回顾闭包应用场景:作为参数被传入,作为返回值被返回
  • 回顾:自由变量的查找,要在函数定义的地方(而非执行的地方)
  • 影响:变量会常驻内存,得不到释放,闭包不要乱用(内存泄露一般是bug导致的)

13. 如何阻止事件冒泡和默认行为?

event.stopPropagination();
event.preventDefault();

14. 查找、添加、删除、移动DOM节点的方法?

document.getElementsById();
const p1 = document.querySelector('p');
p1.className = 'red';

p1.setAttribute('data-name', 'yyy');
p1.getAttribute('data-name');

const newP = document.createElement('p');

// 插入节点(将新节点appendChild)
div2.appendChild(newP);
// 获取节点(将已存在节点appendChild)
div2.appendChild(p1);

// 获取父元素
p1.parentNode;
// 获取子元素
p1.childNodes;
// 删除子元素
p1.removeChild(xxxx)

15. 如何减少DOM操作

  • 缓存DOM查询结果
  • 多次操作,合并到一起一次操作
const frag = document.createDocumentFragment();
for(){
	frag.appendChild(i);
}

16. 解释jsonp原理,为何它不是真正的ajax

  • 浏览器的同源策略(服务端没有同源策略,nginx叫转发)和跨域
  • 哪些html标签能绕过跨域?
    img script
    在这里插入图片描述

17. document load和ready的区别

window.addEventListener("load", function () {
  //页面的全部资源加载完成才会执行,包括图片、视频等
});
document.addEventListener("DOMContentLoaded", function () {
  // DOM渲染完即可执行,此时图片、视频可能还没有加载完
});

18. ===== 的区别

  • == 会类型转换
  • 应用 == 的场景

19. 函数声明和函数表达式的区别

  • 函数声明function fn(){},会变量提升
  • 函数表达式const fn = function(){}

20. new Object() 和 Object.create()区别

  • {}等于new Object(), 原型Object.prototype
  • Object.create(null)没有原型,因为Object.create({…})可以指定原型
const obj1 = {
  a: 10,
  b: 20,
  sum() {
    return this.a + this.b;
  },
};
const obj2 = new Object({
  a: 10,
  b: 20,
  sum() {
    return this.a + this.b;
  },
});

const obj3 = new Object(obj1);
console.log(obj1 === obj2); // false
console.log(obj1 === obj3); // true
const obj3 = Object.create(null); // {} 没有原型
const obj4 = new Object() // {}

在这里插入图片描述
Object.create创建了一个空对象,然后把传入的原型指向当前对象

21. this的场景题

在这里插入图片描述

22. 关于作用域和自由变量的场景题 - 1

在这里插入图片描述

23. 判断字符串以字母开头,后面数字下划线,长度6-30

  • const reg = /^[a-zA-Z]\w{5,29}$/
  • ^ 表示字符串的开头
  • []表示字符串的选择
  • \w命中数字字母下划线
  • {}范围
  • $结束
// 邮政编码  6位
/\d{6}/

// 小写英文字母 + 一次到多次
/^[a-z]+$/

// 英文字母
/^[a-zA-Z]+$/

// 日期格式 \d{1,2} 一到两位的数字
/^\d{4}-\d{1,2}-\d{1,2}$/

// 用户名
/^[a-zA-Z]\w{5,17}$/

// ip
/\d+\.\d+\.\d+\.\d+/

正则表达式30分钟入门教程

24. 手写trim方法,保证浏览器兼容性

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

25. 如何获取多个数字中的最大值

Math.max 或:

function max() {
  const nums = Array.prototype.slice.call(arguments); //改变为数组
  let max = 0;
  nums.forEach((n) => {
    if (n > max) {
      max = n;
    }
  });
}

26. 如何用js实现继承

  • class继承
  • prototype继承

27. 如何捕获js中的异常

// 一
try {
  // todo
} catch (ex) {
  console.error(ex); // 手动捕获catch
} finally {
  // todo
}
// 二 自动捕获
/**
 *
 * @param {*} message 报错信息
 * @param {*} source 源码
 * @param {*} lineNum 行号
 * @param {*} colNum 列号
 * @param {*} error 错误栈,抛出
 */
window.onerror = function (message, source, lineNum, colNum, error) {
  // 第一,对跨域的js,如CDN的,不会有详细的报错信息
  // 第二,对于压缩的js,还要配合sourseMap反查到未压缩代码的行、列
};

28. 什么是json?

  • 一种数据格式,本质是一段字符串
  • 格式和js对象结构一致,对js语言友好
  • window.JSON是一个全局对象:JSON.parse JSON.stringify
  • key要用双引号引起来

29. 获取当前页面url参数

  • 传统方式,查找location.search
  • 新API,URL
// 传统方式
// url: 127.0.0.1/pages/info?a=10&b=20&c=30
function query(name) {
  const search = location.search.substr(1); // 类似数组的slice,截取问号后的参数
  // search: 'a=10&b=20&c=30'
  const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, "i");
  const res = search.match(reg);
  if (res == null) {
    return null;
  }
  console.log(res);
  return res[2];
}
query("b");

在这里插入图片描述

// URLSearchParams
function query(name) {
  const search = location.search;
  const p = new URLSearchParams(search); // 注意浏览器兼容
  return p.get(name);
}
console.log(query("b"));

30. 将url参数解析为js对象

// 传统方式
function queryToObj() {
  const res = {};
  const search = location.search.substr(1);
  search.split("&").forEach((i) => {
    const arr = i.split("=");
    const key = arr[0];
    const val = arr[1];
    res[key] = val;
  });
  return res;
}
// URLSearchParams方式
function queryToObj() {
  const res = {};
  const p = new URLSearchParams(location.search);
  p.forEach((item, index) => {
    res[index] = item;
  });
  return res;
}

31. 手写数组flatern,考虑多层级(排平)

function flat(arr) {
  // 验证arr中还有没有深层数据
  const isDeep = arr.some((item) => item instanceof Array);
  if (!isDeep) {
    return arr;
  }
  const res = Array.prototype.concat.apply([], arr);
  // 或
  // Array.prototype.concat.call([], ...arr);
  // 或
  // [].concat(...arr);
  return flat(res);
}
const res = flat([1, 2, [3, 4, [67, 89]], 5]);
console.log(res);

32. 数组去重

// 传统方式
function unique(arr) {
  const res = [];
  arr.forEach((item) => {
    if (res.indexOf(item < 0)) {
      res.push(item);
    }
  });
  return res;
}

// set (无需结构,不能重复),效率更高
function unique(arr) {
  const set = new Set(arr);
  return [...set];
}

33. 手写深拷贝

在这里插入图片描述
Object.assign不是深拷贝,第一层级的浅层拷贝,有副作用,层级深的话就不是深拷贝了,更改一个另一个也会变

34. 介绍RAF requestAnimationFrame

  • 想要动画流畅,更新频率要60帧/s,即16.67ms更新一次视图
  • setTimeout要手动控制频率,而RAF浏览器会自动刷新
  • 后台标签或隐藏iframe中,RAF会暂停,而setTimeout依然执行
// 3s把宽度100px变为640px,即增加540px
// 60帧/s,3s 180帧,每次变化3px
const $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
function animate() {
  curWidth = curWidth + 3;
  $div1.css("width", curWidth);
  if (curWidth < maxWidth) {
    window.requestAnimationFrame(animate); // 时间不用自己控制,浏览器会帮运行,如果切换别的标签就会自动停止
  }
}

animate();

35. 前端性能如何优化?从哪几方面考虑?

36. 如何理解async

  1. 是Generator的语法糖
  2. 是解决异步的问题
  3. 返回的是一个Promise函数
  4. 与await搭配使用
  5. 一般用try catch搭配
  6. 嵌套低,代码可读性强
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐