Implementation fn.apply() fn.call() And fn.bind()
create:August 09, 2019 update:April 12, 2022 • ☕️ 3 min read
同步链接: https://www.shanejix.com/posts/Implementation fn.apply() fn.call() And fn.bind()/
why
demo:
const name = "foo";
const obj = {
name: "bar",
say: function () {
console.log(this.name);
},
};
obj.say(); // output: bar // this => obj
const sayName = obj.say;
sayName(); // output: foo // this => window
函数作为 JavaScript 的一等公民,可以作为值任意传递;在不同执行上下文中,this 的值是被动态计算出来的。有时为了绑定函数的执行环境,fn.apply()
, fn.call()
,fn.bind()
就起到至关重要的作用
how?如何改变函数的 this 呢?看个例子:
var sData = "display() Wisen"; //not let
function display() {
console.log("sData value is %s ", this.sData);
}
display(); //sData value is display() Wisen // this => Window
object wrap:
let displayWrap = {
sData: "displayWrap() Wisen",
display() {
console.log("sData value is %s ", this.sData);
},
};
displayWrap.display(); //sData value is displayWrap() Wisen //this => displayWrap
没错正是利用:obj.fn()的 this 指向 obj 的特点
fn.apply()
function fn(...args) {
console.log(this, args);
}
let obj = {};
fn(1, 2); // this => window
fn.apply(obj, [1, 2]); // this => obj
fn.apply(null, [1, 2]); // this => window
fn.apply(undefined, [1, 2]); // this => window
可以看出:
- apply 接受两个参数,第一个参数是 this 的指向,第二个参数是数组
- 当第一个参数为 null、undefined 的时候,默认指向 window(在浏览器中)
- 原函数会立即执行
fn.call()
function fn(...args) {
console.log(this, args);
}
let obj = {};
fn(1, 2); // this => window
fn.call(obj, [1, 2]); // this => obj
fn.call(null, [1, 2]); // this => window
fn.call(undefined, [1, 2]); // this => window
可以看出:
- call 接受两个参数,第一个参数是 this 的指向,第二个参数是参数列表
- 当第一个参数为 null、undefined 的时候,默认指向 window(在浏览器中)
- 原函数会立即执行
和 apaly 唯一的区别就是 第二个参数不同
fn.bind()
function fn(...args){
console.log(this,args);
}
let obj = {};
const bindFn = fn.bind(obj); // 需要执行依次 fn.bind()
fn(1,2) // this => window
bindFn.([1,2]); // this => obj
fn.bind()([1,2]); // this => window
fn.bind(null)([1,2]); // this => window
可以看出:
- bind 接受两个参数,第一个参数是 this 的指向,第二个参数是参数列表
- 当第一个参数为 null、undefined 的时候,默认指向 window(在浏览器中)
- 返回一个改变 this 指向的函数
implement
fn.apply
Function.prototype.apply = function (thisArg, argsArray) {
if (thisArg === undefined || thisArg === null) {
thisArg = window;
} else {
thisArg = Object(thisArg);
}
const func = Symbol("func");
thisArg[func] = this;
let result;
if (argsArray && typeof argsArray === "object" && "length" in argsArray) {
result = thisArg[func](...Array.from(argsArray));
} else {
result = thisArg[func]();
}
delete thisArg[func];
return result;
};
fn.call
Function.prototype.call = function (thisArg, ...argsArray) {
if (thisArg === undefined || thisArg === null) {
thisArg = window;
} else {
thisArg = Object(thisArg);
}
const func = Symbol("func");
thisArg[func] = this;
let result;
if (argsArray.length) {
result = thisArg[func](...Array.from(argsArray));
} else {
result = thisArg[func]();
}
delete thisArg[func];
return result;
};
fn.bind()
Function.prototype.call = function (thisArg, ...argsArray) {
if (thisArg === undefined || thisArg === null) {
thisArg = window;
} else {
thisArg = Object(thisArg);
}
const func = this;
const bound = function (...boundArgsArray) {
return func.apply(
this instanceof func ? this : thisArg,
argsArray.concat(boundArgsArray)
);
};
return bound;
};
references
作者:shanejix 出处:https://www.shanejix.com/posts/Implementation fn.apply() fn.call() And fn.bind()/ 版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。 声明:转载请注明出处!