JavaScript高频面试题整理

作用域链

作用域就是程序执行到某一个特定部分时,可以访问这一部分的变量或者函数,这些变量或者函数就组成了一个特定的域,称为作用域。

程序执行会有一个环境栈,从全局环境开始,生成一个全局执行环境的关联对象,该对象拥有全局作用域的所有变量和方法。然后程序继续执行,遇到函数,也会生成该函数的一个关联的环境对象,并且将它压入栈中,此时会根据环境栈生成一个作用链,栈顶(作用域链顶端)也就是当时的函数执行环境对象,查找变量时就可以从作用域链顶端开始一直往后。

每一个执行环境都有一个作用域链,他可以访问作用域链上的属性和方法

原型、原型链

为什么要有原型。因为原本的构造函数,工厂模式产生的实例不能共享方法,非常浪费内存。

测试类型、数组类型

typeof 可以测试基本类型值,引用类型值只能测出 object instanceof 可以测所有类型的值,包括是哪种引用类型值 constructor 该属性是属于原型的属性,指向了其构造原型的构造函数 Object.prototype.toString.call() 对象拥有的原型方法

var x = Object.prototype.toString;
console.log(x.call(11));
console.log(x.call("yes"));
console.log(x.call(true));
console.log(x.call(null));
console.log(x.call([1,2,4]));
console.log(x.call(undefined));
console.log(x.call({}));
console.log(x.call(function f() {}))

闭包

一种延长作用域链的方式,通过函数中创建另一个函数并且引用函数内部的属性,当函数运行完之后,返回的匿名函数依然保存着对原来函数的属性引用,这个引用是通过作用域链来完成的

function Great() {
  var x = 1, b = "yes";
  return function() {
    return ++x;
  }
}
var com = Great();
console.log(com());
console.log(com());

null和undefine的区别

null 为空值,是一个空指针对象,通过 number 可以转为 0 undefined 是未定义,定义的变量没有初始化就会默认为这个值,通过 number 转为 NaN

call,apply,bind区别 ,手写bind

ES6

函数和执行域相关

记住函数作用域以及 var 有变量提升

函数柯里化

实现诸如 add(1,3)(2) = 6 的效果,利用闭包的特性来做

function KeLi(...arg) {
  let args = [...arg];
  return function add(...newArgs) {
    if(newArgs.length != 0) {
      args.push(...newArgs);
      return add;
    } else {
      return args.reduce((pre, next) => pre += next);
    }
  }
}

console.log(KeLi(1,2)(4,5,5,6,7)(1,2)(-13,2)())
//22

对node.js有了解吗

promise

promise.all 方法

每一个promise 实例变为 full ,最后的才会变为 full。否则遇到的第一个 reject ,就会返回该 reject。并且整个 Promise.all 会停止执行

let pro = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('e了');
  }, 1000);
})
// .then(res => {
//   console.log(`触发成功${res}`);
//   return res;
// } , err => {
//   console.log(`触发错误${err}`);
//   return err
// } );

let pro1 = new Promise((resolve, reject) => {
  for(let i = 3;i < 10;i ++) {
    if(i % 2 == 0) {
      resolve(i);
    } else {
      reject(i);
    }
  }
});

let sum = Promise.all([pro, pro1]);

sum.then(res => {
  console.log(`最终结果为成功${res}`);
}, err => {
  console.log(`最终结果为错误${err}`);
});
//最终结果为错误3

但是这里有一个点,如果在实例上有 then 方法,那么最终实例是接不到 err 回调函数的,因为在最终实例看来是成功的,并且整个 Promise.all 会执行完

自己实现一个 Promise.all 方法

async 和 await

async function great() {
  await setTimeout(() => {
    console.log('开始下一条')
  }, 1000);
  await console.log('Yes');
}
great();

数组扁平化

//数组扁平化方式一
function flaten(array) {
  let clone = [];
  for(let i = 0;i < array.length;i ++) {
    clone = clone.concat(Array.isArray(array[i]) ? flaten(array[i]) : array[i]);
  }
  return clone;
}

//数组扁平化方式二
function flaten1(array) {
  let clone = array;
  //console.log(clone.some(item => Array.isArray(item)));
  while(clone.some(item => Array.isArray(item))) {
    clone = [].concat(...clone);
  }
  return clone;
}

//ES6中的方法 flat
console.log(x.flat(Infinity))

let x = [1,2,[3,4,[5,6,'yes'],{a: 1}]];
let y = [1,2,4,4,[1,2]]

console.log(flaten1(y))

数组的常用方法

检测方法

  • Array.isArray(x) 是数组就返回 true

位置方法

  • array[i]
  • array.indexOf(x)
  • array.lastIndexOf(x)

操作方法

  • pop push
  • unshift shift
  • splice
  • slice
  • concat

排序方法

  • sort() 传入一个函数作为比较条件
  • reverse() 翻转数组

迭代方法

  • some every
  • map filter
  • forEach
  • reduce 向后迭代
  • reduceRight 从后向前迭代

同花顺一面

Ajax 的基本使用

同花顺一面

创建 XHR 对象

可以使用 new XMLHttpRequest(); 来创建

IE 7之前的要使用 new ActiveXObject(); 来创建

使用

xhr.open(methods, url, boolean);

如上所示,method 为请求方法,url 为请求地址,可加参数。布尔值表示是否要异步发送

xhr.send(null);

在成功调用 open 方法后,必须调用 send 方法才能开始发送。该方法接受一个参数,表示要在请求主体里面添加的数据,若是没有必须设置为空。

获取头部信息

  • getResponseHeader(params) 参数代表头部字段名称,函数返回相应头部字段对应的信息
  • getAllResponseHeaders() 获取所有头部字段包含的信息

监听

使用 send 后必须监听响应或者错误

  • onreadystatechange() 监听状态码 state ,也就是 http 响应码
  • ontimeout 监听事件是否超时

事件捕获、冒泡、委托

同花顺一面

垃圾回收机制

dom 常见 api

选择节点,创建节点,添加节点、删除节点

事件循环机制

请看这篇文章

几大继承及最优继承

this.$set

大数据渲染(documentFrame、虚拟列表)

防抖、节流

//防止短时间大量执行,等待动作结束在执行
function Jitter(fn, sleep) {
  var timer = null;
  return function() {
    if(timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(fn, sleep);
  }
}
//一定间隔内执行一次
function throttle(fn, sleep) {
  var valid = true;
  return function() {
    if(!valid) {
      return false;
    }
    valid = false;
    setTimeout(function() {
      fn();
      valid = true;
    }, sleep);
  }
}

function ScrollTop() {
  var top = document.body.scrollTop || document.documentElement.scrollTop;
  console.log(top);
}
window.addEventListener('scroll', Jitter(ScrollTop, 500), false);
window.addEventListener('scroll', throttle(ScrollTop, 500), false);

nextTick() --Vue

浅拷贝和深拷贝

利用 assign 或者 “=“ 进行浅拷贝

var obj = {
  a: 1,
  b: {
    x: 12,
    y: "yes"
  },
  c: function() {
    return this.a;
  }
}

var obj1 = Object.assign(obj);
obj1.a = 34;
console.log(obj);
//浅拷贝

利用数组的 concat 和 slice 进行第一层深拷贝

var obj = [1, 2, 4, [12, 13]];

var obj1 = obj.concat();
var obj2 = obj.slice();
obj1.push(34);
obj2[3].push(15);
console.log(obj, obj1, obj2);
//浅拷贝

利用 JOSN.stringify 和 JSON.parse 进行深拷贝。会过滤掉 undefined、function、symbol 这些值

var obj = {
  a: 1,
  b: {
    x: 12,
    y: "yes"
  },
  c: function() {
    return this.a;
  }
}

var obj1 = JSON.parse(JSON.stringify(obj));
obj1.a = 34;
console.log(obj,obj1);
//浅拷贝

递归进行深拷贝

var obj = {
  a: 1,
  b: {
    x: 12,
    y: "yes"
  },
  c: function() {
    return this.a;
  },
  d: [1,2,[3,4]]
}

function deepClone(obj) {
  var clone = obj instanceof Array ? [] : {};
  for(key in obj) {
    if(obj.hasOwnProperty(key)) {
      clone[key] = typeof obj[key] == "object" ? deepClone(obj[key]) : obj[key];
    }
  }
  return clone;
}
//深拷贝
var obj1 = deepClone(obj);
obj1.a = 5;
obj1.d[2].push(5);
console.log(obj, obj1);

本文为原创文章,若文章内容出现抄袭雷同,请联系文章发布人或者网站管理员,我们将认真核实并及时删除。 除非另有说明,否则此博客中的所有文章均根据CC BY-NC-SA 4.0许可。如需转载请标明出处,谢谢配合!

END--感谢阅读

来发表你的感想吧~

  • 3435
    • 努力向前
      回复
      32525
      测试评论吗~
  • 你好
    • 23
      回复
      112
      呢好
    • 哈哈
      回复
      112
      市政府