bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
参数
thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。
- 如果使用
new运算符构造绑定函数,则忽略该值。 - 当使用
bind在setTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。 - 如果
bind函数的参数列表为空,或者thisArg是null或undefined,执行作用域的this将被视为新函数的thisArg。
arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
返回值
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。
描述
bind() 函数会创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。 绑定函数具有以下内部属性:
- [[BoundTargetFunction]] - 包装的函数对象(即新生成的函数)。
- [[BoundThis]] - 在调用包装函数时始终作为 this 值传递的值。
- [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
- [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。
当调用绑定函数**[[BoundTargetFunction]]**时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(*boundThis*, *args*)。其中,boundThis 是 [[BoundThis]],args 是 [[BoundArguments]] 加上通过函数调用传入的参数列表。
绑定函数也可以使用 new 运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的 this 值会被忽略,但前置参数仍会提供给模拟函数。
用法
改变this指向(创建绑定this指向的函数)
let a = 'outterA'
let obj = {
a: 'innerA'
}
function printA(){
console.log(this.a)
}
printA() //'outterA'
let innerPrintA = printA.bind(obj)
innerPrintA() //innerA
偏函数
通俗的讲,偏函数就是指通过一个初始函数A,创建出另外的函数A1,A2,这两个函数含有了初始参数1,2。这里使用bind是利用了他的两个特点。
bind返回的是绑定函数,区别与Function.prototype.call(),Function.prototype.apply()的立即执行- 其接受的是参数列表,并且会与绑定函数调用时的参数合并作用
[[call]]原函数,区别于Function.prototype.apply()
function add(arg1, arg2){
return arg1 + arg2
}
let addThirtynine = add.bind(null, 39)
let addEleven = add.bind(null, 11)
addThirtynine(3) //42
addEleven(3) //14
改变setTimeout,setInterval的this指向
由于setTimeout,setInterval的函调函数中this的指向总是window(即使在严格模式下)。所以可以使用bind改变回调函数中的this指向。
let obj = {
message: '内部消息'
}
message = '外部消息' //这种方法是吧message变量挂载在window上
setTimeout(function(){
console.log(this.message)
}, 1000)
//一秒后打印出:'外部消息'
setTimeout(function(){
console.log(this.message)
}.bind(obj), 1000) //将this的指向改变为obj
//一秒后打印出:'内部消息'
作为构造函数使用的绑定函数
与偏函数用法类似,相当于为构造器提供默认参数。
function Point(x, y){
this.x = x
this.y = y
}
Point.prototype.toString = function(){
return `${this.x},${this.y}`
}
let p = new Point(1, 2)
p.toString() //'1,2'
let emptyObj = {}
let YAxisPoint = Point.bind(null, 0/*x*/)
let axisPoint5 = new YAxisPoint(5)
axisPoint5.toString() //'0,5'
axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true
快捷调用
一些特定的函数被绑定在特定数据类型种,最典型的例子就是Array.prototype.slice,其只能由数组调用。实际上这个函数也接受类数组的对象(array-like object),这个时候实际上是要改变this的调用。
一般的用法:
let slice = Array.prototype.slice
function convert(){
return slice.apply(arguments) //arguments是一个类数组对象
}
convert(1,2,3,4) //[1, 2, 3, 4]
在使用bind后,由于其可以生成绑定函数,所以将要使用的函数作为this就可以不用每次都使用apply。
let unboundSlice = Array.prototype.slice;
let slice = Function.prototype.apply.bind(unboundSlice); //这个slice就是上面的slice.apply()的一个绑定函数
function convert(){
return slice(arguments) //arguments是一个类数组对象
}
convert(1,2,3,4) //[1, 2, 3, 4]
兼容性
Pollyfill
法一:
// Does not work with `new (funcA.bind(thisArg, args))`
if (!Function.prototype.bind) (function(){
let slice = Array.prototype.slice;
Function.prototype.bind = function() {
let thatFunc = this, thatArg = arguments[0];
let args = slice.call(arguments, 1);
if (typeof thatFunc !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - ' +
'what is trying to be bound is not callable');
}
return function(){
let funcArgs = args.concat(slice.call(arguments)) //合并参数
return thatFunc.apply(thatArg, funcArgs); //绑定this指向
};
};
})();
这里利用了Function.prototype.apply构造一个函数,运行及执行apply方法,达到bind的特点。
法二:
// Yes, it does work with `new (funcA.bind(thisArg, args))`
if (!Function.prototype.bind) (function(){
let ArrayPrototypeSlice = Array.prototype.slice;
Function.prototype.bind = function(otherThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
let baseArgs= ArrayPrototypeSlice.call(arguments, 1),
baseArgsLength = baseArgs.length,
fToBind = this,
fNOP = function() {},
fBound = function() {
baseArgs.length = baseArgsLength; // reset to default base arguments
baseArgs.push.apply(baseArgs, arguments);
return fToBind.apply(
fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
);
};
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
})();
call
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
注意:该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
参数
thisArg
可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
arg1, arg2, ...
指定的参数列表。(与bind一样)
描述
call方法相较于bind,他会生成一个绑定函数并立即调用。
用法
基本方法:改变this指向
let obj = {
message: 'inner-message'
}
message = 'outter-message'
function log(){
console.log(this.message)
}
log() //'outter-message'
log.call(obj) //'inner-message'
使用 call 方法调用父构造函数
使用call方法绑定this对象为自己子类中,则可以完成对父构造器的调用。达到简单的继承效果。
function Product(name, price){
this.name = name
this.price = price
}
function Food(name, price){
Product.call(this, name, price) //调用父构造器,并将this绑定为Food的示例
this.category = 'food'
}
function Toy(name, price){
Product.call(this, name, price) //调用父构造器,并将this绑定为Toy的示例
this.category = 'toy'
}
let cheese = new Food('feta', 5)
let fun = new Toy('robot', 40)
为匿名函数指定this对象
let obj = {
message: 'obj-message'
}
(function(){
console.log(this.message)
}).call(obj)
//'obj-message'
兼容性
Pollyfill
if (!Function.prototype.call) {
Function.prototype.call = function () {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError(
'Function.prototype.call - what is trying to be bound is not callable'
);
}
let func = this
let that = arguments[0]
let args = []
for (let i = 1; i < arguments.length; i++) {
args.push(arguments[i])
}
that.func = func
that.func(...args)
delete that.func
};
}
apply
apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
参数
-
thisArg必选的。在
func函数运行时使用的this值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为null或undefined时会自动替换为指向全局对象,原始值会被包装。 -
argsArray可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给
func函数。如果该参数的值为null或undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
返回值
调用有指定**this**值和参数的函数的结果。
描述
-
apply与call()非常相似,不同之处在于提供参数的方式。apply使用参数数组而不是一组参数列表。apply可以使用数组字面量(array literal),如fun.apply(this, ['eat', 'bananas']),或数组对象, 如fun.apply(this, new Array('eat', 'bananas'))。 -
你也可以使用
arguments对象作为argsArray参数。arguments是一个函数的局部变量。 它可以被用作被调用对象的所有未指定的参数。 这样,你在使用apply函数的时候就不需要知道被调用对象的所有参数。 你可以使用arguments来把所有的参数传递给被调用对象。 被调用对象接下来就负责处理这些参数。 -
从 ECMAScript 第5版开始,可以使用任何种类的类数组对象,就是说只要有一个
length属性和(0..length-1)范围的整数属性。例如现在可以使用NodeList或一个自己定义的类似{'length': 2, '0': 'eat', '1': 'bananas'}形式的对象。需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。
用法
基本方法:改变this指向
let obj = {
message: 'inner-message'
}
message = 'outter-message'
function log(){
console.log(this.message)
}
log() //'outter-message'
log.apply(obj) //'inner-message'
函数数组参数变为列表参数+使用内置函数
let arr = [2,3,4,5,7]
let max = Math.max.apply(null, arr)
//7
在ES6中扩展运算符(spread)···来实现函数数组参数变为列表参数:
let arr = [2,3,4,5,7]
let max = Math.max(...arr)
//7
兼容性
Pollyfill
if (!Function.prototype.apply) {
Function.prototype.apply = function () {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError(
'Function.prototype.apply - what is trying to be bound is not callable'
);
}
let func = this
let that = arguments[0]
let args = []
for (let i = 1; i < arguments.length; i++) {
args.push(arguments[i])
}
that.func = func
that.func(args)
delete that.func
};
}
看看这
更多有趣文章都在公众号-全站学习小师兄