前端面试题之JavaScript

  • A+
所属分类:Web前端
摘要

参考链接:http://es6.ruanyifeng.com/如何优雅处理前端异常错误处理机制call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。


ES6语法有哪些,分别怎么用

参考链接:http://es6.ruanyifeng.com/

new的执行过程

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个对象)
  3. 执行构造函数中的代码(为这个新对象添加属性)
  4. 返回新对象

异常捕获

如何优雅处理前端异常

错误处理机制

call、apply、bind区别

call

call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。

fun.call(thisArg, arg1, arg2, ...) 

apply

apply()方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

func.apply(thisArg, [argsArray]) 

bind

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

function.bind(thisArg[,arg1[,arg2[, ...]]]) 

总结

其实apply和call基本类似,他们的区别只是传入的参数不同。call方法接受的是若干个参数列表,而apply接收的是一个包含多个参数的数组。
bind和call、apply方法作用是一致的,只是该方法会返回一个函数,并且我们必须要手动去调用。

手写一个AJAX

let xhr = new XMLHttpRequest() xhr.open('GET', '/xxx',true) xhr.onreadystatechange = function(){   if(xhr.readyState === 4){     if(xhr.status >= 200 && xhr.status < 300){       console.log('请求成功')     }else{       console.log('请求失败')     }   } } xhr.send() 

什么是JSONP

请求方:xxx.com的前端程序员(浏览器)
响应方:yyy.com的后端程序员(服务器)

  1. 请求方创建script,src指向响应方,同时传一个查询参数?callback=xxx
  2. 响应方根据查询参数callback,构造形如这样的响应:
      1. xxx.call(undefined,’需要的数据’)
      2. xxx(‘需要的数据’)
  3. 浏览器接收到响应,就会执行xxx.call(undefined,’需要的数据’)
  4. 那请求方就知道了所需要的数据了

为什么不支持POST?

  1. JSONP是通过动态创建script的
  2. 动态创建script时只能用GET,没法用POST

如何实现深拷贝

JSON.parse()、JSON.stringify()

这种方法存在一个问题:能正确处理的对象只有Number、String、Array等能够被JSON表示的数据结构,因此函数这种不能被 json 表示的类型将不能被正确处理。

var target = {   a: 1,   b: {     b1: 11,     b2: 22   } }; var targetCopy = JSON.parse(JSON.stringify(target)); targetCopy.a = 22; targetCopy.b.b1 = 111; console.log(target); //{a: 1, b: {b1: 11, b2: 22}}; console.log(targetCopy); //{a: 22, b: {b1: 111, b2: 22}}; console.log(target === targetCopy); //false 

for...in + 递归

function clone(object) {   var object2   if (!(object instanceof Object)) {     return object   } else if (object instanceof Array) {     object2 = []   } else if (object instanceof Function) {     object2 = eval(object.toString())   } else if (object instanceof Object) {     object2 = {}   }   for (let key in object) {     object2[key] = clone(object[key])   }   return object2 } var obj1 = {   name: 'zww',   hi: {     like: 'lol',     age: 18   } } var obj2 = clone(obj1) obj2.name = 'lq' console.log(obj1)   // {name: "zww", hi: {…}} console.log(obj2)   // {name: "lq", hi: {…}} 

全局函数eval()有什么作用

eval()函数会将传入的字符串当做JavaScript代码进行执行。
eval()是全局对象的一个函数属性

  • 如果eval()的参数是一个字符串。如果字符串表示的是表达式,eval()会对表达式进行求值。如果参数表示一个或多个JavaScript语句,那么eval()就会执行这些语句。
  • 如果eval()的参数不是字符串,eval()会将参数原封不动地返回。

eval详解

为什么0.1+0.2!==0.3

因为JavaScript存储数值采用双精度浮点数,会出现精度丢失的问题。

0.100000000000000000000002 === 0.1  //true console.log(0.100000000000000002)   //0.1 

如何使得0.1+0.2===0.3呢

parseFloat((0.1 + 0.2).toFixed(10)) === 0.3   //true 

0.1+0.2!==0.3详细解释

==和===的区别

==:相等运算符
===:严格运算符

  • 对于string、number等基础类型,是有区别的:
    1. 不同类型:==:转换成同一类型后的值看值是否相等;===:如果类型不同,结果就不等。
    2. 相同类型:直接进行值比较
  • 对于Array、Object等高级类型,是没有区别的
  • 对于基础类型与高级类型,是有区别的:
    1. ===:类型不同,结果不等;
    2. ==:将高级类型转化为基础类型,进行值比较。

什么是原型

JS里的原型与原型链

什么是闭包

JS中的闭包是什么?

什么是CORS,什么是跨域

跨域资源共享CORS详解

什么是instanceof操作符

MDN:instanceof运算符

typeof与instanceof区别

typeof

typeof可用于基本数据类型的类型判断,返回值都是小写的字符串。

console.log(typeof undefined)   // undefined console.log(typeof null)    // object console.log(typeof 1)   // number console.log(typeof "a")   // string console.log(typeof true)    // boolean console.log(typeof [1,2])   // object console.log(typeof {})    // object console.log(typeof function(){})    // function console.log(typeof new Function())    // function console.log(typeof new String())    // object console.log(typeof new Object())    // object console.log(typeof new Number())    // object 

instanceof

instanceof是判断变量是否为某个对象的实例,返回值为true或false。

var a = [] var b = {}  console.log(a instanceof Array)   // true console.log(a instanceof Object)    // true console.log(b instanceof Array)   // false console.log(b instanceof Object)    // true 

区别

  1. typeof用于基本数据类型的类型判断,无法判断对象的具体类型(除function)
  2. instanceof可以用来区分数组、对象,不能用来判断字符串、数字等

数组降维

二维数组

遍历

利用双重循环遍历二维数组中的每一个元素并存放到新的数组中。

var arr = [   ['z', 'w', 'w'],   ['l', 'q'],   [12, 52, 7] ]; var result = []; for (var i = 0; i < arr.length; i++) {   for (var a = 0; a < arr[i].length; a++) {     result.push(arr[i][a]);   } } console.log(result);    // (8) ["z", "w", "w", "l", "q", 12, 52, 7] 

concat()

利用concat()方法来合并两个或多个数组。

var arr = [   ['z', 'w', 'w'],   ['l', 'q'],   [12, 52, 7] ]; var result = []; for (var i = 0; i < arr.length; i++) {   result = result.concat(arr[i]); } console.log(result);    // (8) ["z", "w", "w", "l", "q", 12, 52, 7] 

concat + apply

var arr = [   ['z', 'w', 'w'],   ['l', 'q'],   [12, 52, 7] ]; var result = Array.prototype.concat.apply([], arr); console.log(result);    // (8) ["z", "w", "w", "l", "q", 12, 52, 7] 

flat()

flat()方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

var arr = [   ['z', 'w', 'w'],   ['l', 'q'],   [12, 52, 7] ]; var result = arr.flat() console.log(result)   // (8) ["z", "w", "w", "l", "q", 12, 52, 7] 

多维数组

forEach + 递归

var arr = [   1, [2, 3, [     4, 5, [       6, 7     ], 8   ], 9], 10 ]; var result = []  function reduction(arr) {   arr.forEach(item => {     if (Array.isArray(item)) {       reduction(item)     } else {       result.push(item)     }   })   return result }  console.log(reduction(arr));    // (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 

flat()

flat()的参数使用Infinity,可展开任意深度的嵌套数组。

var arr = [   1, [2, 3, [     4, 5, [       6, 7     ], 8   ], 9], 10 ]; var result = arr.flat(Infinity) console.log(result)   // (10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 

null与undefined的区别

null

null是一个字面量,表示空,没有对象。

用法:

  1. 作为函数的参数,表示该函数的参数不是对象。
  2. 作为对象原型链的终点。

undefined

undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。

用法:

  1. 变量被声明了,但没有赋值时,就等于undefined。
  2. 调用函数时,应该提供的参数却没有提供,该参数等于undefined。
  3. 对象没有赋值的属性,该属性的值为undefined。
  4. 函数没有返回值时,默认返回undefined。

注意

null == undefined   // true null === undefined    // false  typeof null   // "object" typeof undefined    // "undefined"  1 + null    // 1 1 + undefined   // NaN 

Promise、Promise.all、Promise.race分别怎么用

含义

Promise是异步编程的一种解决方案。

Promise有以下几种状态:

  1. pending(进行中): 初始状态,既不是成功,也不是失败状态。
  2. fulfilled(已成功): 意味着操作成功完成。
  3. rejected(已失败) 意味着操作失败。

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

Promise

function fn() {   return new Promise((resolve, reject) => {     成功时调用 resolve(value)     失败时调用 reject(error)   }) } fn().then(成功函数1, 失败函数1).then(成功函数2, 失败函数2) 

Promise.all

Promise.all()方法用于将多个Promise实例,包装成一个新的Promise实例。

该promise对象在数组参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个数组参数对象里面的promise对象失败则立即触发该promise对象的失败。

promise1和promise2都成功就会调用成功函数

Promise.all([promise1, promise2]).then(成功函数, 失败函数) 
let p1 = new Promise((resolve, reject) => {   resolve("成功p1") })  let p2 = new Promise((resolve, reject) => {   resolve("成功p2") })  let p3 = Promise.reject("失败p3")  Promise.all([p1, p2]).then(result => {   console.log(result)   // (2) ["成功p1", "成功p2"] }).catch(err => {   console.log(err) })  Promise.all([p1, p3]).then(result => {   console.log(result) }).catch(err => {   console.log(err)    // 失败p3 }) 

Promise.race

Promise.race()方法同样是将多个Promise实例,包装成一个新的Promise实例。

promise1和promise2只要有一个成功就会调用成功函数

Promise.race([promise1, promise2]).then(成功函数, 失败函数) 

Promise.race([promise1, promise2, promise3...])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => {   setTimeout(() => {     resolve('成功')   }, 1000) })  let p2 = new Promise((resolve, reject) => {   setTimeout(() => {     reject('失败')   }, 500) })  Promise.race([p1, p2]).then(result => {   console.log(result) }).catch(err => {   console.log(err)    // 失败 })