不管是寒冬仍是暖冬,找工做以前都须要作好充足的准备,面试的时候才能作到游刃有余。此文是把我最近找工做准备的以及笔试面试中涉及到的手写题作一个总结。给本身,也给须要的同窗。前端
手写题是比较好准备的一个环节,大部分公司考察的题也就那么多,大都不会超出范围。vue
本文是手写题系列的第二篇文章。node
观察者模式是咱们工做中常常能接触到的一种设计模式。用过 jquery
的应该对这种设计模式都不陌生。eventEmitter
是 node
中的核心,主要方法包括on、emit、off、once
。jquery
class EventEmitter {
constructor(){
this.events = {}
}
on(name,cb){
if(!this.events[name]){
this.events[name] = [cb];
}else{
this.events[name].push(cb)
}
}
emit(name,...arg){
if(this.events[name]){
this.events[name].forEach(fn => {
fn.call(this,...arg)
})
}
}
off(name,cb){
if(this.events[name]){
this.events[name] = this.events[name].filter(fn => {
return fn != cb
})
}
}
once(name,fn){
var onlyOnce = () => {
fn.apply(this,arguments);
this.off(name,onlyOnce)
}
this.on(name,onlyOnce);
return this;
}
}
复制代码
继承是一个万年不变的考点。从ES5到ES6,有许多继承方法。专门看有关继承的文章,通常都会从最基础的prototype
原型链继承 到 借用父类构造函数的call
继承 到两者的结合提及。本文只给出终极方法,若是想了解其余方法的话,能够自行搜索。面试
// ES5
function Parent(name,age){
this.name = name;
this.age = age;
}
Parent.prototype.say = function(){
console.log('I am' + this.name)
}
function Child(name, age, sex){
Parent.call(this,name,age);
this.sex = sex;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
复制代码
// ES6
class Parent {
constructor(name,age){
this.name = name;
this.age = age;
}
}
class Child extends Parents{
constructor(name,age,sex){
super(name,age);
this.sex = sex; // 必须先调用super,才能使用this
}
}
复制代码
首先要了解 instanceof
实现的功能,instanceof
运算符用于检测构造函数的 prototype
属性是否出如今某个实例对象的原型链上。其实考察的也是继承。vue-router
function myInstanceof(left,right){
var proto = left.__proto__;
var protoType = right.prototype;
while(true){
if(proto === null){
return false
}
if(proto == protoType){
return true
}
proto = proto.__proto__
}
}
复制代码
当咱们new一个对象的时候,具体执行的是什么?MDN
上给的说明以下:express
JavaScript
对象(即{});this
的上下文 ;this
。以 var child = new Parent()
为例:json
function newParent(){
var obj = {}; // 首先建立一个对象
obj.__proto__ = Parent.prototype; // 而后将该对象的__proto__属性指向构造函数的protoType
var result = Parent.call(obj) // 执行构造函数的方法,将obj做为this传入
return typeof(result) == 'object' ? result : obj
}
复制代码
原题以下:设计模式
实现一个LazyMan,能够按照如下方式调用:
LazyMan("Hank")输出:
Hi! This is Hank!
LazyMan("Hank").sleep(10).eat("dinner")输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~
LazyMan("Hank").eat("dinner").eat("supper")输出
Hi This is Hank!
Eat dinner~
Eat supper~
LazyMan("Hank").sleepFirst(5).eat("supper")输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此类推。
复制代码
这道题主要考察的是链式调用、任务队列、流程控制等。关键是用手动调用next函数来进行下次事件的调用,相似express
中间件和vue-router
路由的执行过程。跨域
function _LazyMan(name){
this.nama = name;
this.queue = [];
this.queue.push(() => {
console.log("Hi! This is " + name + "!");
this.next();
})
setTimeout(()=>{
this.next()
},0)
}
_LazyMan.prototype.eat = function(name){
this.queue.push(() =>{
console.log("Eat " + name + "~");
this.next()
})
return this;
}
_LazyMan.prototype.next = function(){
var fn = this.queue.shift();
fn && fn();
}
_LazyMan.prototype.sleep = function(time){
this.queue.push(() =>{
setTimeout(() => {
console.log("Wake up after " + time + "s!");
this.next()
},time * 1000)
})
return this;
}
_LazyMan.prototype.sleepFirst = function(time){
this.queue.unshift(() =>{
setTimeout(() => {
console.log("Wake up after " + time + "s!");
this.next()
},time * 1000)
})
return this;
}
function LazyMan(name){
return new _LazyMan(name)
}
复制代码
jsonp
的做用是跨域。原理是经过动态插入script
标签来实现跨域,由于script
脚本不受同源策略的限制。它由两部分组成:回调函数和数据。举例:
function handleResponse(response){
alert("You’re at IP address " + response.ip + ", which is in " +response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script,document.body.firstChild);
}
复制代码
根据上面的例子,下面来实现一个通用的JSONP
函数
function jsonp(obj) {
const {url,data} = obj;
if (!url) return
return new Promise((resolve, reject) => {
const cbFn = `jsonp_${Date.now()}`
data.callback = cbFn
const head = document.querySelector('head')
const script = document.createElement('script')
const src = `${url}?${data2Url(data)}`
console.log('scr',src)
script.src = src
head.appendChild(script)
window[cbFn] = function(res) {
res ? resolve(res) : reject('error')
head.removeChild(script)
window[cbFn] = null
}
})
}
function data2Url(data) {
return Object.keys(data).reduce((acc, cur) => {
acc.push(`${cur}=${data[cur]}`)
return acc
}, []).join('&')
}
// jsonp({url:'www.xxx.com',data:{a:1,b:2}})
复制代码
函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术,是高阶函数的一种用法。好比求和函数add(1,2,3)
, 通过柯里化后变成add(1)(2)(3)
function currying(fn,...args){
if(fn.length <= args.length){
return fn(...args)
}
return function(...args1){
return currying(fn,...args,...args1)
}
}
function add(a,b,c){
return a + b + c
}
add(1,2,3) // 6
var curryingAdd = currying(add);
curryingAdd(1)(2)(3) // 6
复制代码
有错误之处还请小伙伴们及时指出,以避免误人子弟。想看往期内容,翻到页面最上面有连接~