5

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

 1 year ago
source link: https://blog.51cto.com/u_11365839/5857829
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

精选 原创

尼羲 2022-11-16 21:53:18 博主文章分类:技术扯淡 ©著作权

文章标签 构造函数 数组 方法调用 文章分类 JavaScript 前端开发 yyds干货盘点 阅读数162

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() 被调用时,这个新函数的 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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK