为什么写这个文章了呢?面试的时候(完整面试内容点这儿),面试官问bind的作用是啥?bind的第二个参数是做什的?箭头函数能使用bind函数吗 ?
前面其实也有看这方面的文章,但是不够细节,所以重新学习这方面的知识。
bind
bind()
方法创建一个新的函数,在bind()
被调用时,这个新函数的this被指定为bind()
的第一个参数,而其余参数作为新函数的参数,供调用时使用。
bind函数会创建一个新的绑定函数(bound function, BF)。绑定函数时一个exotic function object(怪异函数对象),它包装了原函数对象。调用绑定函数具有以下内部属性
- [[BoundTargetFunction]]-包装函数对象
- [[BoundThis]]-在调用包装函数时始终作为this值传递的值
- [[BoundArguments]]-参数列表,在对包装函数做任何调用都会优先用列表元素填充参数列表
- [[Call]]-执行与该对象关联的代码。通过函数调用表达式调用,内部方法的参数时一个this值和一个包含通过表达式传递给函数的参数列表。
当调用bind函数时,会调用[[BoundTargetFunction]]上的内部方法[[call]],类似Call(BoundThis, args)。其中,boundThis时[[BoundThis]],args是[[BoundArguments]]通过函数调用传入的参数列表。
语法
function.bind(thisArg[, arg1[, arg2[, arg3);
参数
thisArg:
调用绑定函数时作为this
参数传递给目标函数的值。如果使用new
运算符构造绑定函数,则忽略该值,当使用bind
在setTimeout
中创建一个函数(作为回调)时,作为thisArg传递的任何原始值都转换为Object。如果bind函数的参数列表为空,或者thisArg为null或undefined,执行作用域的this将被视为新函数的thisArg。
// 全局作用域 this 执行window
function bindFun() {
console.log(this);
}
const obj1 = {name: 'obj1'}
bindFun.bind(obj1)(); // obj1
bindFun.bind()(); // window
obj1.bindFun1 = bindFun.bind();
obj1.bindFun1(); // window
const obj2 = {name: 'obj2'}
obj1.bindFun2 = bindFun.bind(obj2);
obj1.bindFun2(); // obj2
obj1.bindFun3 = bindFun.bind().bind(obj2);
obj1.bindFun3(); // window
返回值
返回一个函数的,并拥有指定this值和初始参数
示例
创建绑定函数
this.x = 'window';
let obj = {
x: 'obj',
getX: function() { console.log(this.x); }
}
obj.getX(); // obj
let getX = obj.getX;
getX(); // window
let boundGetx = getX.bind(obj);
boundGetx(); // obj
传入参数的绑定
bind另外一种用法,就是在创建绑定函数的时候,传入部分的参数。
function getList() {
return Array.prototype.slice(argumentd);
}
function addArgs(arg1, arg2) {
return arg1 + arg2;
}
const list1 = getList(1, 2, 3); // [1, 2, 3]
const res1 = addArgs(1, 2); // 3
// 创建一个带有参数的 绑定函数
const getListBy37 = getList.bind(null, 37);
const list2 = getListBy37(); // [37]
const list3 = getListBy37(1, 2); // [37, 1, 2]
const addBy37 = addArgs.bind(null, 37);
const res2 = addBy37(5); // 37 + 5 = 42
const res3 = addBy37(5, 10); // 37 + 4 = 45 因为addArgs只能接受2个参数,所以这儿的10会被忽略
bind与setTimeout
通常作为setTimeout
入参数的回调函数,它的 this将指向window或者全局对象。但是很多时候我们有需要这个回调函数中的this指向当前的示例,此时我们就需要利用bind。
function LateBloomer() {
this.petalCount = Math.floor(Math.random() * 12) + 1;
}
LateBloomer.prototype.bloom = function() {
setTimeout(this.declare.bind(this), 1000);
}
LateBloomer.prototype.declare = function() {
console.log(this.petalCount);
}
const flower = new LateBloomer();
flower.bloom();
bind与构造函数
当bind函数被用在构造函数时,bind将被忽略
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return `${this.x},${this.y}`;
}
const p1 = new Point(1, 2);
p.toString(); // 1,2
const YAxisPoint = Point.bind(null, 0);
const p2 = new YAxisPoint(2, 3); // null没生效
p2.toString(); // 0,2
const YAxisPoint3 = Point.bind({}, 0);
const p3 = new YAxisPoint3(2, 3); // bind传入的thisArg不会生效
p3.toString(); // 0,2
p3 instanceof Point // true
p3 instanceof YAxisPoint3 // true
创建快捷方式
const unboundSlice = Array.prototype.slice;
const slice = Function.prototype.apply.bind(unboundSlice);
// ……
slice(arguments);
bind与箭头函数
我们都知道箭头函数this指向与外层作用域的this,那如果我们对箭头函数使用bind会是什么效果呢?
bind传入的thisArg不会生效,但是传入的参数是会生效的,如下:
var name = 'window';
const fun = (x, y) => {
console.log(this.name, x);
}
const obj = {name: 'obj'};
const boundFun = fun.bind(obj, 'x');
boundFun('y'); // window x y
boundFun.bind({name: 'bind + bind'}, 'y2')('y3'); // window x y2
手写bind
Function.prototype.myBind = function(thisArg, ...arg1) {
const _this = this;
return function _a (...arg2) {
const params = [...arg1, ...arg2];
if(this instanceof _a) {
return new _this(...params);
}
else {
return _this.apply(thisArg, params);
}
}
}
call
call函数在函数执行时绑定this的值和参数
语法
fun.call();
fun.call(thisArg);
fun.call(thisArg, arg1);
fun.call(thisArg, arg1, arg2);
fun.call(thisArg, arg1, ..., argn);
参数
thisArg:
func
被调用时this的值。
在非严格模式下,thisArg的值为null 或 undefined的时候,thisArg将被替代换全局对象。
arg1, arg2, ……, argn:函数的入参
返回值
调用call的函数使用thisArg和参数后的执行结果
示例
var name = 'window'
function great() {
console.log(`hello ${this.name}`);
}
great.call(); // hello window
great.call(undefined); // hello window
great.call(null); // hello window
great.call({name: 'bajiu'}); // hello bajiu
手写call
Function.prototype.call = function(thisArg, ...arg) {
const context = (thisArg === null || thisArg === undefiend) ? window : Object(thisArg);
context.fn = this;
const res = context.fn(...arg);
delete context.fn;
return res;
}
apply
方法整体用法与call一样,只存在入参形式的不同
手写apply
Function.prototype.myApply = function(thsArg, arg) {
const context = (thisArg === null || thisArg === undefiend) ? window : Object(thisArg);
context.fn = this;
const res = context.fn(...arg);
delete context.fn;
return res;
}
``