First
在JavaScript中,有四种this绑定规则分别是:
- 默认绑定(window)
- 隐式绑定(调用对象)
- 显示绑定(call、apply、bind)
- new 绑定
这篇文章主要实现一下显示绑定规则的三种方式;
一、call(thisArg, arg1, arg2, ...)方法
call()方法使用一个指定的
this值和单独给出的一个或多个参数来调用一个函数;
//用在这里的...表示剩余参数运算符 = [arg1, arg2...]
Function.prototype.mycall = function(thisArg, ...args) {
if (typeof this !== "function") {
throw new Error("Type error");
};
// 1.获取需要被执行的函数,也就是我们的调用者foo、baz...也就是this;
var fns = this;
// 2.对绑定的this做边界判断;
// 为什么使用Object()呢?对转入字符串和数字的处理,转成对象String{}、Number{};
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
// 3.使用绑定的this调用需要被执行的函数。第二步调用第一步;
thisArg.fns = fns;
//用在这里的...表示展开运算符 = (arg1, arg2...)
var result = thisArg.fns(...args);
// 4.如果函数有返回值,拿到返回结果并返回出去;
return result;
};
function foo() {
// 为什么多了个fns:f呢?因为我们往thisArg添加了属性,调用完之后并没有删除;
// 可以在调用完thisArg.fns(); 之后执行 delete thisArg.fns;
// 打印这些内容是在fns调用时就打印了,所以删除之后,这里的打印依旧会有fns:f属性;实际上已经删除了;
console.log("foo函数", this); //5. 输出 `foo函数 {name: 'foo', fns: ƒ}`
};
function baz(n1, n2) {
console.log("baz函数", this, n1, n2); // 6.输出 `baz函数 {name: 'baz', fns: ƒ} 10 20`
return n1 + n2;
};
// 调用我们在函数的显示原型上面添加的方法`mycall()`;
foo.mycall({name: 'foo'});
var res = baz.mycall({name: 'baz'}, 10, 20);
console.log(res); //30 有返回值,就接收使用。
二、apply(thisArg, [argsArray])方法
该方法的语法和作用与
call()方法类似,只有一个区别,就是call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组;
Function.prototype.myapply = function(thisArg, argsArray) {
if (typeof this !== "function") {
throw new Error("Type error");
};
var fns = this;
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
argsArray = argsArray || []; //未传入参数就是空数组;
thisArg.fns = fns;
var result = thisArg.fns(...argsArray);
return result;
};
function foo() {
console.log('foo函数', this); // 输出 `foo函数 {name: 'foo', fns: ƒ}`
};
function baz(n1, n2) {
console.log('baz函数', this, n1, n2); // 输出 `baz函数 {name: 'baz', fns: ƒ} 10 20`
return n1 + n2;
};
foo.myapply({name: 'foo'});
var res = baz.myapply({name: 'baz'}, 10, 20);
console.log(res); //30
三、bind(thisArg, arg1, arg2...)方法
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用;
Function.prototype.mybind = function(thisArg, ...args) {
if (typeof this !== "function") {
throw new Error("Type error");
};
var fns = this;
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
thisArg.fns = fns;
return function(...args2) {
// 参数合并;
var finalArgs = [...args, ...args2];
var result = thisArg.fns(...finalArgs);
return result;
};
};
function foo() {
console.log('foo函数', this); //输出 `foo函数 {name: 'foo', fns: ƒ}`
};
function baz(n1, n2, n3, n4) {
console.log('baz函数', this, n1, n2, n3, n4); //输出 `baz函数 {name: 'baz', fns: ƒ} 10 20 30 40`
};
var fn1 = foo.mybind({name: 'foo'});
fn1();
var fn2 = baz.mybind({name: 'baz'}, 10, 20);
fn2(30, 40);
Finish
实现这三个方法并没有过多的考虑一些边界情况,不足的地方还希望多多指点,谢谢!
1)默认规则的优先级最低;
2)显示绑定优先级高于隐式绑定;
3)new 绑定优先级高于隐式绑定;
4)new 绑定优先级高于bind;new绑定和call、apply是不允许同时使用的;
不是因为没有信念而失败,而是因为不能把信念化成动力,并且坚持到底。