说说bind、call、apply 区别?

callapply 都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。

除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组。

let a = {
    value: 1
}
function getValue(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}
getValue.call(a, 'yck', '24')
getValue.apply(a, ['yck', '24'])

bind和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind实现柯里化。

如何实现一个 bind 函数

对于实现以下几个函数,可以从几个方面思考

  • 不传入第一个参数,那么默认为 window
  • 改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?
Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

如何实现一个call函数

Function.prototype.myCall = function (context) {
  var context = context || window
  // 给 context 添加一个属性
  // getValue.call(a, 'yck', '24') => a.fn = getValue
  context.fn = this
  // 将 context 后面的参数取出来
  var args = [...arguments].slice(1)
  // getValue.call(a, 'yck', '24') => a.fn('yck', '24')
  var result = context.fn(...args)
  // 删除 fn
  delete context.fn
  return result
}

如何实现一个apply函数

Function.prototype.myApply = function (context) {
  var context = context || window
  context.fn = this

  var result
  // 需要判断是否存储第二个参数
  // 如果存在,就将第二个参数展开
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }

  delete context.fn
  return result
}

call/apply

作用

用来改变函数内部 this 的指向。

特点

任何函数都可以调用这两个方法,说明它们是添加在函数原型上的方法(Function.prototype)。

console.dir(Function.prototype)

调用 callapply 的函数会立即执行。

callapply 的返回值就是函数的返回值。

var name = '一尾流莺'
var obj = {
  name: 'warbler',
}
function foo() {
  console.log(this.name);
  return 'success'
}
foo.call(obj) //=> warbler
console.log(foo.call(obj)); // => success

调用 callapply 指向 undefined 或者 null ,会将 this 指向 window

function foo() {
  console.log(this)
}
foo.call(undefined)
foo.call(null)
foo.apply(undefined)
foo.apply(null)

调用 callapply 指向一个值类型, 会将 this 指向由它们的构造函数创建的实例。

function foo() {
  console.log(this)
}
foo.call(11)
foo.call('11')
foo.call(true)

[图片上传失败...(image-15f2a1-1666010480746)]

调用 callapply 指向一个引用类型, 会将 this 指向这个对象。

我们声明了一个全局变量 name 和一个全局作用域下的函数 foo

var name = '一尾流莺'
var obj = {
  name: 'warbler',
}
function foo() {
  console.log(this.name)
}
foo() //=> 一尾流莺

这段代码很好理解,name 等价于 window.name , foo() 等价于 window.foo() ,我们打印出this.name,当前的 this 指向它的调用者 window, 也就是 window.name 得到 一尾流莺。 但是如果我想打印出 warbler 该怎么办呢?在 obj 里面再定义一个 obj.fn 么? 当然不需要, 我们只需要调用 call/apply 改变 this 的指向,指向 obj 这个对象就可以了。这个时候 this.name 等价于 obj.name ,就得到了 warbler 。

foo.call(obj) //=> warbler
foo.apply(obj) //=> warbler

call 和 apply的区别

除了传参的形式不同没什么区别。

传给fn的参数写法不同:

  • call 接收多个参数,第一个为函数上下文也就是 this ,后边参数为函数本身的参数。
  • apply 接收两个参数,第一个参数为函数上下文 this,第二个参数为函数参数只不过是通过一个 数组 的形式传入的。

只要记住 apply 是以 a 开头,它传给 fun 的参数是 Array,也是以 a 开头的,就可以很好的分别这两个函数了。

手写call/apply

手写call

var name = '一尾流莺'
var obj = {
  name: 'warbler',
}
function foo() {
  console.dir(this);
  return 'success'
}

/**
* Object()方法
* 如果传入的是值类型 会返回对应类型的构造函数创建的实例
* 如果传入的是对象 返回对象本身
* 如果传入 undefined 或者 null 会返回空对象
*/
Function.prototype._call = function(ctx, ...args) {
  // 判断上下文类型 如果是undefined或者 null 指向window
  // 否则使用 Object() 将上下文包装成对象
  const o = ctx == undefined ? window : Object(ctx)
  // 如何把函数foo的this 指向 ctx这个上下文呢
  // 把函数foo赋值给对象o的一个属性  用这个对象o去调用foo  this就指向了这个对象o
  // 下面的this就是调用_call的函数foo  我们把this给对象o的属性fn 就是把函数foo赋值给了o.fn
  //给context新增一个独一无二的属性以免覆盖原有属性
  const key = Symbol()
  o[key] = this
  // 立即执行一次
  const result = o[key](...args)
  // 删除这个属性
  delete o[key]
  // 把函数的返回值赋值给_call的返回值
  return result
}

手写 apply

之前讲过,callapply 的唯一区别就是传递参数的不同,所以我们只需要改一下对参数的处理,其它的和 call 一致就可以了。

var age = 10
var obj = {
  age: 20,
}
function foo(a, b) {
  console.dir(this.age + a + b);
}
// 只需要把第二个参数改成数组形式就可以了。
Function.prototype._apply = function(ctx, array = []) {
  const o = ctx == undefined ? window : Object(ctx)
  //给context新增一个独一无二的属性以免覆盖原有属性
  const key = Symbol()
  o[key] = this
  const result = o[key](...array)
  delete o[key]
  return result
}
foo(3, 4) // => 17
foo._apply(obj, [3, 4]) //=> 27

bind

作用

也是用来改变函数内部 this 的指向。

bind 和 call/apply 的区别

是否立刻执行

  • call/apply 改变了函数的 this 上下文后 马上 执行该函数。
  • bind 则是返回改变了上下文后的函数, 不执行该函数

返回值的区别:

  • call/apply 返回 fun 的执行结果。
  • bind 返回 fun 的拷贝,并指定了 funthis 指向,保存了 fun 的参数。
var name = '一尾流莺'
var obj = {
  name: 'warbler',
}

// this 指向调用者document
document.onclick = function() {
  console.dir(this); // => #document
}

// this 指向 obj
document.onclick = function() {
  console.dir(this); // => #Object{name:'warbler}
}.bind(obj)

手写bind

Function.prototype._bind = function(ctx, ...args) {
  // 下面的this就是调用_bind的函数,保存给_self
  const _self = this
  // bind 要返回一个函数, 就不会立即执行了
  const newFn = function(...rest) {
    // 调用 call 修改 this 指向
    return _self.call(ctx, ...args, ...rest)
  }
  if (_self.prototype) {
    // 复制源函数的prototype给newFn 一些情况下函数没有prototype,比如箭头函数
    newFn.prototype = Object.create(_self.prototype);
  }
  return newFn
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容