Brief introduction of how to 'Call, Apply and Bind'

柳芳
• 阅读 1167

关于 this

在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。

全局 this

window.something = 'I love JavaScript'
console.log(this.something) // 'I love JavaScript'
console.log(window === this) // true

调用全局 function

var a = 1
function test() { console.log(this.a) }
test() // 1 - still remains the window reference

调用对象中的 function

this.a = 'I am in the global scope'
function Test() {
  this.a = 'I am in the test scope'
  this.show = function() { console.log(this.a) }
}
Test.prototype.display = function () { console.log(this.a) }
var test = new Test() // updated the scope of this
test.show() // I am in the test scope
test.display() // I am in the test scope 

关于 call / apply

JavaScript 内部提供了一种机制,让我们可以自行手动设置 this 的指向。它们就是 call 与 apply。所有的函数都具有着两个方法。它们除了参数略有不同,其功能完全一样。它们的第一个参数都为 this 将要指向的对象。

一个最简单的继承

function Laptop(name, storage) {
  this.name = name
  this.storage = storage
}

function Dell(name, storage, company) {
  Laptop.call(this, 'Dell', 1024)
  this.company = company
}

console.log(new Dell('Dell', 1024, 'Dell Inc').storage)

改变 this

var obj = {
  entry: 'mammals-banana-tower',
  duration: 0
}

function breed(name) {
  console.log('Show this breeding info', name, this.entry, this.duration)
  console.log(this === obj)
}

breed() // this => window
breed.call(obj, 'Frank') // this => obj

注:当没有传递任何参数作为 call() 的第一个参数时,在非严格模式下,this 会指向 window。

实现一个简单的 call

var _call = function (that) {
  that = that ? Object(that) : window
  that.func = this

  function formatArgs(oArgs, sign) {
    var _args
    for (var i = 1, len = oArgs.length; i < len; i++) {
      _args.push(sign ? ('_param_' + i) : oArgs[i])
    }
    return _args
  }

  var args = formatArgs(arguments)
  var newFunc = (new Function('args', 'return that.func(' + formatArgs(args, true).toString() + ')'))(args)

  that.func = null
  return newFunc
}

关于 bind

() => {} 和 bind this

用过 React 的同学都知道,当使用 class component 时,需要在 constructor 绑定当前的成员函数,或者针对事件委托的情况下,也需要进行绑定;ES6 箭头函数可以让我们更专注于具体的实现逻辑,简化了 this 操作

// ES5
// <a onclick={this.handleClick.bind(this)}></a>
// constructor() { this.handleClick = this.handleClick.bind(this) }

// ES6
// <a onclick={() => handleClick()}></a>
// handleClick = () => {}

无效的 re-bound

var f = function() { console.log(this.text) }
f = f.bind({ text: 'I was bound' }).bind({ text: 'I won't be bound' })
f() // I was bound

很容易发现,f.bind() 返回的绑定函数对象仅在创建是保留当前的上下文(或者传入的参数),因此无法在第二次进行重绑定。

一个相对完善的 bind

var _bind = function (that) {

  var fBound,
    target = this,
    slice = Array.prototype.slice,
    toStr = Object.prototype.toString,
    args = slice.call(arguments, 1); // except that

  if (typeof target !== 'function' || toStr.call(target) !== '[object Function]') {
    throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  }

  var binder = function () {
    var oArgs = args.concat(slice.call(arguments))
    if (this instanceof fBound) {
      var result = target.apply(this, oArgs);
      return Object(result) === result ? result : this;
    } else {
      return target.apply(that, oArgs);
    }
  };

  var i = 0,
    params = [],
    paramLength = Math.max(0, target.length - args.length);

  for (; i < paramLength; i++) {
    params.push('_param_' + i);
  }

  fBound = (new Function(
    'binder',
    'return function(' + params.join(',') + ') { return binder.apply(this,arguments); }'
  ))(binder);

  // maintain the reference of prototype
  if (target.prototype) {
    var fNOP = function () { };
    fNOP.prototype = target.prototype;
    fBound.prototype = new fNOP();
    fNOP.prototype = null;
  }

  return fBound;
};

参考

https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://www.ecma-internationa...
https://javascript.info/bind
https://juejin.im/post/5c0605...
https://github.com/mqyqingfen...

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
巴拉米 巴拉米
4年前
bind、call、apply 区别?如何实现一个bind?
一、作用call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向那么什么情况下需要改变this的指向呢?下面举个例子var name"lucy";const obj{    name:"martin",    say:function (){        co
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Stella981 Stella981
3年前
JS 中的this指向问题和call、apply、bind的区别
this的指向问题一般情况下this对象指向调用函数的对象,全局环境中执行函数this对象指向window。functiona(){console.log(this);//输出函数a中的this对象}functionb(){};varc{name:"call"}
Stella981 Stella981
3年前
JavaScript之深入理解this
定义this是函数运行时自动生成的内部对象,即调用函数的那个对象。(不一定很准确的定义,但还算通俗易懂)在大多数情况下,this的值由函数调用方式决定,它不能在执行期间赋值来设置,它在每次执行下可能都有不同的值。全局执行环境(outsidefunction)在全局执行环境中,this一直
柳芳
柳芳
Lv1
暮雨不来春不去,花满地月朦胧。
文章
3
粉丝
0
获赞
0