2021.7 货拉拉虾皮有赞。。。高级前端岗面试整理
Posted .Der_Rabe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021.7 货拉拉虾皮有赞。。。高级前端岗面试整理相关的知识,希望对你有一定的参考价值。
最近猎头、朋友内推面试了几家公司( 货拉拉、虾皮、有赞、乐信、美的iot、商汤科技、Qtrade苹果树、富途、涂鸦、OPPO、微保、微众、丰巢、众安保险、小满科技、高灯科技、AfterShip、稿定科技、万物云、明源云、元戎启行、Crytpo ),以下做了一个简单的记录梳理。
文章转载:乐字节
JS相关JS原型及原型链function Person() {}JS继承的几种方式
Person.prototype.name = \'Zaxlct\';
Person.prototype.sayName = function() {
alert(this.name);
}
var person1 = new Person();
//JS 在创建对象的时候,都有一个__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。
// 对象 person1 有一个 __proto__属性,创建它的构造函数是 Person,构造函数的原型对象是 Person.prototype
console.log(person1.__proto__ == Person.prototype) //true
//所有函数对象的__proto__都指向Function.prototype
String.__proto__ === Function.prototype // true
String.constructor == Function //true
复制代码
详解
- 原型继承 function Parent () { this.name = \'Parent\' this.sex = \'boy\' } function Child () { this.name = \'child\' } // 将子类的原型对象指向父类的实例 Child.prototype = new Parent() //优:继承了父类的模板,又继承了父类的原型对象 //缺:1.无法实现多继承(因为已经指定了原型对象了) // 2.创建子类时,无法向父类构造函数传参数 复制代码
- 构造函数继承 在子类构造函数内部使用call或apply来调用父类构造函数,复制父类的实例属性给子类。 function Parent (name) { this.name = name } function Child () { //用.call 来改变 Parent 构造函数内的指向 Parent.call(this, \'child\') } //优:解决了原型链继承中子类实例共享父类引用对象的问题,实现多继承,创建子类实例时,可以向父类传递参数 //缺:构造继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法 复制代码
- 组合继承 组合继承就是将原型链继承与构造函数继承组合在一起。 使用原型链继承来保证子类能继承到父类原型中的属性和方法 使用构造继承来保证子类能继承到父类的实例属性和方法
- 寄生组合继承
- class继承 在class 中继承主要是依靠两个东西: extends super class Parent { constructor (name) { this.name = name } getName () { console.log(this.name) } } class Child extends Parent { constructor (name) { super(name) this.sex = \'boy\' } } 复制代码
同步与异步、宏任务和微任务分别是函数两个不同维度的描述。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务; 异步任务指的是,不进入主线程、而进入任务队列(task queue)的任务,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程执行。
当某个宏任务执行完后,会查看是否有微任务队列。 如果有,先执行微任务队列中的所有任务; 如果没有,在执行环境栈中会读取宏任务队列中排在最前的任务; 执行宏任务的过程中,遇到微任务,依次加入微任务队列。 栈空后,再次读取微任务队列里的任务,依次类推。
同步(Promise)>异步(微任务(process.nextTick ,Promises.then, Promise.catch ,resove,reject,MutationObserver)>宏任务(setTimeout,setInterval,setImmediate))
await阻塞 后面的代码执行,因此跳出async函数执行下一个微任务
Promise 与Async/Await 区别async/await是基于Promise实现的,看起来更像同步代码,
- 不需要写匿名函数处理Promise的resolve值
- 错误处理: Async/Await 让 try/catch 可以同时处理同步和异步错误。
- 条件语句也跟错误处理一样简洁一点
- 中间值处理(第一个方法返回值,用作第二个方法参数) 解决嵌套问题
- 调试方便
const makeRequest = () => {
try {
getJSON().then(result => {
// JSON.parse可能会出错
const data = JSON.parse(result)
console.log(data)
})
// 取消注释,处理异步代码的错误
// .catch((err) => {
// console.log(err)
// })
} catch (err) {
console.log(err)
}
}
复制代码
使用aync/await的话,catch能处理JSON.parse错误:
const makeRequest = async () => {promise怎么实现链式调用跟返回不同的状态
try {
// this parse may fail
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
复制代码
实现链式调用:使用.then()或者.catch()方法之后会返回一个promise对象,可以继续用.then()方法调用,再次调用所获取的参数是上个then方法return的内容
- promise的三种状态是 fulfilled(已成功)/pengding(进行中)/rejected(已拒绝)
- 状态只能由 Pending --> Fulfilled 或者 Pending --> Rejected,且一但发生改变便不可二次修改;
- Promise 中使用 resolve 和 reject 两个函数来更改状态;
- then 方法内部做的事情就是状态判断:
- 如果状态是成功,调用成功回调函数
- 如果状态是失败,调用失败回调函数
柯里化(Currying) 是把接收多个参数的原函数变换成接受一个单一参数(原来函数的第一个参数的函数)并返回一个新的函数,新的函数能够接受余下的参数,并返回和原函数相同的结果。
- 参数对复用
- 提高实用性
- 延迟执行
只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。 柯里化的函数可以延迟接收参数,就是比如一个函数需要接收的参数是两个,执行的时候必须接收两个参数,否则没法执行。但是柯里化后的函数,可以先接收一个参数
// 普通的add函数JS对象深克隆
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
复制代码
递归遍历对象,解决循环引用问题
解决循环引用问题,我们需要一个存储容器存放当前对象和拷贝对象的对应关系(适合用key-value的数据结构进行存储,也就是map),当进行拷贝当前对象的时候,我们先查找存储容器是否已经拷贝过当前对象,如果已经拷贝过,那么直接把返回,没有的话则是继续拷贝。
function deepClone(target) {JS模块化
const map = new Map()
function clone (target) {
if (isObject(target)) {
let cloneTarget = isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target)
}
map.set(target,cloneTarget)
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
}
return clone(target)
};
复制代码
nodeJS里面的模块是基于commonJS规范实现的,原理是文件的读写,导出文件要使用exports、module.exports,引入文件用require。 每个文件就是一个模块;每个文件里面的代码会用默认写在一个闭包函数里面 AMD规范则是非同步加载模块,允许指定回调函数,AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
AMD推崇依赖前置, CMD推崇依赖就近。对于依赖的模块AMD是提前执行,CMD是延迟执行。
在ES6中,我们可以使用 import 关键字引入模块,通过 exprot 关键字导出模块,但是由于ES6目前无法在浏览器中执行,所以,我们只能通过babel将不被支持的import编译为当前受到广泛支持的 require。
CommonJs 和 ES6 模块化的区别:
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
前端模块化:CommonJS,AMD,CMD,ES6
import 和 require 导入的区别import 的ES6 标准模块; require 是 AMD规范引入方式;
import是编译时调用,所以必须放在文件开头;是解构过程 require是运行时调用,所以require理论上可以运用在代码的任何地方;是赋值过程。其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量
异步加载JS方式- 匿名函数自调动态创建script标签加载js
(function(){
var scriptEle = document.createElement("script");
scriptEle.type = "text/javasctipt";
scriptEle.async = true;
scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
var x = document.getElementsByTagName("head")[0];
x.insertBefore(scriptEle, x.firstChild);
})();
复制代码
- async属性
// async属性规定一旦加载脚本可用,则会异步执行
<script type="text/javascript" src="xxx.js" async="async"></script>
复制代码
- defer属性
// defer属性规定是否对脚本执行进行延迟,直到页面加载为止Set、Map、WeakSet、WeakMap
<script type="text/javascript" src="xxx.js" defer="defer"></script>
复制代码
Set对象可以存储任何类型的数据。 值是唯一的,没有重复的值。
Map对象保存键值对,任意值都可以成为它的键或值。
WeakSet 结构与 Set 类似,也是不重复的值的集合 . WeakMap 对象是一组键值对的集合
不同: WeakSet 的成员只能是对象,而不能是其他类型的值。 WeakSet 不可遍历。
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
WeakMap的键名所指向的对象,不计入垃圾回收机制。
call、applycall( this,a,b,c ) 在第一个参数之后的,后续所有参数就是传入该函数的值。
apply( this,[a,b,c] ) 只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。
共同之处:都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次 所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。
addEventListener的第三个参数干嘛的,为true时捕获,false时冒泡
Object.prototype.toString.call() 判断对象类型
// new Set是实现数组去重,词法作用域与作用域链
// Array.from()把去重之后转换成数组
let arr2 = Array.from(new Set(arr));
复制代码
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
ES5只有全局作用域没和函数作用域,ES6增加块级作用域
暂时性死区:在代码块内,使用 let 和 const 命令声明变量之前,该变量都是不可用的,语法上被称为暂时性死区。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
秋招面试分享一则大数据面经:货拉拉大数据平台实习岗