前端进阶面试-JS面试题(阿里、字节、腾讯、美团面试高频)

1. ES5和ES6继承方式区别

ES5定义类以函数形式, 以prototype来实现继承
ES6以class形式定义类, 以extend形式继承

2. Generator了解

ES6 提供的一种异步编程解决方案, Generator 函数是一个状态机,封装了多个内部状态。

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

调用后返回指向内部状态的指针, 调用next()才会移向下一个状态, 参数:

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

3. 手写Promise实现

var myPromise = new Promise((resolve, reject) => {
  // 需要执行的代码
  ...
  if (/* 异步执行成功 */) {
    resolve(value)
  } else if (/* 异步执行失败 */) {
    reject(error)
  }
})

myPromise.then((value) => {
  // 成功后调用, 使用value值
}, (error) => {
  // 失败后调用, 获取错误信息error
})

4. Promise优缺点

优点: 解决回调地狱, 对异步任务写法更标准化与简洁化
缺点: 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消; 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部; 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成).
极简版promise封装:

function promise () {
  this.msg = '' // 存放value和error
  this.status = 'pending'
  var that = this
  var process = arguments[0]

  process (function () {
    that.status = 'fulfilled'
    that.msg = arguments[0]
  }, function () {
    that.status = 'rejected'
    that.msg = arguments[0]
  })
  return this
}

promise.prototype.then = function () {
  if (this.status === 'fulfilled') {
    arguments[0](this.msg)
  } else if (this.status === 'rejected' && arguments[1]) {
    arguments[1](this.msg)
  }
}

5. 观察者模式

又称发布-订阅模式, 举例子说明.
实现: 发布者管理订阅者队列, 并有新消息推送功能. 订阅者仅关注更新就行

6. 手写实现bind

Function.prototype.bind = function () {
   // 保存原函数
  var self = this
  // 取出第一个参数作为上下文, 相当于[].shift.call(arguments)
  var context = Array.prototype.shift.call(arguments)
  // 取剩余的参数作为arg; 因为arguments是伪数组, 所以要转化为数组才能使用数组方法
  var arg = Array.prototype.slice.call(arguments)
  // 返回一个新函数
  return function () {
    // 绑定上下文并传参
    self.apply(context, Array.prototype.concat.call(arg, Array.prototype.slice.call(arguments)))
  }
}

7. 手写实现4种继承

function Father () {}
function Child () {}
// 1\. 原型继承
Child.prototype = new Father()
// 2\. 构造继承
function Child (name) {
  Father.call(this, name)
}
// 3\. 组合继承
function Child (name) {
  Father.call(this, name)
}
Child.prototype = new Father()
// 4\. 寄生继承
function cloneObj (o) {
  var clone = object.create(o)
  clone.sayName = ...
  return clone
}
// 5\. 寄生组合继承
// 6\. ES6 class extend继承

8. css菊花图

四个小圆点一直旋转

// 父标签
animation: antRotate 1.2s infinite linear;
// 子标签
animation: antSpin 1s infinite linear;
@keyframe antSpin {
  to {
    opacity: 1 
  }
}
@keyframe antRotate {
  to {
    transform: rotate(405)
  }
}
// animation-delay: 逐个延迟0.4s

9. http状态码

1**: 服务器收到请求, 需请求者进一步操作
2**: 请求成功
3**: 重定向, 资源被转移到其他URL了
4**: 客户端错误, 请求语法错误或没有找到相应资源
5**: 服务端错误, server error
304: Not Modified. 指定日期后未修改, 不返回资源

10. 变量对象

变量对象,是执行上下文中的一部分,可以抽象为一种 数据作用域,其实也可以理解为就是一个简单的对象,它存储着该执行上下文中的所有 变量和函数声明(不包含函数表达式)。
活动对象 (AO): 当变量对象所处的上下文为 active EC 时,称为活动对象。

11. async和await:

Generator函数的语法糖,将*改成async,将yield换成await。
是对Generator函数的改进, 返回promise。
异步写法同步化,遇到await先返回,执行完异步再执行接下来的.
内置执行器, 无需next()

12. 算法和数据结构:

算法:
解决具体问题所需要的解决方法。执行效率最快的最优算法。时间复杂度。输入,输出,有穷性,确定性,可行性。冒泡排序,二叉树遍历,最长回文,二分查找,指针,链表等,堆栈,队列等。力扣,codewar,算法导论。
数据结构:
逻辑结构:集合、线性、树形、图形结构
物理结构:顺序、链式存储结构

13. 封装JSONP

function jsonp ({url, param, callback}) {
  return new Promise((resolve, reject) => {
    var script = document.createElement('script')
    window.callback = function (data) {
      resolve(data)
      document.body.removeChild('script')
    }
    var param = {...param, callback}
    var arr = []
    for (let key in param) {
      arr.push(`${key}=${param[key]}`)
    }
    script.src = `${url}?${arr.join('&')}`
    document.body.appendChild(script)
  })
}

14. 手动实现map(forEach以及filter也类似)

// for循环实现
Array.prototype.myMap = function () {
  var arr = this
  var [fn, thisValue] = Array.prototype.slice.call(arguments)
  var result = []
  for (var i = 0; i < arr.length; i++) {
    result.push(fn.call(thisValue, arr[i], i, arr))
  }
  return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))

// forEach实现(reduce类似)
Array.prototype.myMap = function (fn, thisValue) {
  var result = []
  this.forEach((v, i, arr) => {
    result.push(fn.call(thisValue, v, i, arr))
  })
  return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))

15. js实现checkbox全选以及反选


    
    全选
    1
    2
    3
    
  

16. 对原型链的理解?prototype上都有哪些属性

在js里,继承机制是原型继承。继承的起点是 对象的原型(Object prototype)。
一切皆为对象,只要是对象,就会有 proto 属性,该属性存储了指向其构造的指针。
Object prototype也是对象,其 proto 指向null。
对象分为两种:函数对象和普通对象,只有函数对象拥有『原型』对象(prototype)。
prototype的本质是普通对象。
Function prototype比较特殊,是没有prototype的函数对象。
new操作得到的对象是普通对象。
当调取一个对象的属性时,会先在本身查找,若无,就根据 proto 找到构造原型,若无,继续往上找。最后会到达顶层Object prototype,它的 proto 指向null,均无结果则返回undefined,结束。
由 proto 串起的路径就是『原型链』。
通过prototype可以给所有子类共享属性

17. 为什么使用继承

通常在一般的项目里不需要,因为应用简单,但你要用纯js做一些复杂的工具或框架系统就要用到了,比如webgis、或者js框架如jquery、ext什么的,不然一个几千行代码的框架不用继承得写几万行,甚至还无法维护

18. setTimeout时间延迟为何不准

单线程, 先执行同步主线程, 再执行异步任务队列

19. 事件循环述,宏任务和微任务有什么区别?

先主线程后异步任务队列
先微任务再宏任务

20. let const var作用域

块级作用域, 暂时性死区

21. 节流和防抖

函数节流是指一定时间内js方法只跑一次。比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释。

// 函数节流   滚动条滚动


var canRun = true;
document.getElementById("throttle").onscroll = function(){
    if(!canRun){
        // 判断是否已空闲,如果在执行中,则直接return
        return;
    }

    canRun = false;
    setTimeout(function(){
        console.log("函数节流");
        canRun = true;
    }, 300);
};

函数防抖是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,司机才开车。

// 函数防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
    clearTimeout(timer); // 清除未执行的代码,重置回初始化状态

    timer = setTimeout(function(){
        console.log("函数防抖");
    }, 300);
};

22. 实现一个sleep函数

// 这种实现方式是利用一个伪死循环阻塞主线程。因为JS是单线程的。所以通过这种方式可以实现真正意义上的sleep()。

function sleep(delay) {
  var start = (new Date()).getTime();
  while ((new Date()).getTime() - start < delay) {
    continue;
  }
}

function test() {
  console.log('111');
  sleep(2000);
  console.log('222');
}

test()

23. 闭包

闭包属于一种特殊的作用域,称为 静态作用域。它的定义可以理解为: 父函数被销毁 的情况下,返回出的子函数的[[scope]]中仍然保留着父级的单变量对象和作用域链,因此可以继续访问到父级的变量对象,这样的函数称为闭包。
闭包会产生一个很经典的问题:
o多个子函数的[[scope]]都是同时指向父级,是完全共享的。因此当父级的变量对象被修改时,所有子函数都受到影响。
解决:
o变量可以通过 函数参数的形式 传入,避免使用默认的[[scope]]向上查找
o使用setTimeout包裹,通过第三个参数传入
o使用 块级作用域,让变量成为自己上下文的属性,避免共享

24. Immutable.js

Facebook出品, 倡导数据的不可变性, 用的最多就是List和Map.

25. js实现instanceof

// 检测l的原型链(__proto__)上是否有r.prototype,若有返回true,否则false
function myInstanceof (l, r) {
  var R = r.prototype
  while (l.__proto__) {
    if (l.__proto__ === R) return true
  }
  return false
}

26. 严格模式

// 严格模式下, 隐式绑定丢失后this不会指向window, 而是指向undefined

  'use strict'
  var a = 2
  var obj = {
    a: 1,
    b: function() {
      // console.log(this.a)
      console.log(this)
    }
  }
  var c = obj.b
  c() // undefined

27. typescript缺点

并不是严格意义的js的超集, 与js不完全兼容, 会报错
更多的限制, 是一种桎梏
有些js第三方库没有dts, 有问题

28. 构造函数实现原理

构造函数中没有显示的创建Object对象, 实际上后台自动创建了
直接给this对象赋值属性和方法, this即指向创建的对象
没有return返回值, 后台自动返回了该对象

// 模拟构造函数实现
var Book = function(name) {
          this.name = name;
        };

        //正常用法
        var java = new Book(‘Master Java’);

        //使用代码模拟,在非IE浏览器中测试,IE浏览器不支持
        var python = {};
        python.__proto__ = Book.prototype;
        Book.call(python, 'Master Python');

29. for in 和 for of区别

for in遍历数组会遍历到数组原型上的属性和方法, 更适合遍历对象
forEach不支持break, continue, return等
使用for of可以成功遍历数组的值, 而不是索引, 不会遍历原型
for in 可以遍历到myObject的原型方法method,如果不想遍历原型方法和属性的话,可以在循环内部判断一下,hasOwnPropery方法可以判断某属性是否是该对象的实例属性

30.. JS实现并发控制:

使用消息队列以及setInterval或promise进行入队和出队

31.promise.finally实现

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

32.作用域

执行上下文中还包含作用域链。理解作用域之前,先介绍下作用域。作用域其实可理解为该上下文中声明的 变量和声明的作用范围。可分为 块级作用域 和 函数作用域
特性:
声明提前: 一个声明在函数体内都是可见的, 函数优先于变量
非匿名自执行函数,函数变量为 只读 状态,无法修改

let foo = function() { console.log(1) };
(function foo() {
    foo = 10  // 由于foo在函数中只为可读,因此赋值无效
    console.log(foo)
}()) 
// 结果打印:  ƒ foo() { foo = 10 ; console.log(foo) }

33.作用域链

我们知道,我们可以在执行上下文中访问到父级甚至全局的变量,这便是作用域链的功劳。作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,因此我们便能通过作用域链访问到父级里声明的变量或者函数。
由两部分组成:
o[[scope]]属性: 指向父级变量对象和作用域链,也就是包含了父级的[[scope]]和AO
oAO: 自身活动对象
如此 [[scopr]]包含[[scope]],便自上而下形成一条 链式作用域。

34.对象的拷贝

浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
oObject.assign
o展开运算符(...)
深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响
oJSON.parse(JSON.stringify(obj)): 性能最快
具有循环引用的对象时,报错
当值为函数、undefined、或symbol时,无法拷贝
o递归进行逐一赋值

35.new运算符的执行过程

新生成一个对象
链接到原型: obj.__proto__ = Con.prototype
绑定this: apply
返回新对象(如果构造函数有自己 retrun 时,则返回该值)

36.instanceof原理

能在实例的 原型对象链 中找到该构造函数的prototype属性所指向的 原型对象,就返回true。即:
`// __proto__: 代表原型对象链
instance.[__proto__...] === instance.constructor.prototype
// return true`

你可能感兴趣的:(javascript)