中高级前端面试题(一)基础篇

事件循环机制

同步:执行栈

异步:任务队列

1.宏任务前端

  • 点击回调
  • settimeout

2.微任务面试

  • 当前task执行结束后当即执行的任务
  • 新微任务将放到队列尾部
  • promise

微任务会在下一个宏任务前,所有执行完编程

3.事件队列数组

同步任务 → 主线程(执行栈)→ 执行完成 → 任务队列
复制代码

浏览器渲染

  1. 处理 HTML 标记并构建 DOM 树。
  2. 处理 CSS 标记并构建 CSSOM(Style Rules) 树。
  3. 将 DOM 与 CSSOM 合并成一个渲染树(Render Tree)。
  4. 根据渲染树(Layout)来布局,以计算每一个节点的几何信息。
  5. 将各个节点绘制(Painting)到屏幕上

深拷贝&浅拷贝

  • 首先能够经过 Object.assign 来解决这个问题,不少人认为这个函数是用来深拷贝的。其实并非,Object.assign 只会拷贝全部的属性值到新的对象中,若是属性值是对象的话,拷贝的是地址,因此并非深拷贝。 浅拷贝就只是拷贝的对象的地址。
let a = {
  age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
复制代码
  • 另外咱们还能够经过展开运算符 ... 来实现浅拷贝
let a = {
  age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
复制代码

解构赋值的拷贝是浅拷贝,不能复制继承自原型对象的属性。promise

  • 深拷贝 这个问题一般能够经过 JSON.parse(JSON.stringify(object)) 来解决。
let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
复制代码

可是该方法也是有局限性的:浏览器

  • 会忽略 undefined
  • 会忽略 symbol
  • 不能序列化函数
  • 不能解决循环引用的对象

关于枚举

四个操做会忽略enumerable为false的属性。bash

  1. for...in循环:只遍历对象自身的和继承的可枚举的属性。含原型链
  2. Object.keys():返回对象自身的全部可枚举的属性的键名。含原型链
  3. JSON.stringify():只串行化对象自身的可枚举的属性。
  4. Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。

属性的遍历 ES6 一共有 5 种方法能够遍历对象的属性。框架

  • (1)for...in

for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。异步

  • (2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)全部可枚举属性(不含 Symbol 属性)的键名。函数

  • (3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的全部属性(不含 Symbol 属性,可是包括不可枚举属性)的键名。

  • (4)object.getOwnPropertySymbols返回一个数组,包含对象自身的全部 Symbol 属性的键名。

  • (5)Reflect.ownKeys(obj)

Reflect.ownKeys()返回一个包含全部自身属性(不包含继承属性)的数组。(相似于 Object.keys(), 但不会受enumerable影响).

  • (6)for…of循环:只遍历对象自身的可枚举的属性。不会输出原型链

js原型

一、proto ( [[Prototype]] )

对象原型链,能够直接经过属性访问;若是属性不在当前对象中,就经过原型链查找

var anotherObject = { a:2 };
//自行了解 Object.create(..) 的原理,如今只须要知道它会建立一个
对象并把这个对象的 [[Prototype]] 关联到指定的对象。

var myObject = Object.create( anotherObject ); 
myObject.a; // 2 

myObject.__proto__ === anotherObject  //true;
复制代码

全部普通的 [[Prototype]] 链最终都会指向内置的 Object.prototype 都是Object实例出来的;

anotherObject 改变  myObject.a改变

myObject.a 改变后,脱离anotherObject

myObject.hasOwnProperty( "a" ); // true
复制代码

**myObject.foo 老是会选择原型链中最底层的 foo 属性 **

二、prototype

全部的函数默认都会拥有一个 名为prototyp的公有而且不可枚举的属性,它会指向另外一个对象 ,这个对象一般被称为 Foo 的原型

function Foo() { // ...
}
var a = new Foo();

a.__proto__ === Foo.prototype// true复制代码

new Foo() 只是间接完成了咱们的目 标:一个关联到其余对象的新对象。

三、constructor

function Foo() { // ...   }
 Foo.prototype.constructor === Foo; // true
//Foo.prototype 的 .constructor 属性只是 Foo 函数在声明时的默认属性。
若是 你建立了一个新对象并替换了函数默认的 .prototype
对象引用,那么新对象并不会自动获 得 .constructor 属性。 
复制代码
var a = new Foo();
 a.constructor === Foo; // true


a.__proto__ === NothingSpecial.prototype //true;

a.constructor  === Foo.prototype.constructor === Foo; // true
复制代码

实际上 a 自己并无 .constructor 属性。并且,虽然 a.constructor 确实指 向 Foo 函数,可是这个属性并非表示 a 由 Foo“构造”.

function NothingSpecial() { console.log( "Don't mind me!" ); }

var a = new NothingSpecial(); 
// "Don't mind me!" a; 
// {} 
复制代码

小结

  • Object 是全部对象的爸爸,全部对象均可以经过__proto__找到它
  • Function 是全部函数的爸爸,全部函数均可以经过__proto__找到它
  • 函数的prototype是一个对象
  • 对象的__proto__属性指向原型,__proto__将对象和原型链接起来组成了原型链

面向对象编程

JavaScript不区分类和实例的概念,而是经过原型(prototype)来实现面向对象编程。

全部对象都是实例,所谓继承关系不过是把一个对象的原型指向另外一个对象而已。

Object.create()方法能够传入一个原型对象,并建立一个基于该原型的新对象,可是新对象什么属性都没有,所以,咱们能够编写一个函数来建立.

下一篇,框架篇中高级前端面试题(二)框架篇

相关文章
相关标签/搜索