Bind, apply, call three brothers

Realization idea ๐Ÿคจ

Change this by assigning a temporary property to the target object.

Precautions ๐Ÿ˜ฑ

  • For incoming context, when null | undefined point to global objects, when any other remaining type should call Object method.
  • Use globalThis instead of Window.
  • For Apply to determine if the second argument is an array (class array), do not iterate directly.
  • Determine if the caller type is function.
  • Notice the length attribute of the function returned by bind.

apply

//apply Function.prototype.apply = function (context, args = []) { if(typeof this ! == 'function'){ throw new Error(this + "it's not callable"); } // In fact, args supports an array-like object. // Class array object: as long as there is a 'length' attribute and '(0.. Length-1) 'range of integer attributes. if(! Array.isArray(args)){ throw new TypeError('CreateListFromArrayLike called on non-object'); } context = Object(context || globalThis); const _symbol = Symbol(); context[_symbol] = this; const result = context[_symbol](... args); delete context[_symbol]; return result; };Copy the code

call

//call Function.prototype.call = function (context,... args) { if(typeof this ! == 'function'){ throw new Error(this + "it's not callable"); } //args defaults to []. context = Object(context || globalThis); const _symbol = Symbol(); context[_symbol] = this; let result = context[_symbol](... args); delete context[_symbol]; return result; };Copy the code

bind

Function.prototype.bind = function(context,... args){ if(typeof this ! == 'function'){ throw new Error(this + "it's not callable"); } context = Object(context || globalThis); const _symbol = Symbol(); const target = this; return function(... rest){ context[_symbol] = target; let result = context[_symbol](... args,... rest); delete context[_symbol]; return result; }}Copy the code

The bind – consider the length

The following code can not understand the first look at juejin.cn/post/705567…

Function.prototype.bind = function (context, ... rest) { if(typeof this ! == 'function'){ throw new Error(this + "it's not callable"); } let target = this; const targetLength = Math.max(0, target.length - rest.length); const list = []; for (let i = 0; i < targetLength; i++) { list[i] = `$${i}`; } const binder = function(){ return target.apply(context,[...rest,...arguments]); } return Function( `binder`, `return function(${list.join(",")}){ return binder(... arguments); }` )(binder); };Copy the code

supplement

This article just released and I was not a serious test, lead to many mistakes ๐Ÿ˜Ÿ (in this also thank you correct comments area), now I have to use the jest has carried on the simple test to the code, make sure similar mistakes won’t happen (sure, of course, there will be less obvious errors, welcome to correct).

All test cases – Github

//bind.test.js Function.prototype.myBind = function (context, ... rest) { if (typeof this ! == "function") { throw new Error(this + "it's not callable"); } let target = this; const targetLength = Math.max(0, target.length - rest.length); const list = []; for (let i = 0; i < targetLength; i++) { list[i] = `$${i}`; } const binder = function () { return target.apply(context, [...rest, ...arguments]); }; return Function( `binder`, `return function(${list.join(",")}){ return binder(... arguments); }` )(binder); }; function fn(a, b, c) { return [a, b, c]; } function fn1() { return this; } the test (" bind ", () = > {/ / routine tests expect (fn) myBind (this, 1, 2, 3) the ()). ToEqual (fn) bind (this, 1, 2, 3) ()); 1 expect(fn.mybind (this)()).toequal (fn.bind(this)()); / / empty parameter test 2 expect (fn1 myBind () ()). ToEqual (fn1. The bind () ()); Let objTest = {test: 111}; expect(fn1.myBind(objTest)()).toEqual(fn1.bind(objTest)()); // expect(fdn.mybind (objTest).apply({test: 222})).toequal (fdn.mybind (objTest).apply({test: 333})); // expect(fn.mybind (this, 1).length).tobe (fn.bind(this, 1).length); //ToObject test expect(typeof NF1.mybind (1)()).tobe (typeof NF1.bind (1)()); });Copy the code

reference

Developer.mozilla.org/zh-CN/docs/…

Tc39. Es/ecma262 / # se…