一、apply
apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
func.apply(thisArg, [argsArray])
thisArg
必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象(需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常)。
- 用 apply 修改方法的 this 指向
const firstName = "Ming";
const lastName = "Xiao";
const person = {
firstName: "Mei",
lastName: "Li",
fullName: function(age, score, hobby) {
return this.firstName + " " + this.lastName + "今年" + age + "岁,考了" + score + "分, 爱好是" + hobby + "。";
}
}
const personInstance = {
firstName: "Bill",
lastName: "Gates",
}
console.log(person.fullName.apply(null, [12, 60, '足球'])); // 将返回 "Ming Xiao今年12岁,考了60分, 爱好是足球。"
console.log(person.fullName.apply(person, [13, 88, '跳舞'])); // 将返回 "Mei Li今年13岁,考了88分, 爱好是跳舞。"
console.log(person.fullName.apply(personInstance, [50, 90, '计算机'])); // 将返回 "Bill Gates今年50岁,考了90分, 爱好是计算机。"
如果按照上面代码去测试,后面两个返回是对的,但是第一个person.fullName.apply(null)
并非预期的Xiao Ming, 而是undefined undefined。将最外层定义的 firstName 和 lastName 使用 var 定义就会达到预期,这块涉及到 ES6 const 和 let 的作用域和 JS this 指向的相关内容,有兴趣的大家去玩一玩、试一试。
MDN上还提供了三种示例:
- 用 apply 将数组各项添加到另一个数组
- 使用apply和内置函数
- 使用apply来链接构造器
大家可以到MDN 官网上去查看。
二、call
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
function.call(thisArg, arg1, arg2, ...)
thisArg
可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
arg1, arg2, …
指定的参数列表。
该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
- 用 apply 修改方法的 this 指向
const firstName = "Ming";
const lastName = "Xiao";
const person = {
firstName: "Mei",
lastName: "Li",
fullName: function(age, score, hobby) {
return this.firstName + " " + this.lastName + "今年" + age + "岁,考了" + score + "分, 爱好是" + hobby + "。";
}
}
const personInstance = {
firstName: "Bill",
lastName: "Gates",
}
console.log(person.fullName.call(null, 12, 60, '足球')); // 将返回 "Ming Xiao今年12岁,考了60分, 爱好是足球。"
console.log(person.fullName.call(person, 13, 88, '跳舞')); // 将返回 "Mei Li今年13岁,考了88分, 爱好是跳舞。"
console.log(person.fullName.call(personInstance, 50, 90, '计算机')); // 将返回 "Bill Gates今年50岁,考了90分, 爱好是计算机。"
MDN 关于 call() 的示例感觉还是实际场景见得更多一些:
- 使用 call 方法调用父构造函数
function Product(name, price){
this.name = name;
this.price = price;
}
function Food(name, price){
Product.call(this, name, price);
Food.prototype = Product.prototype;
this.category = 'food';
}
Food.prototype = new Product();
Food.prototype.constructor = Food;
Food.prototype.getInfo = function(){
console.log(this.name +" "+ this.price + " " + this.category);
}
function Toy(name, price){
Product.call(this, name, price);
Toy.prototype = Product.prototype;
this.category = 'toy';
}
Toy.prototype = new Product();
Toy.prototype.constructor = Toy;
Toy.prototype.getInfo = function(){
console.log(this.name +" "+ this.price + " " + this.category);
}
let cheese = new Food('feta', 5);
let fun = new Toy('robot', 40);
cheese.getInfo() // feta 5 food
fun.getInfo() // robot 40 toy
我只是想使用一下call,关于JS的继承,原型链什么的,请自行去学习
- 使用 call 方法调用匿名函数
const animals = [
{ species: 'Lion', name: 'King' },
{ species: 'Whale', name: 'Fail' }
];
for (let i = 0; i < animals.length; i++) {
(function(i) {
this.print = function() {
console.log('#' + i + ' ' + this.species
+ ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
三、bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
function.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArg是null或undefined,执行作用域的 this 将被视为新函数的 thisArg。
arg1, arg2, … 当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
- 创建绑定函数
bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题:
var name = "Xiao Ming"
const person = {
name: 'Li Lei',
getName: function() { return this.name; }
};
console.log(person.getName()); // Li Lei
const unboundGetName = person.getName;
console.log(unboundGetName());
// 返回 Xiao Ming - 因为函数是在全局作用域中调用的
// 创建一个新函数,把 'this' 绑定到 person 对象
const boundGetName = unboundGetName.bind(person);
console.log(boundGetName()); // Li Lei
-
偏函数
bind() 的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。 -
配合 setTimeout
在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或 global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。
总结
- 三者都可以改变函数的this对象指向。
- 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window或global。
- 三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。
- bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行。