大家好,欢迎来到IT知识分享网。
相信大家对于call、bind都不陌生,他们都可以动态改变函数执行时this指向,先来看看两个方法在MDN官网上的语法定义。
call:function.call(thisArg, arg1, arg2, …)bind:
function.bind(thisArg[, arg1[, arg2[, …]]])
call方法接收多个参数,第一个为函数上下文即this,其余为函数本身参数。
bind方法接收多个参数并返回一个新函数,第一个为函数上下文即this,其余参数作为返回函数的参数。
const obj = {foo: ‘bar’};
const fn = function(name) {
console.log(this.foo, name);
};
fn.call(obj, ‘ada’); // bar ada
fn.bind(obj, ‘ada’)(); // bar ada
图片源于网络,侵删
1. call方法的实现
call的特点:
第一个参数为要绑定的this,剩余参数为函数的实参。
那我们怎样改更改this的绑定呢?
我们知道当我们以对象的方法调用一个普通函数时,this始终指向当前调用的对象。
var value = 1;
function foo(x, y) {
console.log(this.value);
};
var obj = {
value: 2
};
foo.call(obj, 3, 4); // 2
// 相当于
obj.foo(3, 4);
思路:
- 返回当前函数执行后的结果。
- 删除该对象上的属性。
- 通过 对象 . 方法 执行这个函数。
- 将函数作为要更改this绑定的对象的一个属性。也就是把函数作为call方法中第一个参数中的一个属性。
特点:
- 当第一个参数(要更改的this绑定的对象)为null或者undefined时,this绑定为window(非严格模式)。如果为严格模式,均为第一个参数的值。
- 当call方法中第一个参数为除null和undefined外的基本类型(String,Number,Boolean)时,先对该基本类型进行”装箱”操作。
/
* @description: 实现call方法
* @param : context this要绑定的值
* @param : args 除第一个参数外的参数集合
* @return: 函数返回值
*/
Function.prototype.myCall = function(context, …args) {
let handler = Symbol();// 生成一个唯一的值,用来作为要绑定对象的属性key,储存当前调用call方法的函数
if(typeof this !== ‘function’) {
//调用者不是函数
throw this + ‘.myCall is not a function’
};
// 如果第一个参数为引用类型或者null
if(typeof context === ‘object’ || typeof context === ‘function’) {
// 如果为null 则this为window
context = context||window;
} else {
// 如果为undefined 则this绑定为window
if(typeof context === ‘undefined’) {
context = window;
} else {
// 基本类型包装 1 => Number{1}
context = Object(context);
};
};
// this 为当前调用call方法的函数。
context[handler] = this;
// 执行这个函数。这时这个函数内部this绑定为cxt,储存函数执行后的返回值。
let result = context[handler](…args);
// 删除对象上的函数
delete context[handler];
// 返回返回值
return result;
};
2. bind的实现
bind与call区别还是很大的,首先让我们通过以下代码来观察一下bind方法的使用:
var obj = {
name: ‘erdong’
};
function foo(name,age) {
this.age = age;
console.log(this.name + ‘:’+ age + ‘岁’);
};
var bar = foo.bind(obj,’chen’);
bar(18); // erdong:18岁
var b = new bar(27); // undefined:27岁
console.log(b.age); // 27
思路:
- 调用bind方法会创建一个新函数,我们称呼它为绑定函数(boundF)。
- 当我们直接调用boundF函数时,内部this被绑定为bind方法的第一个参数。
- 当我们把这个boundF函数当做构造函数通过new关键词调用时,函数内部的this绑定为新创建的对象。(相当于bind提供的this值被忽略)。
- 调用bind方法时,除第一个参数外的其余参数,将作为boundF的预置参数,在调用boundF函数时默认填充进boundF函数实参列表中。
特点:
- 当第一个参数(要更改的this绑定的对象)为null或者undefined时,this绑定为window(非严格模式)。
- 当call方法中第一个参数为除null和undefined外的基本类型(String,Number,Boolean)时,先对该基本类型进行”装箱”操作。
- 我们根据上述的bind方法的特点,一步一步实现bind方法。
// 第一步 返回一个函数
/
* @description: 实现bind方法
* @param : context this要绑定的值
* @param : args 调用bind方法时,除第一个参数外的参数集合,这些参数会被预置在绑定函数的参数列表中
* @return: 返回一个函数
*/
Function.prototype.myBind = function(context,…args) {
// 这里的this为调用bind方法的函数。
let thisFunc = this;
let boundF = function() {
};
return boundF;
};
第一步:实现了myBind方法返回一个函数。没错就是这就是利用了闭包。
// 第二步
/
* @description: 实现bind方法
* @param : context this要绑定的值
* @param : args 调用bind方法时,除第一个参数外的参数集合,这些参数会被预置在绑定函数的参数列表中
* @return: 返回一个函数
*/
Function.prototype.myBind = function(context, …args) {
// 这里的this为调用bind方法的函数。
let thisFunc = this;
let boundF = function() {
thisFunc.call(context, …args);
};
return boundF;
};
第二步:当调用boundF方法时,原函数内部this绑定为bind方法的第一个参数,这里我们利用了call来实现。
// 第三步
/
* @description: 实现bind方法
* @param : context this要绑定的值
* @param : args 调用bind方法时,除第一个参数外的参数集合,这些参数会被预置在绑定函数的参数列表中
* @return: 返回一个函数
*/
Function.prototype.myBind = function(context,…args) {
// 这里的this为调用bind方法的函数。
let thisFunc = this;
let boundF = function() {
let isUseNew = this instanceof boundF;
thisFunc.call(isUseNew? this:context,…args);
};
return boundF;
};
第三步:先判断boundF是否通过new调用,也就是判断boundF内部的this是否为boundF的一个实例。如果是通过new调用,boundF函数的内部this绑定为当前新创建的对象,因此调用call方法时把当前新创建的对象当作第一个参数传递。
// 第四步
/
* @description: 实现bind方法
* @param : context this要绑定的值
* @param : args 调用bind方法时,除第一个参数外的参数集合,这些参数会被预置在绑定函数的参数列表中
* @return: 返回一个函数
*/
Function.prototype.myBind = function(context, …args) {
// 这里的this为调用bind方法的函数。
let thisFunc = this;
let boundF = function() {
let boundFAgrs = arguments;
let totalAgrs = […args, …arguments];
let isUseNew = this instanceof boundF;
thisFunc.call(isUseNew? this.context, …totalAgrs);
};
return boundF;
};
第四步:通过闭包的特性我们知道,boundF函数可以访问到外部的args变量,将它与boundF函数中的参数合并。然后当作调用原函数的参数。
完整版
到此我们完整版的bind已经显示完毕,下面测试:
Function.prototype.myBind = function(context,…args) {
// 这里的this为调用bind方法的函数。
let thisFunc = this;
let boundF = function() {
let boundFAgrs = arguments;
let totalAgrs = […args,…arguments];
let isUseNew = this instanceof boundF;
thisFunc.call(isUseNew? this.context,…totalAgrs);
};
return boundF;
};
var obj = {
name: ‘erdong’
};
function foo(name,age) {
this.age = age;
console.log(this.name+’:’+age+’岁’);
};
var bar = foo.myBind(obj,’chen’);
bar(18); // erdong:18岁
var b = new bar(27); // undefined:27岁
console.log(b);
console.log(b.age); // 27
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/115911.html