preface
We must get rid of the comfort zone, only with time can you get what you want, or the same words, what is the use of light envy, we have to act.
Call and apply
If we want to change the this reference inside a function, there are three methods, of which call and apply are similar
const res = myFn.call(context, ... args); const res = myFn.apply(context, args);Copy the code
In both cases, the function context and other parameters are passed in and the return value is obtained. The other parameters are passed in different ways.
Therefore, implementing these two methods is relatively simple:
- Declare a myCall/myApply function
- Internally through
arguments
Gets the context passed incontext
, and other remaining parametersargs
- The function for that method is called
fn
(fn = this) mounts tocontext
on - through
context.fn
Method to call - Deletes the mounted temporary property and returns the result
myCall(context, ... Args) {// Check whether the caller is a function. If (typeof this! == "function") { throw new Error("need function"); } const fnKey = Symbol(); Context is null, undefined, etc. If (! context) { context = window; } // Mount, call context[fnKey] = this; const res = context[fnKey](... args); delete context[fnKey]; return res; }Copy the code
bind
Bind is special here because it not only changes the context this, but also returns a completely new function (instead of calling it directly). Here are a few things to note:
Additional arguments passed when bind
It’s going to come laterThe actual arguments passed when called
before- The returned function may be
new
Operator call
new
No. The new one
What happened to the new process?
-
Create a new object, obj
-
__proto__ = cons. prototype or object.setProtoTypeof (obj, cons.prototype).
-
Call cons. call(obj,… args)
-
Judge by the return value of the constructor
new
What to return- If the constructor returnsThe base typeIf there is no return statement, undefined is returned
new
The action returns the newly created obj object - If the constructor returns a reference type, the new operator is ignored and the constructor’s return value is returned
- If the constructor returnsThe base typeIf there is no return statement, undefined is returned
Write a new
function New(fn, ... args) { const obj = {}; Object.setPrototypeOf(obj, fn); Prototype // const obj = object. create(fn. Prototype) const res = fn. Call (obj,... args); Return res instanceof Object? res : obj; }Copy the code
To realize the bind
myBind(context, ... preArgs) { const fn = this; if (typeof fn ! == "function") { throw new Error("need function"); } function newFn(... Args) {// If the external is called by the new operator // if it is, the previously passed context will be nullified.the procedure this refers to the newly created obj will call(obj) the constructor. So the current context this is obj const isByNew = this instanceof fn? this : context; / /!!!!! Focus!!!!! const newContext = isByNew ? this : context; return fn.call(newContext, ... preArgs, ... args); } // make sure that the arrow function does not have a prototype NewFn. Prototype = object.create (fn. Prototype); } return newFn; Function (prototype) {// const F = function () {}; // F.prototype = prototype; // return new F(); / /}Copy the code
The logic here is a little convoluted, but it’s easy to understand the isByNew process if you know that cons. call(obj) happens to new.
Extend the instanceof operator
Don’t sleep, we can expand, and finally instanceof
The instanceof operator is used to determine if there is a reference to the constructor’s prototype object on the prototype chain (__proto__ property) of the object
SetPrototype (obj, cons. prototype) obj instanceof Cons === true
The soul of torture
What if I do a bind to the arrow function?
We might as well try
const arrow = () => { console.log(this.a) }; arrow(); // undefined a = 1; arrow(); // 1 const bindArrow = arrow.bind({a: 2}); bindArrow(); / / 1Copy the code
As you can see, when we bind the arrow function, the context that was passed in is ignored.