首页 > 编程语言 >#yyds干货盘点#聊聊javascript——call、applay、bind

#yyds干货盘点#聊聊javascript——call、applay、bind

时间:2022-11-16 22:34:58浏览次数:54  
标签:yyds bar name johnny bind applay call context foo

Call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

​使用方法:

let bar = {
name: 'johnny'
}
function foo() {
console.log(this.name)
}
foo.call(bar); // johnny

首先 call 是个原型方法,所以一个函数能很自然的使用此方法,即 foo 的 call 方法继承自 Function.prototype.call。其次,call 方法中传入的值会成为 foo 函数的 this 未来指向的对象

其本质是改变 this 指向,将 this 指向了(call 的)传入值。

实现 call

我们根据之前的描述来实现 call

Function.prototype.mycall = function(context = window, ...args) {
if (this === Function.prototype) {
return undefined // 防止 Function.prototype.mycall 直接调用
}
const fn = Symbol()
context[fn] = this;
const result = context[fn](...args)
delete context[fn]
return result
}

我们一步步分析,首先不支持​​Function.prototype.mycall​​ 直接调用,

Function.prototype.call(bar) // undefined

这个很好理解,Function.prototype 中本来就没有 this,调用了也XXX

let context = context || window

因为要考虑如果 context 传入的是 null 呢

foo.call(null);

如果是 null 的话,context 就指向 window,这个很好理解,防御性代码。

这是 call 函数的关键所在,我们在前文讲 call 示例时,就说到 call 会改变 this 指向,讲 this 指向传入的 context,所以我们在模式实现 call 时,就可以先将 this 存在 context 上的一个属性上,再执行它,this 的规则是谁调用它,它指向谁。这样 this 就指向了 context

delete context[fn]
return result

删除 context 属性,释放内存,并返回结果值 result

call 实现就是如此,测试一波

let bar = {
name: 'johnny'
}
function foo() {
console.log(this.name)
}
foo.mycall(bar);
const fn = Symbol()
context[fn] = this;
const result = context[fn](...args)

apply

apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象的形式)提供的参数。

​使用方法:

let bar = {
name: 'johnny'
}
function foo(age, hobby) {
console.log(this.name, age, hobby)
}
foo.apply(bar, [28, 'sleep']); // johnny 28 sleep
// call 使用方法
// foo.call(bar, 28, 'sleep');

apply 和 call 使用上差不太多,只是传参方式不同

foo.call(obj, param1, param2,...,paramN) // 传入一串参数
foo.apply(obj, [param1, param2,...,paramN]) // 第二参数为类数组

所以实现上 和 call 大差不差。


实现 apply

Function.prototype.myapply = function (context = window, args) {
if (this === Function.prototype) {
return undefined
}
const fn = Symbol()
context[fn] = this

let result;
if (!Array.isArray(args)) {
result = context[fn]()
} else {
result = context[fn](...args)
}

delete context[fn]
return result
}

测试一波 myapply

let bar = {
name: 'johnny'
}
function foo(age, hobby) {
console.log(this.name, age, hobby)
}
foo.myapply(bar, [28, 'sleep']); // johnny 28 sleep

bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

​使用方法:

let bar = {
name: 'johnny'
}
function foo(age) {
console.log(this.name, age)
}
// 返回一个函数
let bindBar = foo.bind(bar)

bindBar(28) // johnny 28

与 call、apply 一样,它是函数的原型方法,不过与它们不同的是,它是 ES5 新增的方法,它返回的是一个函数(并且它还支持传参)

看到返回的是一个函数,就说明 bind 方法是一个闭包。


实现 bind

Function.prototype.mybind = function (context, ...args1) {
if (this === Function.prototype) {
throw new TypeError('Error')
}
const _this = this
return function F(...args2) {
if (this instanceof F) {
return new _this(...args1, ...args2)
}
return _this.apply(context, args1.concat(args2))
}
}

我们分析如何实现 bind

首先 bind 不能原型方法调用,如果使用就提示报错

其次我们根据 bind 的一个特性,对其的使用分为两种

一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当作构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数

也就是说,我们要判断它是否为构造函数调用,如果是则用 new 调用当前函数;如果不是,则使用 apply 来出现 context

// 判断是否是构造函数
if (this instanceof F) {
// 如果是构造函数,则以 foo(即_this)为构造器调用函数
return new _this(...args1, ...args2)
}
// 如果非构造函数,则用 apply 指向代码
return _this.apply(context, args1.concat(args2))

测试一波

// 测试非构造函数使用
let bar = {
name: 'johnny'
}
function foo(age, hobby) {
console.log(this.name, age, hobby)
}
// 返回一个函数
let bindBar = foo.mybind(bar, 28)

bindBar('sleep') // johnny 28 sleep
// 测试构造函数时使用
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function() {
console.log('my name is ' + this.name)
}
let emptyObj = {}
var FakerPerson = Person.mybind(emptyObj)

var johnny = new FakerPerson('johnny', 28)
johnny.sayName() // my name is johnny

标签:yyds,bar,name,johnny,bind,applay,call,context,foo
From: https://blog.51cto.com/u_11365839/5857829

相关文章

  • docker使用bind9实现域名解析
    目录刷新服务修改配置文件从114缓存查询数据可以dig无法ping查看已经区域解析,并添加新的解析项在linux安装局域网certrndc查看默认的解析条目添加自己......
  • #yyds干货盘点# 动态规划专题:二维差分
    1.简述:描述给你一个n行m列的矩阵,下标从1开始。接下来有q次操作,每次操作输入5个参数x1,y1,x2,y2,k表示把以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的每个元素都加上k,......
  • #yyds干货盘点# LeetCode 腾讯精选练习 50 题:数组中的第K个最大元素
    题目:给定整数数组nums和整数k,请返回数组中第k个最大的元素。请注意,你需要找的是数组排序后的第k个最大的元素,而不是第k个不同的元素。你必须设计并实现时间复杂度......
  • #yyds干货盘点#【愚公系列】2022年11月 微信小程序-实时音视频录制
    前言小程序的实时音视频播放需要先去微信开发者平台开通权限,「开发」-「接口设置」中自助开通该组件权限。类目属性如下:一级类目/主体类型二级类目小程序内容场景......
  • bind:tap与 catcg:tap 的区别
    介绍事件bindtap和catchtap都属于点击事件,绑定之后,点击组件是可以触发这个函数的。区别bindtap:子元素使用bindtap绑定事件后,执行的时候,会冒泡到父元素(触发父元......
  • bind原理图释
    (原文:http://blog.think-async.com/2010/04/bind-illustrated.html)转载:https://www.cnblogs.com/xusd-null/p/3698969.html本文解释了bind 是如何工作的。为了清晰,我......
  • 解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not fou
      我的问题产生与下面图片毫无关系,如果你参照下面的解决办法无法解决,可以看看applicationContext.xml中<beans> </beans>标签中的配置,看import标签是不是在bean标签的......
  • 探究Kubectl默认使用的Role和Rolebinding,以及如何通过认证和授权的
    默认情况下,我们能很方便地通过kubectl访问集群内的资源。但是你有没有想过,它在访问集群过程中,是如何通过认证和授权过程的,以及在默认开启了RABC时,它使用了什么样的角色和......
  • 状态机的技术选型,yyds!
    前言今天跟大家分享一个关于“状态机”的话题。状态属性在我们的现实生活中无处不在。比如电商场景会有一系列的订单状态(待支付、待发货、已发货、超时、关闭);员工提交请......
  • #yyds干货盘点# 动态规划专题:abb
    1.简述:描述leafee最近爱上了abb型语句,比如“叠词词”、“恶心心”leafee拿到了一个只含有小写字母的字符串,她想知道有多少个"abb"型的子序列?定义:abb型字符串满足以......