Home  >  Article  >  Web Front-end  >  How to implement call, apply and bind natively in js

How to implement call, apply and bind natively in js

不言
不言Original
2018-07-23 11:26:582059browse

The content shared with you in this article is about how to implement call, apply and bind natively in js. It has certain reference value. Friends in need can refer to it.

Because it is related to the issue pointed to by this, the usage of call, apply and bind can be said to be cliché. The main function of this article is to use js native methods to implement the three methods, to understand the principles, and to have a better grasp of relevant knowledge points. The native implementation of github address call, apply and bind

call and apply

A brief introduction: The call and apply methods both call a function using a specified this value and corresponding parameters. or method. The difference is that call passes multiple parameters, while apply passes an array.
For example:

var obj = {
  name: 'linxin'
}

function func(age, sex) {
  console.log(this.name,age,sex);
}

func.call(obj,12,'女');         // linxin 12 女
func.apply(obj, [18, '女']);        //linxin 18 女

Simulation implementation

Idea: The this point in JavaScript says: the function can also be called as a method of an object, then this refers to this Superior object. That is what we usually say, whoever calls, this will point to. So the implementation method is to add such a method to the incoming object and then execute this method. In order to keep the object persistent, the object is deleted after execution. Isn’t it very simple^-^.
First experience:

Function.prototype.newCall = function(context) {
  context.fn = this;  // 通过this获取call的函数
  context.fn();
  delete context.fn;
}
let foo = {
  value: 1
}
function bar() {
  console.log(this.value);
}
bar.newCall (foo); // 1

This completes the implementation of the basic version, but what if there are parameters to be passed?
So we can optimize it, because the number of parameters passed in is uncertain, so we can get it from the Arguments object, which is relatively simple. The problem is that the parameters are uncertain. How do we pass them into the function we want to execute? Here we have two options: one is through eval splicing, and the other is to use es6.
Experience upgrade (eval version):

Function.prototype.newCall = function(context) {
  context.fn = this;
  var args = [];
  for(var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
  }
  eval('context.fn(' + args +')');
  delete context.fn;
}
let person = {
  name: 'Abiel'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, '男'); // Abiel 25 男

Experience upgrade (ES6 version):

Function.prototype.newCall = function(context) {
  context.fn = this;  
  context.fn(...Array.from(arguments).slice(1));
  delete context.fn;
}
let person = {
  name: 'Abiel'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, '男'); // Abiel 25 男

The ES6 method is okay The
ES6 version can be upgraded without using arguments:

Function.prototype.newCall = function(context, ...parameter) {
  context.fn = this;  
  context.fn(...parameter);
  delete context.fn;
}
let person = {
  name: 'Abiel'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, '男'); // Abiel 25 男

In this way, we basically realize the call function, but there are still some hidden dangers and differences.
When the object itself has the fn method, there is a big problem.
When the object passed in by call is null, or some other type, the function will report an error.
Ultimate experience:

Function.prototype.newCall = function(context, ...parameter) {
  if (typeof context === 'object') {
    context = context || window
  } else {
    context = Object.create(null)
  }
  let fn = Symbol()
  context[fn] = this
  context[fn](...parameter);
  delete context[fn]
}
let person = {
  name: 'Abiel'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, '男'); // Abiel 25 男

After implementing call, apply has the same idea.
apply implementation:

Function.prototype.newApply = function(context, parameter) {
  if (typeof context === 'object') {
    context = context || window
  } else {
    context = Object.create(null)
  }
  let fn = Symbol()
  context[fn] = this
  context[fn](parameter);
  delete context[fn]
}

bind

bind is also a function method. Its function is to change the execution of this, and it can also pass multiple parameters. Different from call and apply, the bind method will not be executed immediately, but will return a function that changes the context this pointer. The original function has not been changed. And if the function itself is a function bound to this object, then apply and call will not execute as expected.
First experience:

Function.prototype.bind = function (context) {
  var me = this
  return function () { // bind之后得到的函数
    return me.call(context)  // 执行是改变this执行
  }
}

Added parameters:

Function.prototype.bind = function (context,...innerArgs) {
  var me = this
  return function (...finnalyArgs) {
    return me.call(context,...innerArgs,...finnalyArgs)
  }
}
let person = {
  name: 'Abiel'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
let personSayHi = sayHi.bind(person, 25)
personSayHi('男')

Related recommendations:

js module Analysis (namespace)

Detailed introduction to JS inheritance (prototype chain, constructor, combination, prototype, parasitic, parasitic combination, Class extends)

The above is the detailed content of How to implement call, apply and bind natively in js. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn