preface
Now the front end of the bar is getting higher and higher, not just write pages so simple. Modularity, automation, cross-end development, etc. are increasingly required, but these need to be built on our solid foundation. No matter how the framework and model change, the basic principles of the firm to quickly adapt to the changes in the market. Here are some common source implementations:
- The call to realize
- The bind to realize
- The new implementation
- Instanceof implementation
- Object. The create implementation
- Deep copy implementation
- Publish subscribe model
call
Call is used to change the reference of the function this and to execute the function
In general, the function’s “this” refers to the person who called it. Using this feature, you can change the point of this function by calling it as a property of an object. This is called implicit binding. The apply implementation does the same, simply changing the input parameter form.
let obj = {
name: 'JoJo'
}
function foo(){
console.log(this.name)
}
obj.fn = foo
obj.fn() // log: JoJo
Copy the code
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)
letres = othis._fn(... arg)Reflect.deleteProperty(othis, '_fn') // Delete the _fn attribute
return res
}
Copy the code
use
let obj = {
name: 'JoJo'
}
function foo(){
console.log(this.name)
}
foo.mycall(obj) // JoJo
Copy the code
bind
Bind changes the reference to this and returns a function
Note:
- As a constructor call, this points to
- Maintaining 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 the class Array into an Array
let arg = Array.prototype.slice.call(arguments.1)
let NOP = function(){}
let fBound = function(){
let arg_ = Array.prototype.slice.call(arguments)
// New binding is higher than explicit binding
// When called as a constructor, the pointer is left unchanged
// Use instanceof to determine if it is a constructor call
return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_))
}
// Maintain the prototype
if(this.prototype){
NOP.prototype = this.prototype
fBound.prototype = new NOP()
}
return fBound
}
Copy the code
use
let obj = {
msg: 'JoJo'
}
function foo(msg){
console.log(msg + ' ' + this.msg)
}
let f = foo.mybind(obj)
f('hello') // hello JoJo
Copy the code
new
New uses the constructor to create an instance object, adding the this property and method to the instance object
2. The process of new:
- Create a new object
- The new object __proto__ refers to the constructor prototype
- New object adds property method (this points to)
- Returns the new object to which this refers
function new_(){
let fn = Array.prototype.shift.call(arguments)
if(typeoffn ! ='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
}
Copy the code
In ES5, we can simplify this process by using object.create:
function new_(){
let fn = Array.prototype.shift.call(arguments)
if(typeoffn ! ='function') {throw fn + ' is not a constructor'
}
let obj = Object.create(fn.prototype)
let res = fn.apply(obj, arguments)
return typeof res === 'object' ? res : obj
}
Copy the code
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 does not exist in the prototype chain, otherwise exist.
function instanceof_(left, right){
left = left.__proto__
while(left ! == right.prototype){ left = left.__proto__// Look for the prototype and while it again
if(left === null) {return false}}return true
}
Copy the code
Object.create
Object.create creates a new Object, using an existing Object to provide the __proto__ of the newly created Object. The second optional argument is a property describing the Object
function objectCreate_(proto, propertiesObject = {}){
if(typeofproto ! = ='object' && typeofproto ! = ='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
}
Copy the code
Deep copy
A deep copy creates an identical copy of an object with different reference addresses. A deep copy is a good choice when you want to use an object but don’t want to modify the original object. Here we implement a base version that only makes deep copies of objects and arrays.
Implementation idea: traversal object, reference type use recursion to continue to copy, basic type direct assignment
function deepClone(origin) {
let toStr = Object.prototype.toString
letisInvalid = 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
}
Copy the code
Publish subscribe model
The publis-subscribe pattern enables complete decoupling between modules in real development, where modules only need to be concerned with registering and triggering events.
Publish and subscribe 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 taskQueue = this.task[name]
if(taskQueue && taskQueue.length > 0){
taskQueue.forEach(cb= >{ cb(... arg) }) } }off(name, cb){
let taskQueue = this.task[name]
if(taskQueue && taskQueue.length > 0) {if(typeof cb === 'function') {letindex = taskQueue.indexOf(cb) index ! = -1 && taskQueue.splice(index, 1)}if(typeof cb === 'undefined') {this.task[name].length = 0}}}once(name, cb){
let callback = (. arg) = > {
this.off(name, callback) cb(... arg) }typeof cb === 'function' && this.on(name, callback)
}
}
Copy the code
use
let bus = new EventBus()
bus.on('add'.function(a,b){
console.log(a+b)
})
bus.emit('add'.10.20) / / 30
Copy the code