手写汇总
本文最后更新于:7 个月前
一些手写的内容汇总
1、通过XMLHttpRequest实现网络请求
const xhr = new XMLHttpRequest();
xhr.open('GET',url,true)//异步,false同步
xhr.onreadystatechange = function(){
if(xhr.readyState==4&&xhr.state==200)
console.log('成功')
else
console.log('失败')
}
xhr.onerror = function(e){
console.log('连接失败',e)
}
xhr.send();
2、手写call(),apply(),bind()
之前探讨了这三种函数的区别,正好理解的还算可以,所以试一试写出三种函数的手写代码。
call()
Function.prototype.call = function(thisArg){
if (typeof this !== 'function') {
throw new TypeError('不能被call');
}
//如果传入的是null或者undefined 或者空则绑定为全局
let thisArg = thisArg || window;
//在传入的对象上创建方法并赋给它
thisArg.fn = this;
let args = [];
//把参数拿出来,从第二个拿,第一个是指定的this
for(let i = 1;i < arguments.length; i++){
args.push(`arguments[${i}]`)
}
//把参数传入对象的方法执行,得到结果
let result = eval(`context.fn(${args})`);
//删除方法
delete thisArg.fn;
//返回结果
return result;
}
apply()
Function.prototype.apply = function(thisArg,arr){
if (typeof this !== 'function') {
throw new TypeError('不能被apply');
}
//如果传入的是null或者undefined 或者空则绑定为全局
let thisArg = Object(thisArg) || window;
//在传入的对象上创建方法并赋给它
thisArg.fn = this;
let result;
//如果arr没有,就直接执行
if(!arr){
result = thisArg.fn();
}else{
//如果有参数就一一取出来给方法执行
let args = [];
//把参数拿出来,从第二个拿,第一个是指定的this
for(let i = 1;i < arr.length; i++){
args.push(`arguments[${i}]`)
}
//把参数传入对象的方法执行,得到结果
let result = eval(`context.fn(${args})`);
}
//删除方法
delete thisArg.fn;
//返回结果
return result;
}
bind()
以上两种都好写,但是bind()因为要返回一个新的函数,然后还可以被new出来,new之后this指向新创建的new实例。
Function.prototype.bind = function(otherThis) {
if (typeof this !== 'function') {
throw new TypeError('不能被bind');
}
//把第一个参数后面的参数拿出来
var baseArgs= Array.prototype.slice.call(arguments, 1),
baseArgsLength = baseArgs.length,
fToBind = this,
fNOP = function() {},
fBound = function() {
baseArgs.length = baseArgsLength; // reset to default base arguments
baseArgs.push.apply(baseArgs, arguments);
return fToBind.apply(
fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
);
};
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
以上代码是mdn上的代码,但是和原本的bind函数还是有很大差别。以下是一个较为完整的方案。
Function.prototype.bind = function bind(that) {
var target = this;
if (typeof target !== 'function' || toStr.call(target) !== funcType) {
throw new TypeError(ERROR_MESSAGE + target);
}
var args = slice.call(arguments, 1);
var bound;
var binder = function () {
if (this instanceof bound) {
var result = target.apply(
this,
args.concat(slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return this;
} else {
return target.apply(
that,
args.concat(slice.call(arguments))
);
}
};
var boundLength = Math.max(0, target.length - args.length);
var boundArgs = [];
for (var i = 0; i < boundLength; i++) {
boundArgs.push('$' + i);
}
bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);
if (target.prototype) {
var Empty = function Empty() {};
Empty.prototype = target.prototype;
bound.prototype = new Empty();
Empty.prototype = null;
}
return bound;
};
3、手写数组深拷贝
深拷贝,主要是要对深层次的内容进行拷贝,拷贝到新的地址里。
function deepClone(obj){
if(typeof obj!='object' || obj ==null){
return obj
}
let result;
if(obj instanceof Array){
result = []
}else{
result = {}
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
result[key]=deepClone(obj[key])
}
}
return result;
}
let test = {
aa:12,
bb:'string',
cc:[1,2,3,4],
dd:{
ab:123,
ac:'array',
bc:['2','3']
}
}
test.__proto__.gg=123;
deepClone(test)==test //false
let tt = test;
tt==test; //true
3、柯里化函数的实现
柯里化就是将多参数转换成单一参数的函数,要实现参数的分解,我们主要使用bind()来分解参数。
function currying(fn,length){
length=length||fn.length;
return function(...args){
return args.length >= length ?
fn.apply(this,args):
currying(fn.bind(this,...args),length-args.length);
}
}
4、手写EventEmitter
遇到这么个题目,嗯,题目,某人让我写一下,就写一下吧,照着文档算是能够实现一些功能了,但是有挺多要求没有实现的,但起码还是能达到一定要求的。
events(事件触发器)是nodejs中的提供事件触发的模块,events提供了类EventEmitter,EventEmitter 的核心就是事件触发与事件监听器功能的封装。提供了很多方法来进行监听器的绑定移除,这里我就只写其中几个简单的(主要是好实现,而且懒得做多了)。
class EventEmitterOfme {
constructor() {
this.MaxListeners = 10;
this.currentListeners = 0;
this.events = [];
}
//为指定事件添加一个监听器到监听器数组的尾部。
addListener(event, listener) {
if (!this.events[event]) {
console.log('没有此监听器')
} else {
this.events[event].push(listener);
}
}
//为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
on(event, listener) {
if (this.currentListeners + 1 > this.MaxListeners) {
console.log('超过监听器上限')
} else {
if (!this.events[event]) {
this.currentListeners++;
this.events[event] = [listener];
} else {
this.events[event].push(listener);
}
}
return this;
}
//为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
once(event, listener) {
let fn = (arg) => {
listener(arg);
this.removeListener(event, fn);
}
this.on(event, fn);
return this;
}
//移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。
//它接受两个参数,第一个是事件名称,第二个是回调函数名称。
removeListener(event, listener) {
if (!this.events[event]) {
console.log(`不存在${event}监听器`)
} else {
this.events[event].splice(this.events[event].lastIndexOf(listener),1);
}
return this;
}
//移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
removeAllListeners(events) {
if (events === undefined) {
this.events = [];
this.currentListeners = 0;
} else {
for(let key of events) {
delete this.events[key];
this.currentListeners--;
}
}
return this;
}
//默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
setMaxListeners(n) {
this.MaxListeners = n;
return this;
}
//返回指定事件的监听器数组。
listeners(event) {
return this.events[event]
}
allListeners() {
return this.events;
}
//按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
emit(event, ...args) {
if (!this.events[event])
return false;
else {
this.events[event].forEach((fun, index) => fun.call(this, args[index]))
return true;
}
}
}
5、实现防抖函数
防抖的非立即执行版本就是在触发一次后的固定时间内,一直不会执行操作,如果在固定时间内触发了,则重新计时,如果一直没触发,则等到固定时间到了执行.
function thro(fn,wait){
let timer;
let _that = this;
let agrs = arguments;
return function(){
clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(_that,agrs);
},wait)
}
}
防抖的立即执行版本
function thro(fn,wait,rightNow){
let timer;
let _that = this;
let agrs = arguments;
return function(){
//进去先把计时器清除
if(timer) clearTimeout(timer);
//在这里判断是不是第一次运行,是就立刻执行,不是就只需要执行计时器就行
const im = !timer;
if(rightNow){
timer = setTimeout(()=>{
timer = null;
},wait);
if(im) fn.apply(_that,agrs);
}else{
timer = setTimeout(()=>{
fn.apply(_that,agrs);
},wait)
}
}
}
6、节流函数
节流函数就是在规定时间内只执行一次操作,触发多少次都只会执行一次。
function thro(fn,wait){
let timer;
let _that = this;
let agrs = arguments;
return function(){
if(!timer){
timer = setTimeout(()=>{
fn.apply(_that,agrs);
timer = null;
},wait)
}
}
}
7、手写flat()扁平化
一般是对数组实现扁平化,这里我们可以用到apply和concat的组合,因为apply会把数组参数拆解,所以正好符合我们拍扁数组的需求
function flat(arrs){
const isDeep = arrs.some(arr=>arr instanceof Array)
if(!isDeep){
return arrs
}else{
return flat(Array.prototype.concat.apply([],arrs))
}
}
flat([1,[2,2,[3,3]],[4,5]]) //[1, 2, 2, 3, 3, 4, 5]
8、instanceof的实现
instanceof是通过判断其原型链上的属性是否在目标对象的原型链上也有,所以只需要对比prototype就行了。
function myInstanceof(obj,resouceObj){
let objProto = obj.__proto__;
let resouceProto = resouceObj.prototype
while(true){
if(objProto === null){
return false
}
if(objProto === resouceProto){
return true
}
objProto = objProto.__proto__
}
}
9、手写继承
JavaScript的继承虽然有class可以用extends来继承,但是如果需要我们使用原型链进行继承,我们就需要自己来实现了。
需要实现继承,需要我们把父类的原型链和静态属性继承到子类中,所以代码如下:
//父类
function Parents(name){
this.name = name;
}
//父类原型链上的方法
Parents.prototype.getName = function(){
console.log(this.name)
}
//父类的静态方法
Parents.family = 'Bob';
function child(age){
//继承父类的属性
Parents.call(this);
}
//把父类的原型链拷贝一个进来,同时把构造函数修正为自己的构造函数,并且不能被修改
child.prototype = Object.create(Parents.prototype,{
constructor:{
value:new child(),
writeable:false
}
});
//获取父类的静态属性
let keys = Object.entries(Parents);
//把静态属性放进子类
for(let i = 0;i<keys.length;i++){
let key = keys[i][0];
let value = keys[i][1];
child[key] = value;
}
new child();
10、手写new方法
new操作符做了哪些事情?
创建一个新的对象
新对象会被执行[[Prototype]]连接,关联到构造函数的.prototype对象上
新对象会绑定到函数调用的this
如果函数没有返回其他对象,那么new表达式中的函数会调用自动返回这个新对象
function makeNew(fun){ if(typeof fun !== "Object"){ throw TypeError(); } let obj = Object.create(null); //设置对象的原型(即内部 [[Prototype]] 属性)。 Object.setPrototypeof(obj,fun.prototype); let result = fun.apply(obj,Array.prototype.slice.call(arguments,1)); if(typeof result == "Object" || (typeof result =="Function" && result !==null)){ return result }else{ return obj; } }
11、手写Object.create方法
要实现create方法,我们需要知道create有哪些属性
Object.create(proto,ptopertiesObject)
- 使用指定的proto对象和属性创建一个新的对象
- ptopertiesObject表示给新创建的对象的属性进行设置
- 如果ptopertiesObject是null或者不是对象,就会抛出异常
Object.create = function(proto,propertiesObject){
if(typeof propertiesObject !== "object"){
throw TypeError();
}
function temp(){};
//挂上原型链
temp.prototype = proto;
let o = new temp();
if(propertiesObject !== undefined){
if(propertiesObject!==Object(propertiesObject)){
throw TypeError();
}
Object.defineProperties(o,propertiesObject);
}
return o;
}
12、手写Promise方法
class MyPromise {
constructor(exector) {
this.status = MyPromise.PENDING;
// 1.3 “value” is any legal JavaScript value (including undefined, a thenable, or a promise).
this.value = null;
// 1.5 “reason” is a value that indicates why a promise was rejected.
this.reason = null;
/**
* 2.2.6 then may be called multiple times on the same promise
* 2.2.6.1 If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then
* 2.2.6.2 If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.
*/
this.onFulfilledCallback = [];
this.onRejectedCallback = [];
this.initBind();
this.init(exector);
}
initBind() {
// 绑定 this
// 因为 resolve 和 reject 会在 exector 作用域中执行,因此这里需要将 this 绑定到当前的实例
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
}
init(exector) {
try {
exector(this.resolve, this.reject);
} catch (err) {
this.reject(err);
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
setTimeout(() => {
this.status = MyPromise.FULFILLED;
this.value = value;
this.onFulfilledCallback.forEach(cb => cb(this.value));
})
}
}
reject(reason) {
if (this.status === MyPromise.PENDING) {
setTimeout(() => {
this.status = MyPromise.REJECTED;
this.reason = reason;
this.onRejectedCallback.forEach(cb => cb(this.reason));
})
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason }
let promise2;
if (this.status === MyPromise.FULFILLED) {
return promise2 = new MyPromise((resolve,reject) => {
setTimeout(() => {
try{
const x = onFulfilled(this.value);
MyPromise.resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e)
}
})
})
}
if (this.status === MyPromise.REJECTED) {
return promise2 = new MyPromise((resolve,reject) => {
setTimeout(() => {
try{
const x = onRejected(this.reason)
MyPromise.resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
})
}
if (this.status === MyPromise.PENDING) {
return promise2 = new MyPromise((resolve,reject) => {
// 向对了中装入 onFulfilled 和 onRejected 函数
this.onFulfilledCallback.push((value) => {
try{
const x = onFulfilled(value)
MyPromise.resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
this.onRejectedCallback.push((reason) => {
try{
const x = onRejected(reason)
MyPromise.resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
})
}
}
}
// 2.1 A promise must be in one of three states: pending, fulfilled, or rejected.
MyPromise.PENDING = "pending"
MyPromise.FULFILLED = "fulfilled"
MyPromise.REJECTED = "rejected"
MyPromise.resolvePromise = (promise2,x,resolve,reject) => {
let called = false;
/**
* 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
*/
if(promise2 === x){
return reject(new TypeError("cannot return the same promise object from onfulfilled or on rejected callback."))
}
if(x instanceof MyPromise){
// 处理返回值是 Promise 对象的情况
/**
* new MyPromise(resolve => {
* resolve("Success")
* }).then(data => {
* return new MyPromise(resolve => {
* resolve("Success2")
* })
* })
*/
if(x.status === MyPromise.PENDING){
/**
* 2.3.2.1 If x is pending, promise must remain pending until x is fulfilled or rejected.
*/
x.then(y => {
// 用 x 的 fulfilled 后的 value 值 y,去设置 promise2 的状态
// 上面的注视,展示了返回 Promise 对象的情况,这里调用 then 方法的原因
// 就是通过参数 y 或者 reason,获取到 x 中的 value/reason
// 拿到 y 的值后,使用 y 的值来改变 promise2 的状态
// 依照上例,上面生成的 Promise 对象,其 value 应该是 Success2
// 这个 y 值,也有可能是新的 Promise,因此要递归的进行解析,例如下面这种情况
/**
* new Promise(resolve => {
* resolve("Success")
* }).then(data => {
* return new Promise(resolve => {
* resolve(new Promise(resolve => {
* resolve("Success3")
* }))
* })
* }).then(data => console.log(data))
*/
MyPromise.resolvePromise(promise2, y, resolve, reject)
},reason => {
reject(reason)
})
}else{
/**
* 2.3 If x is a thenable, it attempts to make promise adopt the state of x,
* under the assumption that x behaves at least somewhat like a promise.
*
* 2.3.2 If x is a promise, adopt its state [3.4]:
* 2.3.2.2 If/when x is fulfilled, fulfill promise with the same value.
* 2.3.2.4 If/when x is rejected, reject promise with the same reason.
*/
x.then(resolve,reject)
}
/**
* 2.3.3 Otherwise, if x is an object or function,
*/
}else if((x !== null && typeof x === "object") || typeof x === "function"){
/**
* 2.3.3.1 Let then be x.then.
* 2.3.3.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
*/
try{
// then 方法可能设置了访问限制(setter),因此这里进行了错误捕获处理
const then = x.then;
if(typeof then === "function"){
/**
* 2.3.3.2 If retrieving the property x.then results in a thrown exception e,
* reject promise with e as the reason.
*/
/**
* 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
* 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
*/
then.call(x,y => {
/**
* If both resolvePromise and rejectPromise are called,
* or multiple calls to the same argument are made,
* the first call takes precedence, and any further calls are ignored.
*/
if(called) return;
called = true;
MyPromise.resolvePromise(promise2, y, resolve, reject)
},r => {
if(called) return;
called = true;
reject(r);
})
}else{
resolve(x)
}
}catch(e){
/**
* 2.3.3.3.4 If calling then throws an exception e,
* 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
* 2.3.3.3.4.2 Otherwise, reject promise with e as the reason.
*/
if(called) return;
called = true;
reject(e)
}
}else{
// If x is not an object or function, fulfill promise with x.
resolve(x);
}
}
MyPromise.deferred = function() {
const defer = {}
defer.promise = new MyPromise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
try {
module.exports = MyPromise
} catch (e) {
}
13、setInterval
- setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式
- setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。
- 由 setInterval() 返回的 ID 值可用作clearInterval()方法的参数。
要实现这个函数,可以通过setTimeout()来实现,我们也可以修改实现的效果,比如说按照叠加的周期进行输出。实现过程很简单:
//在外面定义一个timer标记我们的定时器,到时候可以关掉
let timer = null;
function mySetInterVal(fn, a, b) {
return {
start: () => {
timer = setTimeout(() => {
fn();
timer ? mySetInterVal(fn, a, b + b).start() : null;
}, a + b)
},
end: () => {
clearTimeout(timer);
timer = null;
}
}
}
mySetInterVal(()=>{console.log(123),500,500}).start(); //开始
mySetInterVal(()=>{console.log(123),500,500}).end(); //结束
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!