前端面试题

window 对象 和 document 对象

window 对象:代表浏览器中一个打开的窗口
document 对象:代表整个 HTML 文档,可以用来访问页面中的所有元素,且每个载入浏览器的 HTML 文档都是 document 对象
document 对象是 window 对象的一部分,可以有 window. document 来访问

javascript 中的 this 指的对象是什么?

查看 JavaScript 中 this 指向的对象,this 指向的一句话法则:

永远指向其所在函数的所有者,如果没有所有者时,指向 window。

理解 this 的要点:关键在于将函数与函数名分开看待。同一个函数,在不同的执行方法下,会有不同的效果。

1)全局函数中的 this 指向

2)对象方法中的 this 指向

3)绑定函数时的 this

4)绑定函数时的 this

5)鼠标单击事件等进行函数的绑定时,this 的指向

6)setTimeout 等传参形式的 this 指向

7)改变 this 的方法:call,apply

计时器

  • setTimeout
  • Promises
  • setInterval
  • setImeediate
  • requestIdleCallback
  • requestdleCallback

setTimeoutsetInterval

setTimeout将任务排在 x 毫秒之后运行,setInterval每隔 x 毫秒运行一次任务。

promisesmicrotasks

一个Promise回调也被称为”microtask”,它以与 MutationObserver 回调相同的频率运行。

浅拷贝,深拷贝

  • 浅拷贝是创建一个新对象,这个对象有着原始对象属性值得一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 深拷贝就是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟出一个新的区域存放新的对象,且修改新对象不会影响原对象。
let a1 = { b: { c: {} } };

let a2 = shallowClone(a1); //浅拷贝方法
a2.b.c === a1.b.c; //true  新旧对象还是共享同一块内存

let a3 = deepClone(a3); //深拷贝方法
a3.b.c === a1.b.c; // false 新对象跟原对象不共享内存
// 浅拷贝
let obj1 = {
  name: '浪里行舟',
  arr: [1, [2, 3], 4],
};
let obj3 = shallowClone(obj1);
obj3.name = '阿浪';
obj3.arr[1] = [5, 6, 7]; // 新旧对象还是共享同一块内存
// 这是个浅拷贝的方法
function shallowClone(source) {
  var target = {};
  for (var i in source) {
    if (source.hasOwnProperty(i)) {
      target[i] = source[i];
    }
  }
  return target;
}
console.log('obj1', obj1); // obj1 { name: '浪里行舟', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3', obj3); // obj3 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
// 深拷贝
let obj1 = {
  name: '浪里行舟',
  arr: [1, [2, 3], 4],
};
let obj4 = deepClone(obj1);
obj4.name = '阿浪';
obj4.arr[1] = [5, 6, 7]; // 新对象跟原对象不共享内存
// 这是个深拷贝的方法
function deepClone(obj) {
  if (obj === null) return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  if (typeof obj !== 'object') return obj;
  let cloneObj = new obj.constructor();
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key]);
    }
  }
  return cloneObj;
}
console.log('obj1', obj1); // obj1 { name: '浪里行舟', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4', obj4); // obj4 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }

总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

浅拷贝的实现方式

  1. Object.assign()

    Object.assign()方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

    let obj1 = { person: { name: 'kobe', age: 41 }, sports: 'basketball' };
    let obj2 = Object.assign({}, obj1);
    obj2.person.name = 'wade';
    obj2.sports = 'football';
    console.log(obj1); //{ person: { name: 'wade', age: 41 }, sports: 'basketball' }
  2. 函数库 lodash 的_.clone 方法

  3. 展开运算符

    let obj1 = { name: 'Kobe', address: { x: 100, y: 100 } };
    let obj2 = { ...obj1 };
    obj1.address.x = 200;
    obj1.name = 'wade';
    console.log('obj2', obj2); // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
  4. Array.prototype.concat()

深拷贝的实现方式

  1. JSON.parse(JSON.stringify())

    let arr = [
      1,
      3,
      {
        username: ' kobe',
      },
    ];
    let arr4 = JSON.parse(JSON.stringify(arr));
    arr4[2].username = 'duncan';
    console.log(arr, arr4);
  2. 函数库 lodash 的_.cloneDeep 方法

  3. 手写递归方法

    递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝

    function deepClone(obj, hash = new WeakMap()) {
      if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
      if (typeof obj !== 'object') return obj;
      // 是对象的话就要进行深拷贝
      if (hash.get(obj)) return hash.get(obj);
      let cloneObj = new obj.constructor();
      // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
      hash.set(obj, cloneObj);
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 实现一个递归拷贝
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }
    let obj = { name: 1, address: { x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let d = deepClone(obj);
    obj.address.x = 200;
    console.log(d);

    继承

1.原型链继承

2.借用构造函数继承

3.组合继承

4.原型式继承

防抖、节流

函数防抖

在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时

只执行最后一个被触发的,清除之前的异步任务,核心在于清零

例子:页面滚动处理事件,搜索框输入联想

// 防抖
function debounce(fn, delay) {
  var timer = null;
  return function () {
    var that = this;
    var args = arguments;
    clearTimeout(timer); // 清除重新计时
    timer = setTimeout(function () {
      fn.apply(that, args);
    }, delay || 500);
  };
}

// 使用
function clickHandler() {
  console.log('防抖click!');
}
const handler = debounce(clickHandler);
document.getElementById('button').addEventListener('click', handler);

函数节流

只在开始执行一次,未执行完成过程中触发的忽略,核心在于开关锁。

例如:多次点击按钮提交表单,第一次有效

//节流
function throttle(fn, delay) {
  let timer = null;
  return function () {
    if (timer) {
      return false;
    }
    let that = this;
    let args = arguments;
    fn.apply(that, args);
    timer = setTimeout(function () {
      clearTimeout(timer);
      timer = null;
    }, delay || 500);
  };
}

//使用
function clickHandler() {
  console.log('节流click');
}
const handler = throttle(clickHandler);
document.getElementById('button').addEventListener('click', handler);

节流第一次有效,防抖反之

ajax

什么是 ajax?ajax 作用是什么

异步的javascript和xml  AJAX 是一种用于创建快速动态网页的技术。  ajax用来与后台交互

原生 ajax 的五步请求

//1.创建XMLHttpRequest对象
let ajax = new XMLHttpRequest();
//2.规定请求的类型、URL以及是否异步处理请求。
ajax.open("GET",url,true);
//3.发送信息至服务器时内容编码类型
ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//4.发送请求
ajax.send(null);
//5.接受服务器响应数据
ajax.onreadystatechange = function (){
	if(obj.readyState === 4 && (obj.status === 200 || obj.status === 304))
}