In js, call can change the pointer of this, bind can change the pointer of this, and return a function. How is this implemented? This article will take you step by step to implement these functions, and I hope it will be helpful to friends who are learning JavaScript.
Preface
Now the front-end threshold is getting higher and higher, and it is no longer just writing The page is so simple. Modularization, automation, cross-end development, etc. are gradually becoming requirements, but these need to be built on our solid foundation. No matter how the framework and model change, only by laying down the basic principles can we quickly adapt to market changes. Here are some commonly used source code implementations:
call implementation
bind implementation
new implementation
instanceof implementation
Object .create implementation
Deep copy implementation
Publish and subscribe mode
call
call is used to change the function this pointer , and execute the function
(recommendedjs tutorial, welcome to learn!)
Generally, whoever calls the function, the this of the function points to whom. Taking advantage of this feature, by using the function as an attribute of the object and calling it from the object, you can change the this point of the function. This is called implicit binding. Apply implements the same method, just change the input parameter form.
let obj = { name: 'JoJo' } function foo(){ console.log(this.name) } obj.fn = foo obj.fn() // log: JOJO
Implementation
Function.prototype.mycall = function () { if(typeof this !== 'function'){ throw 'caller must be a function' } let othis = arguments[0] || window othis._fn = this let arg = [...arguments].slice(1) let res = othis._fn(...arg) Reflect.deleteProperty(othis, '_fn') //删除_fn属性 return res }
Use
let obj = { name: 'JoJo' } function foo(){ console.log(this.name) } foo.mycall(obj) // JoJo
bind
bind is used to change the function this pointer and return a function
Note:
This points to the constructor call
Maintain the prototype chain
Function.prototype.mybind = function (oThis) { if(typeof this != 'function'){ throw 'caller must be a function' } let fThis = this //Array.prototype.slice.call 将类数组转为数组 let arg = Array.prototype.slice.call(arguments,1) let NOP = function(){} let fBound = function(){ let arg_ = Array.prototype.slice.call(arguments) // new 绑定等级高于显式绑定 // 作为构造函数调用时,保留指向不做修改 // 使用 instanceof 判断是否为构造函数调用 return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_)) } // 维护原型 if(this.prototype){ NOP.prototype = this.prototype } fBound.prototype = new NOP() return fBound }
Use
let obj = { msg: 'JoJo' } function foo(msg){ console.log(msg + '' + this.msg) } let f = foo.mybind(obj) f('hello') // hello JoJo
new
new uses the constructor to create an instance object, and adds this attribute and method to the instance object.
The process of new:
Create new Object
New object __proto__ points to the constructor prototype
New object adds attribute method (this points to)
Returns the new object pointed to by this
function new_(){ let fn = Array.prototype.shift.call(arguments) if(typeof fn != 'function'){ throw fn + ' is not a constructor' } let obj = {} obj.__proto__ = fn.prototype let res = fn.apply(obj, arguments) return typeof res === 'object' ? res : obj }
instanceof
instanceof determines whether the prototype on the left exists in the prototype chain on the right.
Implementation idea: Look up the prototype layer by layer. If the final prototype is null, it proves that it does not exist in the prototype chain, otherwise it does.
function instanceof_(left, right){ left = left.__proto__ while(left !== right.prototype){ left = left.__proto__ // 查找原型,再次while判断 if(left === null){ return false } } return true }
Object.create
Object.create creates a new object, using an existing object to provide the __proto__ of the newly created object, the second optional The parameter is the attribute description object
function objectCreate_(proto, propertiesObject = {}){ if(typeof proto !== 'object' || typeof proto !== 'function' || proto !== null){ throw('Object prototype may only be an Object or null:'+proto) } let res = {} res.__proto__ = proto Object.defineProperties(res, propertiesObject) return res }
Deep copy
Deep copy creates an identical copy of the object, but the reference addresses of the two are different. Deep copying is a good choice when you want to use an object but don't want to modify the original object. A basic version is implemented here, only making deep copies of objects and arrays.
Implementation idea: traverse the object, use recursion to continue copying the reference type, and directly assign the basic type
function deepClone(origin) { let toStr = Object.prototype.toString let isInvalid = toStr.call(origin) !== '[object Object]' && toStr.call(origin) !== '[object Array]' if (isInvalid) { return origin } let target = toStr.call(origin) === '[object Object]' ? {} : [] for (const key in origin) { if (origin.hasOwnProperty(key)) { const item = origin[key]; if (typeof item === 'object' && item !== null) { target[key] = deepClone(item) } else { target[key] = item } } } return target }
Publish and subscribe mode
Publish and subscribe mode in practice Complete decoupling between modules can be achieved during development, and the modules only need to focus on the registration and triggering of events.
Publish and subscribe mode to implement EventBus:
class EventBus{ constructor(){ this.task = {} } on(name, cb){ if(!this.task[name]){ this.task[name] = [] } typeof cb === 'function' && this.task[name].push(cb) } emit(name, ...arg){ let taskQueen = this.task[name] if(taskQueen && taskQueen.length > 0){ taskQueen.forEach(cb=>{ cb(...arg) }) } } off(name, cb){ let taskQueen = this.task[name] if(taskQueen && taskQueen.length > 0){ let index = taskQueen.indexOf(cb) index != -1 && taskQueen.splice(index, 1) } } once(name, cb){ function callback(...arg){ this.off(name, cb) cb(...arg) } typeof cb === 'function' && this.on(name, callback) } }
Use
let bus = new EventBus() bus.on('add', function(a,b){ console.log(a+b) }) bus.emit('add', 10, 20) //30
The above is the detailed content of Manually implement call, bind, instanceof in js. For more information, please follow other related articles on the PHP Chinese website!