因为最近面试被问太多这样的套路了
- 谈谈对XXX的理解?
- 你对XXX是怎么实现的有什么思路?
- 你可以自己手写实现一个简单demo吗?
所以就整理了一下
手写实现Bind函数
// 简单实现
Function.prototype.bind = function(){
var self = this;
// 保存传入的上下文
context = [].shift().call(arguments);
// 保存传入的参数
params = [].split().call(arguments);
return this.apply(context,[].concat.call(params,[].split().call(arguments)))
}
// MDN源码实现
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
// 下面这句有点难以理解…我的理解也不一定正确
// 当bind返回的函数不是作为构造函数的话,this instanceof fNOP 是false,反之为true
// 所以就是进入Othis || window
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis || window,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 把原来函数的原型链传给了fbound,然后返回fbound就好
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
- Bind是什么:bind就是改变某个方法的执行时上下文主体(简单理解为改变this的指向)
- Bind解决了什么问题:简单理解为,在你需要的时候去借用一个函数。(指定某个方法的执行时上下文主体)
- Bind和apply,call的区别在哪里:
- bind返回的是改变了上下文后的函数,apply,call返回的是改变了上下文后的函数的执行结果
- 传参格式不同,Bind是一个个传,apply传一个数组
手写实现Promise
try {
module.exports = Promise
} catch (e) {
console.log(e)
}
function Promise(executor) {
var self = this
// 确定一个当前状态值
self.status = 'pending'
// 创建一个resolved 和 reject 的调用链
self.onResolvedCallback = []
self.onRejectedCallback = []
function resolve(value) {
// 如果传入的是一个Promise对象,则继续链式调用
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(function() { // 异步执行所有的回调函数
if (self.status === 'pending') {
self.status = 'resolved'
self.data = value
for (var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value)
}
}
})
}
function reject(reason) {
setTimeout(function() { // 异步执行所有的回调函数
if (self.status === 'pending') {
self.status = 'rejected'
self.data = reason
for (var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason)
}
}
})
}
// 这个executor就是执行函数,但是其实我没有查到这个函数的相关资料 = =
try {
executor(resolve, reject)
} catch (reason) {
reject(reason)
}
}
function resolvePromise(promise2, x, resolve, reject) {
var then
var thenCalledOrThrow = false
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise!'))
}
if (x instanceof Promise) {
if (x.status === 'pending') { //because x could resolved by a Promise Object
x.then(function(v) {
resolvePromise(promise2, v, resolve, reject)
}, reject)
} else { //but if it is resolved, it will never resolved by a Promise Object but a static value;
x.then(resolve, reject)
}
return
}
if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
try {
then = x.then //because x.then could be a getter
if (typeof then === 'function') {
then.call(x, function rs(y) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return resolvePromise(promise2, y, resolve, reject)
}, function rj(r) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(e)
}
} else {
resolve(x)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
var self = this
var promise2
onResolved = typeof onResolved === 'function' ? onResolved : function(v) {
return v
}
onRejected = typeof onRejected === 'function' ? onRejected : function(r) {
throw r
}
if (self.status === 'resolved') {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() { // 异步执行onResolved
try {
var x = onResolved(self.data)
resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
})
})
}
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() { // 异步执行onRejected
try {
var x = onRejected(self.data)
resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
})
})
}
if (self.status === 'pending') {
// 这里之所以没有异步执行,是因为这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已是异步执行,构造函数里的定义
return promise2 = new Promise(function(resolve, reject) {
self.onResolvedCallback.push(function(value) {
try {
var x = onResolved(value)
resolvePromise(promise2, x, resolve, reject)
} catch (r) {
reject(r)
}
})
self.onRejectedCallback.push(function(reason) {
try {
var x = onRejected(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (r) {
reject(r)
}
})
})
}
}
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
Promise.deferred = Promise.defer = function() {
var dfd = {}
dfd.promise = new Promise(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
手写原生AJAX
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest){
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else{
// 旧版IE用ActiveXObject
// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
// readyState有5个状态,分别代表
// 0: 请求未初始化
// 1: 服务器连接已建立
// 2: 请求已接收
// 3: 请求处理中
// 4: 请求已完成,且响应已就绪
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","/ajax/test1.txt",true);
xmlhttp.send();
}
手写Promise包装下的AJAX
function myGet(url) {
return new Promise(function(resolve, reject) {
$.get(url, resolve);
});
}
myGet('/url1').then(function (data) {
// 一些处理,data为返回值
return myGet('/url2');
}).then(function (data) {
// 一些处理
return myGet('/url3');
}).then(function (data) {
// 最终处理
});
手写 EventEmitter
// 参考https://blog.csdn.net/bdss58/article/details/51473107
// 参考https://github.com/mqyqingfeng/EventEmitter/blob/master/eventEmitter.js
// 参考https://juejin.cn/post/1
class EventEmitter {
constructor(max_event) {
this.listeners = new Map();
// 设置一个最大监听数
this.max_event = max_event || 10;
}
// 添加一个监听器
addListener(label, callback) {
this.listeners.has(label) || this.listeners.set(label, []); // 这里的意思其实是,如果有这个label,那么将传入的cb函数再传给label下的函数,再放到队尾;如果没有label,直接push
this.listeners.get(label).push(callback);
}
// 移除一个监听器
removeListener(label, callback) {
let listeners = this.listeners.get(label);
let index;
if (listeners && listeners.length) {
// MDN:reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其简化为单个值。
// reduce() API:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
// 注意到addListener方法中,一个label是对应了一个数组的,也就是说label下可以监听多个函数,那么remove和emit都要找到这个用户指定的函数。找不到就是-1;但是这里有个问题就是用户如果重复监听怎么办
// 也就是两次addListener同一个label同一个callback
index = listeners.reduce((i, listener, index) => {
return (isFunction(listener) && listener === callback) ? i = index : i;
}, -1);
}
// 如果有这个函数,删掉
if (index > -1) {
listeners.splice(index, 1);
this.listeners.set(label, listeners);
return true;
}
return false;
}
// 监听,调用,注意这里是把这个label下的所有方法全都调用一次
emit(label, ...args) {
let listeners = this.listeners.get(label);
if (listeners && listeners.length) {
listeners.forEach((listener) => {
listener(...args);
})
return true;
}
return false;
}
}
// 实现观察者
class Observer {
constructor(id, subject) {
this.id = id;
this.subject = subject;
}
on(label, callback) {
this.subject.addListener(label, callback);
}
}
手写kmp算法
var kmp = function(sourceStr, subStr){
var partMatch = kmpGetPartMatchLen(subStr);
var result = false;
for(var i = 0; i < sourceStr.length; i++){
for(var j = 0; j < subStr.length; j++){
if(subStr.charAt(j) === sourceStr.charAt(i + j)){
if(j === subStr.length - 1){
result = true;
break;
}
}else{
//实现回滚,以subStr为参照物,即sourceStr往前移动
if(j > 0 && partMatch[j-1] >= 0){
//公式在此处实现
i += (j - 1 - partMatch[j-1] - 1);
}else{
break;
}
}
}
if(result) break;
}
if(result){
return i;
}else{
return -1;
}
};
参考资料