- Published on
手写原理实现
- Authors
- Name
- LIZHI
new的原理
new是javascript的一个关键词,用来创建对象的实例,它的实现思路如下:
创建一个空对象obj 将obj象的原型指向构造函数的prototype 执行构造函数,将obj作为其运行时的this 如果构造函数返回了一个对象,则用这个对象作为new的结果,反之则返回obj
function myNew(fn, ...args) {
const obj = Object.create(fn.prototype);
const res = fn.apply(obj, args);
return typeof res === 'object' ? res : obj;
}
instanceof原理
instanceof的主要作用是判断某个实例是否属于某种类型,也可以判断一个实例是否是其父类型或者祖先类型的实例。
实现思路:通过原型链往上一级一级查找,直到找到和当前对象的原型相等的原型。
function myInstanceof(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.create()原理
实现思路:传入一个对象,以该对象作为原型构造出一个新对象。
function myCreate(proto) {
if (typeof proto !== 'object') {
throw new TypeError('Object prototype may only be an Object or null');
}
function F() {}
F.prototype = proto;
return new F();
}
实现一个发布订阅模式 EventEmitter
通过初始化一个events对象保存事件和监听函数的映射关系 on:监听事件,将事件名eventName和回调函数 fn 存入events中 emit:触发事件,通过eventName事件名取出对应的回调函数并执行 off:取消监听,通过eventName事件名和找到对应的回调函数fn并将其移除 once:只触发一次,在触发fn时,调用off方法将其移除
class EventEmitter {
constructor() {
this.events = {}
}
on(eventName, fn) {
this.events[eventName] ? this.events[eventName].push(fn) : (this.events[eventName] = [fn])
return this
}
emit(eventName) {
(this.events[eventName] || []).forEach(fn => fn())
return this
}
off(eventName, fn) {
if (this.events[eventName]) {
this.events[eventName] = fn ? this.events[eventName].filter(item => item !== fn) : []
}
return this
}
once(eventName, callback) {
const fn = () => {
callback()
this.off(eventName, fn)
}
this.on(eventName, fn)
}
}
reduce原理
实现思路:
如果传入了初始值 initialValue,那么就将此值作为调用回调函数的第一个入参,然后循环的索引 i 从0开始 未传入初始值 initialValue,将数组的第一项作为 initialValue,然后循环的索引 i 从1开始 从 i 开始循环到 数组的length - 1,将上一次的输出作为下一次循环的第一个参数
function myReduce(arr, callback, initialValue) {
let i = 0;
let result = initialValue;
if (initialValue === undefined) {
result = arr[0];
i = 1;
}
for (; i < arr.length; i++) {
result = callback(result, arr[i], i, arr);
}
return result;
}
call/apply/bind原理
call、apply和bind都是用于改变函数this,call和apply的区别是传入的参数上有差异,apply只有两个参数,第二个参数是一个数组,call则是可以传入N个参数,第二个参数到第N个参数会当做参数传递给调用的fn,而bind与它们的区别是bind是静态绑定 。 实现思路:
bind:将当前函数赋值给传入的上下文context的某个属性,比如fn,然后通过context.fn调用 apply:除了参数的处理不一样,其它和bind相同 bind: 返回一个新函数,在新函数里面调用原函数,并将this指向它
// call
Function.prototype.myCall = function (context, ...args){
context = context || window
context.fn = this
const res = context.fn(...args)
delete context.fn
return res
}
// apply
Function.prototype.myApply = function (context, arg){
context = context || window
context.fn = this
let res
if (Array.isArray(arg)) {
res = context.fn(...arg)
} else {
res = context.fn()
}
delete context.fn
return res
}
// bind
Function.prototype.myBind = function (context, ...args){
const _this = this
return function F(){
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, [...args, ...arguments])
}
}
map
数组的map方法。
实现思路:
- 创建一个空数组,用于存储映射后的结果
- 遍历数组,对每个元素执行回调函数,并将结果添加到新数组中
- 返回新数组
function myMap(arr, callback) {
const result = []
for (let i = 0; i < arr.length; i++) {
result.push(callback(arr[i], i, arr))
}
return result
}