bind()
语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new操作符调用绑定函数时,该参数无效。
arg1, arg2, ...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
return:返回由指定的this值和初始化参数改造的原函数拷贝
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体。
当新函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。")操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。————MDN
创建绑定函数
bind() 最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的 this 值。
this.x = 9;
var obj = {
x: 81,
GetObjx: function() {
console.log(this.x);
}
};
obj.GetObjx(); // 81
var againGetObjx = obj.GetObjx;
againGetObjx(); // 9 无法获取到obj.x,因为此时的this指向Window
// 创建一个新函数,将this绑定到obj对象
var fn = againGetObjx.bind(obj);
fn(); // 81
上例中,againGetObjx引用了obj对象中的GetObjx方法,但是很显然,this.x没有指向obj,是因为,此时调用againGetObjx的对象是全局对象,理所当然的this引用指向window,打印出window.x,即9。
使用bind()方法,在方法中我们传入了obj对象,强制指定了函数执行上下文,将this绑定到了obj对象。
偏函数
bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1,2,3);
console.log(list1); // [1,2,3];
var leadingThirtysevenList = list.bind(undefined,37);
var list2 = leadingThirtysevenList(); // [37]
console.log(list2);
var list3 = leadingThirtysevenList(1,2,3);
console.log(list3); // [37,1,2,3]
函数中的arguments保存着传入实参,但本身是类数组,没有数组的方法,所以使用数组"类"中的slice方法来代替。
var leadingThirtysevenList = list.bind(undefined,37);实测,把undefined换成null也可以,表示不为this指定对象,函数体执行不变,那么在bind()方法第二个参数传入的值会作为fn.bind()中fn的参数一起传入,在这就是list()函数了。
改写:
function Fn(a,b,c) {
console.log(a);
console.log(b);
console.log(c);
return a+b+c;
}
var fn = Fn.bind(undefined,10);
console.log(
fn(20,30)
);
/*
10
20
30
60
*/
当然,如果在bind()方法中传入足够的参数,那么后续的参数就不会生效,但是它依然存在在arguments类数组中。
function Fn(a,b,c) {
console.log(a);
console.log(b);
console.log(c);
console.log(arguments);
return a+b+c;
}
var fn = Fn.bind(undefined,10,1,2);
console.log(
fn(20,30) // 参数无效,但仍有arguments类数组保存
);
/*
10
1
2
13
[10,1,2,20,30]
*/
配合 setTimeout
在默认情况下,使用window.setTimeout()时,this关键字会指向全局或者对象。当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例。
function fn() {
console.log(this.petalCount);
}
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this),1000);
}
LateBloomer.prototype.declare = function() {
console.log("I am beautiful flower with " + this.petalCount + "petals!");
};
var flower = new LateBloomer();
flower.bloom(); // 一秒后,调用declare方法
apply()
apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组提供的参数。
调用有指定this值和参数的函数的结果。
func.apply(thisArg, [argsArray])
简单理解用法:
var obj = {
a: 1,
sum: function(b,c) {
console.log(this.a + b + c);
}
}
var obj2 = {
a: 2
}
obj.sum.apply(obj2,[5,5]); // 12
与构造函数:
function Ball(shape) {
this.shape = shape;
this.Pi = Math.PI;
}
function FootBall(shape,color) {
Ball.apply(this , arguments);
this.color = color,
this.say = function() {
console.log("shape:" + this.shape + " this.color:" + this.color);
}
}
var football = new FootBall("sphere","black");
football.say(); // shape:sphere this.color:black
函数只不过是在特定环境中执行代码的对象,因此通过apply和call方法也可以在新创建的对象上执行构造函数
使用apply来链接构造器:
Function.prototype.construct = function (aArgs) {
var oNew = Object.create(this.prototype);
console.log(this.prototype === MyConstructor.prototype); // true
this.apply(oNew, aArgs); // this为构造函数,以oNew(构造函数的prototype)为上下文环境,传入aArgs数组
console.log(this);
return oNew;
};
function MyConstructor() {
for (var nProp = 0;nProp < arguments.length; nProp++) {
this["property" + nProp] = arguments[nProp];
}
}
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
console.log(myInstance.property1); // Hello world!
console.log(myInstance instanceof MyConstructor); // true
console.log(myInstance.constructor === MyConstructor); // true
console.log(myInstance.__proto__.constructor === MyConstructor); // true
construct()方法改写(可能这样更易读):
Function.prototype.construct = function (aArgs) {
var oNew = {};
oNew.__proto__ = this.prototype;
this.apply(oNew, aArgs);
return oNew;
};
apply借调内置函数
console.log(Math.max(1,2,3)) // 3
// 借用Math.max()
var numbers = [5,6,2,3,7];
var max = Math.max.apply(null,numbers);
console.log(max); // 7
// 借用Math.min()
var min = Math.min.apply(undefined,numbers);
console.log(min); // 2
call()
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined。
fun.call(thisArg, arg1, arg2, ...)
语法层面,与apply不同就是,call()方法传递的是一个参数列表,apply传递的是参数数组。
常用用法:使用call方法调用函数并且指定上下文的this
function greet() {
var reply = [this.person, 'Is An Awesome', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'Douglas Crockford', role: 'Javascript Developer'
};
greet.call(i);
调用父构造函数的call方法来实现继承
function Product(name,price) {
this.name = name;
this.price = price;
}
function Food(a,b,category) {
Product.call(this,a,b);
this.category = category;
}
var food = new Food("sour apple",5,"sour");
console.log(food); // Food {name: "sour apple", price: 5, category: "sour"}
使用call方法调用匿名函数
var animals = [
{ species: "Lion", name: "King" },
{ species: "Whale", name: "Fail" }
]
for (var i = 0; i < animals.length; i++) {
(function (i) {
console.log("#" + i + " " + this.species + ": " + this.name);
}).call(animals[i], i)
}
// #0 Lion: King
// #1 Whale: Fail