call、apply和bind的原生完成

由于关乎到了this指向的题目,call、apply和bind的用法能够说是陈词滥调了。这篇文章的重要作用是应用js原生要领对三个要领举行完成,升入相识个中的道理,对相干知识点有更好的控制。github地点call、apply和bind的原生完成

call与apply

简朴引见:call和apply要领都是运用一个指定的this值和对应的参数前提下挪用某个函数或要领。区分则在于call是经由过程传多个参数的体式格局,而apply则是传入一个数组。
举个例子:

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 女

模仿完成

思绪:在JavaScript中的this指向说到了:函数还能够作为某个对象的要领挪用,这时刻this就指这个上级对象。也就是我们日常平凡说的,谁挪用,this就指向谁。所以完成的要领就是在传入的对象中增加这么一个要领,然后再去实行这个要领。为了坚持对象一向,在实行完以后再把这个对象给删除了。是否是很简朴^-^。
初体验

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

如许就完成了基本版本的完成,然则假如说有传参数呢?
所以我们能够举行优化一下,由于传入的参数数目是不确定的,所以我们能够从Arguments对象中去猎取,这个比较简朴。题目是参数是不确定的,我们怎样传入到我们要实行的函数中去呢 ? 这里我们有两种挑选:一种是经由过程eval拼接的体式格局,另一种就要用到es6了。
体验晋级(eval版本):

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 男

体验晋级(ES6版本):

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 男

让然ES6的要领还能够不用到arguments就可以完成
ES6版本再晋级:

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 男

如许我们基本上完成了call的功用,然则照样存在一些隐患和区分。
当对象自身就有fn这个要领的时刻,就有大题目了。
当call传入的对象是null的时刻,或许其他一些范例的时刻,函数会报错。
最终体验:

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 男

完成了call以后,apply也是一样的思绪。
apply完成:

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也是函数的要领,作用也是转变this实行,同时也是能传多个参数。与call和apply差别的是bind要领不会马上实行,而是返回一个转变上下文this指向后的函数,原函数并没有被转变。而且假如函数自身是一个绑定了 this 对象的函数,那 apply 和 call 不会像预期那样实行。
初体验:

Function.prototype.bind = function (context) {
  var me = this
  return function () { // bind以后获得的函数
    return me.call(context)  // 实行是转变this实行
  }
}

到场参数:

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('男')

    原文作者:Abiel
    原文地址: https://segmentfault.com/a/1190000015724112
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞