哪些网站可以找到做跨境电商的公司,查询网站备案号,单位门户网站功能,微信公众号开发软件1、实现instanceof运算符
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上#xff0c;运算符左侧是实例对象#xff0c;右侧是构造函数。
const isInstanceof function(left,right){let proto Object.getPrototypeOf(left);while(true… 1、实现instanceof运算符
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上运算符左侧是实例对象右侧是构造函数。
const isInstanceof function(left,right){let proto Object.getPrototypeOf(left);while(true){if(proto null) return false;if(proto right.prototype) return true;proto Object.getPrototypeOf(proto);}
};
// Object.getPrototypeOf(obj1)方法返回指定对象obj1的原型如果没有继承属性则返回null。2、实现new操作符
new执行过程
创建一个新对象新对象的[[prototype]] 特性指向构造函数的prototype属性构造函数内部的this指向新对象执行构造函数如果构造函数返回非空对象则返回该对象否则返回新对象
代码如下
const isNew function(fn,...arg){let instance Object.create(fn.prototype);let res fn.apply(instance,arg);return res ! null (typeof res Object||typeof resFunction) ? res:instance;
}3、实现bind方法
改变函数内的this的值并传参返回一个函数。
const iSBind function(thisObj,...args) {const originFunc this;const boundFunc function(...args1){// 解决bind之后对返回函数new的问题if(new.target){if(originFunc.prototype){bounfFunc.prototype originFunc.prototype;}const res originFunc.apply(this,args.concat(args1));return res ! null (typeof res object||typeof res function)?res:this;}else{return originFunc.apply(thisObj,args.concat(args1));}};//解决length 和name属性的问题const desc Object.getOwnPropertyDescriptors(originFunc);Object.defineProperties(boundFunc,{lengthObject.assign(desc.length,{value:desc.lengthargs.length?0:(desc.length-args.length)}),name:Object.assign(desc.name,{value:bound${desc.name.value}}) });return boundFunc;
}
// 保持bind的数据属性一致
Object.defineProperty(Function.prototype,isBind,{value:isBind,enumerable:false,configurable:true,writable:true
})实现函数的bind方法核心是利用call绑定this的指向同时考虑了一些其它的情况例如
bind返回的函数被new调用作为构造函数时绑定的值会失效并且改为new指定的对象 定义了绑定后函数的length属性和name属性不可枚举性 绑定后函数的prototype需指向原函数的prototype(真实情况中绑定后的函数是没有prototype的取而代之在绑定后的函数中有个内部属性[[TargetFunction]]保存原函数当将绑定后的函数作为构造函数时将创建的实例的__proto__指向[[TargetFunction]]的prototype,这里无法模拟内部属性所以直接声明了一个prototype属性) 4、实现call方法
用指定的this值和参数来调用函数
const isCall function(thisObj,...args){thisObj(thisObj undefined || thisObj null)?window:Object(thisObj);let fn Symbol(fn);thisObj[fn] this;let res thisObj[fn](...args);delete thisObj[fn];return res;
}
// 保持call的数据属性一致
Object.defineProperty(Function.prototype,isCall,{value:isCall,enumerable:false,configurable:true,writable:true,
});原理就是将函数作为传入的上下文参数context的属性执行这里为了防止属性冲突使用了ES6的Symbol类型
5、函数柯里化
将一个多参数函数转化为多个嵌套的单参数函数。
const curry function(targetFn) {return function fn(...rest){if(targetFn.length rest.length) {return targetFn.apply(null,rest);}else{return fn.bind(null,...rest);}};
};
// 用法
function add(a,b,c,d){return a*b*c*d
}
console.log(柯里化,curry(add)(1)(2)(3)(4))6、发布订阅
class EventBus {constructor() {Object.defineProperty(this,handles,{value:{}});}on(eventName,listener) {if(typeof listener !function) {console.error(请传入正确的回调函数);return;}if(!this.handles[eventName]) {this.handles[eventName] [];}this.handles[eventName].push(listener);}emit(eventName,...args) {let listeners this.handles[eventName];if(!listeners) {console.warn(${eventName}事件不存在);return;}for(const listener of listeners) {listener(...args);}}off(eventName,listener) {if(!listener) {delete this.handles[eventName];return;}let listeners this.handles[eventName];if(listeners $$ listeners.length) {let index listeners.findIndex(item item listener);listeners.splice(index,1);}}once(eventName,listener){if(typeof listener !function) {console.error(请传入正确的回调函数);return ;}const onceListener (...args) {listener(...args);this.off(eventName,listener);};this.on(eventName,onceListener);}
}自定义事件的时候用到注意一些边界的检查
7、深拷贝
const deeoClone function(source) {if(source null || typeof source !object) {return source;}let res Array.isArray(source) ? []:{};for(const key in source) {if(source.hansOwnProperty(key)) {res[key] deepClone(source[key]);}}return res;
}
// 以上这个是深拷贝很基础的版本,但存在一些问题例如循环引用递归爆栈。以下这个为进阶版的。const deepClone1 function (obj) {let cloneObj;if( obj typeof obj ! object){cloneObj obj;}else if(obj typeof obj object){cloneObj Array.isArray(obj) ? []:{};for(let key in obj){if(obj.hasOwnProperty(key)){if(obj[key] typeof obj[key] object){cloneObj[key] deepClone1(obj[key]);}else{cloneObj[key] obj[key];}}}}return cloneObj;
}8、实现ES6的Class
用构造函数模拟class只能用new创建不可以直接调用另外注意以下属性的描述符
const checkNew function(instance,con) {if(!(instance instanceof con)){throw new TypeError(Class constructor${con.name} connot be invoked without new);}
};
const defineProperties function(target,obj) {for(const key in obj){Object.defineProperty(target,key,{value:obj[key],enumerable:false,configurable:true,writable:true,}); }
}
const createClass function(con,proto,staticAttr){proto defineProperties(con.prototype,proto);staticAttr defineProperties(con,staticAttr);return con;
}
// 用法
function Person(name) {checkNew(this,Person);this.name name;
}
var PersonClass createClass(Person,{getName:function(){return this.name;}getAge:function(){}
})9、实现ES6的继承
ES6内部使用寄生组合式继承首先用Object.create继承原型并传递第二个参数以将父类构造函数指向自身同时设置数据属性描述符。然后用Object.setPrototypeOf继承静态属性和静态方法。
const inherit function(subType,superType){// 对superType进行类型判断if(typeof superType ! function superType ! null){throw new TypeError(Super expression must either be null or a function);}subType.prototype Object.create(superType superType.prototype,{constructor:{value:subType,enumerable:false,configurable:true,writable:true}});// 继承静态方法superType Object.setPrototypeOf(subType,superType);
}
// 用法
function superType(name) {this.name name;
}
superType.staticFn function(){console.log(这是staticFn);
}
superType.prototype.getName function(){console.log(name:this.name);
}
function subType(name,age){superType.call(name:this.name);this.age age;
}
inherit(subType,superType);
// 必须在继承之后再往subType中添加原型方法否则会被覆盖掉
subType.prototype.getAge function(){console.log(age:this.age);
}
let subTypeInstance new subType(Twittytop,30);
subType.staticFn();
subTypeInstance.getName();
subTypeInstance.getAge();10、使用reduce实现数组flat方法
const selfFlat function (depth 1){let arr Array.prototype.slice.call(this);if(depth 0) return arr;return arr.reduce((pre,cur) {if(Array.isArray(cur)) {return [...pre,...selfFlat.call(cur,depth - 1)]} else {return [...pro,cur]}},[])
}因为selfFlat是依赖this指向的所以在reduce遍历时需要指定selfFlat的this指向否则会默认指向window从而发生错误。 原理通过reduce遍历数组遇到数组的某个元素扔是数组时通过ES6的扩展运算符对其进行降维ES5可以使用concat方法而这个数组元素可能内部还嵌套数组所以需要递归调用selfFlat。 同时原生的flat方法支持一个depth参数表示降维的深度默认为1即给数组降一层维
11、CO协成实现
function co(gen) {return new Promise(function(resolve,reject) {if( typeof gen function) gen gen();if(!gen||typeof gen.next !function) return resolve(gen);onFulfilled();function onFulfilled(res) {let ret;try {ret gen.next(res);} catch(e){return reject(e)}next(ret);}function onRejected(err) {let ret;try{ret gen.throw(err);} catch(e){return reject(e)}next(ret);}function next(ret) {if(ret.done) return resolve(ret.value);let val Promise.resolve(ret.value);return val.then(onFulfilled,onRejected);}})
}使用方法
co(function*() {let res1 yield Promise.resolve(1);console.log(res1);let res2 yield Promise.resolve(2);console.log(res2);let res3 yield Promise.resolve(3);console.log(res3)return res1res2res3;
}).then(val {console.log(add:val);
},function(err){console.error(err.stack);
})co接收一个生成器函数当遇到yield时就暂停执行交出控制权当其他程序执行完毕后将结果返回并从中断的地方继续执行如此往复一直到所有的任务都执行完毕最后返回一个Promise并将生成器函数的返回值作为resolve值。
我们将*换成async将yield换成await时就和我们经常用到的async/await是一样的所以说async/await是生成器函数的语法糖。 JavaScript手写面试题涵盖了很多不同的方面从实现一些内置方法到处理异步编程。以下是一些常见的手写面试题 实现instanceof运算符可以通过检查对象的原型链来判断一个对象是否是某个构造函数的实例。 实现new操作符可以通过创建一个新对象并将构造函数的原型指向该对象来模拟new操作符的行为。 实现bind方法bind方法可以创建一个新的函数该函数的this值被绑定到指定的对象。 实现call方法call方法可以调用一个函数并指定this值以及传递任意数量的参数。 函数柯里化柯里化是一种将多个参数的函数转换成一系列接受一个参数的函数的技术。 发布订阅模式发布订阅是一种消息传递的模式其中发送者发布者不会直接将消息发送给特定的接收者订阅者而是通过事件中心来管理消息的传递。 深拷贝深拷贝是指创建一个完全独立的对象其中包含原始对象的所有属性和嵌套对象的所有属性。 实现ES6的ClassES6的Class用于创建基于类的对象可以通过构造函数、原型和静态方法实现。 实现ES6的继承ES6的继承可以通过extends关键字和super函数实现。 使用reduce实现数组flat方法reduce方法可以将多维数组降维成一维数组。 实现CO协程协程是一种支持异步编程的技术可以通过生成器函数和Promise的组合来实现。
以上是一些常见的JavaScript手写面试题的概述。希望这些信息对你有所帮助。 常见js面试手写题“老大爷”看了都点赞加收藏。_js 手写面试题-CSDN博客