js面试题目总结

114 阅读13分钟

reduce用法

zhuanlan.zhihu.com/p/65235741

js数据类型?undefined和null区别?

跨域

juejin.im/post/5bfa5f…

    • jsonp
    • 代理(为什么) 
    • cors
        • 请求头字段
          • 浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段
        • 响应头字段
          • Access-Control-Allow-Origin
          • Access-Control-Allow-Credentials
          • Access-Control-Expose-Headers
      • 复杂请求(首先发送预检请求)
        • 请求头字段
          • 关键字段是Origin
          • Access-Control-Request-Method
          • Access-Control-Request-Headers
        • 响应头字段
          • Access-Control-Allow-Methods
          • Access-Control-Allow-Headers
          • Access-Control-Allow-Credentials
          • Access-Control-Max-Age

数组去重

juejin.cn/post/715275…

juejin.cn/post/704861…

(数组里面有对象怎么办?)

如何展开二维数组?

juejin.cn/post/699887…

    • 数组实例的 flat(),flatMap()
    • reduce
let arr = [
  [1, 2],
  [3, 4],
  [5, 6]
];

let flatten = arr.reduce((previous, current) => {
  return previous.concat(current);
});

console.log(flatten); // [1, 2, 3, 4, 5, 6]

// ES6中也可以利用...展开运算符来实现的,实现思路一样,只是写法更精简了
flatten = arr.reduce((a, b) => [...a, ...b]);
console.log(flatten); // [1, 2, 3, 4, 5, 6]

fetch和原生ajax有什么区别?

Async与promise有什么不同?

生成器与迭代器有听过吗?

迭代器是迭代器工厂函数(具有Symbol .iterator属性的函数)返回的包含next方法的迭代器对象;

生成器其实也是一种迭代器,不过拥有了 ES6 新增的句法,使其变得更为灵活和便捷,常用来创建迭代器;

使用生成器函数可以使我们在定义一个可迭代类时变得更为简单,我们不需要一层一层编写迭代方法、迭代器、迭代结果对象等等,我们只需要编写 *Symbol.iterator 方法即可:

*[Symbol.iterator]() { // 使用生成器函数编写迭代方法
  for (let x = this.lower; x < this.upper; x++) {
    yield x;
  }
}
// 等同于前面的例子:
[Symbol.iterator]() { // 定义其迭代方法
  let next = this.lower; // 下一个返回值
  let last = this.upper; // 最后的返回值
  return { // 返回可迭代对象
    next() { // 可迭代对象的 next() 方法,其返回值是迭代结果对象
      if ( next <= last) { // 在迭代未结束时返回拥有 value 属性的迭代结果对象
        return { value : next++};
      } else { // 否则返回拥有 done 为 true 的迭代结果对象
        return { done : true};
      }
    },
    [Symbol.iterator]() { return this; } // 可以将迭代器本身设置为可迭代对象
  }
}

声明变量的方式?

  • var
  • function
  • let
    • let声明的变量只在它所在的代码块有效
    • 不存在变量提升
    • 暂时性死区
      • 块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不受外部的影响
    • 不允许重复声明
    • 块级作用域(代替了匿名立即执行函数表达式(匿名 IIFE))
      • 函数声明(对 ES6 的浏览器实现有效,考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。)
        • 允许在块级作用域内声明函数。
        • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
        • 同时,函数声明还会提升到所在的块级作用域的头部。
  • const
    • 本质
      • const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据,值就保存在变量指向的那个内存地址;对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针;

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

  • import
  • class

作用域?

  • ES5
    • 全局作用域
    • 块级作用域
  • ES6
    • 块级作用域

this

处理url字符串?

找出两个数组的交集?

交换两个数组?

  • 临时变量
  • 解构赋值
  • 异或

实现异步编程?

  • callback
  • 事件监听
  • 发布订阅
  • promise
  • Generator
    • 可以暂停执行和恢复执行,这是它能封装异步任务的根本原因;
    • 函数体内外的数据交换和错误处理机制
  • async\await
    • 内置执行器
    • 更好的语义
    • 更广的适用性
    • 返回值是 Promise

事件模型?事件冒泡?事件捕获?事件委托

    • 事件委托 又叫 事件代理,指的是利用事件冒泡原理,只需给外层父容器添加事件,若内层子元素有点击事件,则会冒泡到父容器上,这就是事件委托,简单说就是:子元素委托它们的父级代为执行事件。
  • 优点:
    • 提高性能:节省内存占用,减少事件注册
    • 动态监听:新增子对象时无需再次对其绑定事件,适合动态添加元素

枚举

blog.csdn.net/happyqyt/ar…

拷贝

  • 浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;但是指针指向的地址还是同一个,所以对应的变动会影响双方。
  • 深拷贝是拷贝多层,每一级别的数据都会拷贝出来,递归拷贝一个对象及其属性,完成后两个对象不互相影响
  • 浅拷贝
    • object.assign(目标对象,源对象,源对象,源对象......)
      • Object.assgin 只能深拷贝第一层, 深层的还是浅拷贝
      • 针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
    • 展开运算符(...)=>{...源对象}
  • 深拷贝
    • JSON做字符串转换:用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
    • object.create()
function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
    • jquery中$.extends()
var $ = require('jquery');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false
    • 第三方函数lodash
    • 递归拷贝
function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
var str = {};
var obj = { 
    a: {a: "hello", b: 21},
    b:function(){console.log('aaa')},
    c:[1,2,3,4,5]
 };
deepClone(obj, str);
console.log(str.a);

如何删除对象中不需要的属性?

    • 将属性设置为 undefined
    • delete
    • 使用对象解构
    • rest参数
    • 循环+判断
    • JSON.stringify的第二个参数
    • Reflect.deleteProperty(对象,属性名)
    • lodash omit()

伪数组转换成标准数组?

    • 声明一个空数组,通过遍历伪数组把它们重新添加到新的数组中
    • 使用原型继承:把伪数组的__proto__指向数组的原型

var aLi = document.querySelectorAll('li');
 
console.log(aLi.constructor === Array) //false
    
aLi.__proto__ = Array.prototype;
 
console.log(aLi.constructor === Array) //true
    • Array.prototype.slice.call();
    • Array.from()
    • 扩展运算符...

基本数据类型的prototype指向哪里?

构造函数的原型对象。

去抖、节流具体实现?区别?

事件循环/Promise、async、setTimeout执行顺序?

JS主线程不断的循环往复的从任务队列中读取任务,执行任务,其中运行机制称为事件循环(event loop)。

JS分为同步任务和异步任务

    • 同步任务都在主线程上执行,形成一个执行栈
    • 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
    • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
    • 任务队列分为 macrotasks 和 microtasks
      • macrotasks(setTimeout, setInterval, setImmediate, I/O, UI rendering)
      • microtasks(process.nextTick, Promises, Object.observe(废弃,Proxy 对象替代), MutationObserver)

正则表达式?

instanceOf实现原理?

    • 通过原型链判断:判断构造函数的原型是否在实例的原型链上,如果在返回true;反之返回false

判断数据类型?

    • typeof
    • instanceOf
    • Object.prototype.toString().call().slice(8,-1).toLocaleLowerCase() // 'object'
    • constructor

typeof 和 instanceof 的弊端

typeof 可以判断除了 null 以外的基础数据类型,但是判断引用类型时,除了 function 类型,其他的无法准确判断。

instanceof 可以准确地判断各种引用类型,但是不能正确判断原始数据类型。

es6继承原理?

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性

寄生组合式继承

function inheritPrototype(subType, superType){
    //原型式继承:浅拷贝superType.prototype对象作为superType.prototype为新对象的原型
    // 内部会自带_proto_指向:prototype.__proto__ = superType.prototype;
    var prototype = Object.create(superType.prototype); 
    // subType.prototype.__proto__ = superType.prototype;
    subType.prototype = prototype;               // 将子类的原型替换为这个原型
    prototype.constructor = subType;             // 修正原型的构造函数
    
}

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    SuperType.call(this, name);
    this.age = age;
}
// 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
    alert(this.age);
}

babeljs对extends语法糖的部分编译结果

function _inherits(subClass, superClass) {
    //对superClass进行类型判断
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  //子类的prototype继承父类的prototype
  //也就是说执行后 subClass.prototype.__proto__ === superClass.prototype; 这条语句为true
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { 
        value: subClass, 
        writable: true, 
        configurable: true 
    }
  });
  //子类是父类构建出的函数对象,需要指定对象的__proto__
  if (superClass) _setPrototypeOf(subClass, superClass);
} 
  • 参考

Js原生操作dom的方法?

如何阻塞代码3秒后执行?

import为什么不能在条件中使用?

import命令是编译阶段执行的,在代码运行之前。

promise

  • promise解决了哪些问题?
  • 如何实现promise?

js为什么是单线程的?

详见:github.com/sisterAn/bl…

    • 所谓单线程,是指在JS引擎中负责解释和执行JS代码的线程唯一,同一时间上只能执行一件任务
    • 原因:避免DOM渲染的冲突
      • 浏览器需要渲染DOM
      • JS可以修改DOM结构
      • JS执行时,浏览器DOM渲染停止
      • 两段JS不能同时执行(都修改DOM就冲突了)
      • web worker支持多线程,但是不能访问DOM

js设计模式有了解过吗?

箭头函数与普通函数有什么不同?

    • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
    • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
    • this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。
    • 箭头函数不能使用 arguments、super 和 new.target,也不能用作构造函数。此外,箭头函数也没有 prototype 属性

闭包是什么?有什么作用?

    • 原理在于闭包函数的作用域链中引用了包含函数的作用域。
    • 闭包就是能够读取其他函数内部变量的函数。
      • 可以实现封装,属性私有化
      • 模块化开发,防止污染全局变量

js模块化开发?

    • 存在命名冲突、代码复杂性的问题
    • 模块成员之间看不出直接关系
  • 命名空间
    • 使用的仍然是全局变量
    • 作用: 减少了全局变量,降低了命名冲突
    • 问题: 数据不安全(外部可以直接修改模块内部的数据),无法按需导出
  • 立即执行函数IIFE
    • 创建闭包来封装私有变量
    • 作用:解决全局命名冲突问题,模拟模块的功能;解决按需导出;模块依赖关系明显
    • 问题:每次引入的script需要严格按照顺序引入
  • CommonJS
    • 同步加载, 服务器端,运行时,输出的是值的拷贝
    • 作用:
      • 不需要依赖script加载的顺序,模块加载的顺序,按照代码中require引入的顺序
      • 所有代码都运行在模块作用域,不会污染全局作用域
      • 模块可以多次加载,在第一次加载时运行,之后加载,读取缓存结果。要想让模块再次运行,必须清除缓存。
    • 问题:
      • 文件加载是同步运行,加载时间长
      • 不适用客户端,因为客户端都是通过网络进行下载,如果依赖过多,导致页面非常卡顿。
  • AMD
    • 异步加载,运行时
    • 延迟加载,依赖前置
    • 作用: 使用异步加载模块,提高加载性能
    • 问题: 如果引入了多余的依赖,没有进行区分是否调用,都会进行加载
  • CMD
    • 异步加载,运行时,
    • 依赖就近,按需加载
  • UMD
    • 解决不同模块加载器和环境之间的兼容性问题
  • ESM
    • 输出的是值的引用
    • 编译时输出接口

作用域和作用域链?

每个函数都有自己的执行环境=>每个环境都有相关联的变量对象(包含环境中定义的变量和函数,由于这个变量对象只在函数执行的时候才被创建,因此也称之为“活动对象”)=>“作用域链”则是指向这些变量对象的指针列表=>这张表里除了保存当前作用域的引用,还保存了当前作用域上层以及上上层等作用域的引用(如果有的话),最终到达“全局作用域

es5中的构造函数和es6中的class有什么区别?

    • 在es5中,原型上定义的方法是可枚举的;es6中class里面的方法是不可枚举的;
    • 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行
    • 类不存在变量提升(hoist)
    • ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上(Parent.apply(this))。ES6的继承实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

如何理解面向对象?

创建一个对象会经历哪些过程?

    • 创建一个新对象;
    • 设置原型链(空对象的原型指向构造函数的原型,继承构造函数的原型对象上的公有属性和方法)
    • 将构造函数的作用域赋给新对象(因此this就指向了这个对象,继承构造函数中的属性);
    • 返回新对象(判断返回值的类型,如果是值类型就是返回新的创建对象,如果是引用类型就返回引用类型的对象)
function newObject(ctor, args) {
    let obj = Object.create(ctor.prototype)
    let res = ctor.apply(obj, args)
    let isObject = typeof res === 'object' && res !== null
    let isFunction = typeof res === 'function'
    return isObject || isFunction ? res : obj
}

function sup(a, b){
    this.a = a
    this.b = b
}
sup.prototype.say = function() {
    console.log(this.a + this.b)
}

s = newObject(sup, ['str', '__'])
console.log(JSON.stringify(s))
s.say()

s2 = new sup('str2','__')
console.log(JSON.stringify(s2))
s2.say()

如何判断数据类型为数组?

www.cnblogs.com/padding1015…

  • variable instanceOf Array
  • Object.prototype.toString.call(variable)
  • Array.prototype.isPrototypeOf(variable) 
  • variable.constructor.toString().indexOf("Array") !== -1
  • Array.isArray(variable);