在JavaScript中,call、apply和bind是Function对象自带的三个方法,这三个方法的主要做用是改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向。javascript
call() 方法调用一个函数, 其具备一个指定的 this 值和多个参数(参数的列表)。java
fun.call(thisArg, arg1, arg2, ...)
thisArg的取值有如下4种状况:react
咱们能够用以下代码验证一下:数组
function func1() { console.log(this); } function func2() {} var obj = { name: "jacky" }; func1.call(); // Window func1.call(null); // Window func1.call(undefined); // Window func1.call(1); // Number {1} func1.call(''); // String {""} func1.call(true); // Boolean {true} func1.call(func2); // ƒ func2() {} func1.call(obj); // {name: "jacky"}
咱们再来看个例子,理解怎么改变this的指向:app
function Animal(){ this.name = 'animal'; this.sayName = function(){ console.log(this.name); } } function Cat(){ this.name = 'cat'; } var animal = new Animal(); var cat = new Cat(); animal.sayName.call(cat); // cat // this 永远指向最后调用它的那个对象 // 该例子中sayName方法的this指向Cat
call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展示,apply则是把除了改变上下文对象的参数放在一个数组里面做为它的第二个参数,他们俩之间的差异在于参数的区别。函数
以下代码:this
function Animal(...args) { console.log(`${this.name} 和其余参数 ${args}`); }; let cat = { name: 'xiaomao' }; // 1. 使用 call Animal.call(cat, 1, 2, 3); // xiaomao 和其余参数 1,2,3 // 2. 使用 apply Animal.apply(cat, [1, 2, 3]); // xiaomao 和其余参数 1,2,3
因为两个方法实际效果是同样的,对于二者平时用该如何选择呢?prototype
let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; // 用 apply方法 [].push.apply(arr1, arr2); // 给arr1添加arr2 console.log(arr1); // [1, 2, 3, 4, 5, 6]
var numbers = [1, 4, 6, 2, 3, 100, 98]; console.log( Math.max.apply(Math, numbers) ); // 100 console.log( Math.max.call(Math, 1, 4, 6, 2, 3, 100, 98) ); // 100 console.log( Math.min.call(Math, ...numbers) ); // 1
let arr = [1, 2, 3, 4]; let str = 'string', obj = { name: 'jacky' } // 判断传入参数是否为数组 function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } console.log(isArray(arr)); // true console.log(isArray(str)); // false console.log(Object.prototype.toString.call(arr)); // [object Array] console.log(Object.prototype.toString.call(str)); // [object String] console.log(Object.prototype.toString.call(obj)); // [object Object] console.log(Object.prototype.toString.call(null)); // [object Null]
function Animal(name){ this.name = name; this.showName = function(){ console.log(this.name); } } function Cat(name){ Animal.call(this, name); } var cat = new Cat("xiaomao"); cat.showName(); // xiaomao
缺点:代理
function func() { var args = [].slice.call(arguments); console.log(args); } func(1, 2, 3); // [1, 2, 3]
还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。code
注意这里传入多少个参数是不肯定的,因此使用apply是最好的。
function log(){ console.log.apply(console, arguments); }; log(1); // 1 log(1, 2, 3); // 1 2 3
MDN的解释是:
bind()方法会建立一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以建立它时传入 bind()方法的第一个参数做为 this,传入 bind() 方法的第二个以及之后的参数加上绑定函数运行时自己的参数按照顺序做为原函数的参数来调用原函数。
说直白一点,bind方法是建立一个函数,而后能够在须要调用的时候再执行函数,并不是是当即执行函数;而call,apply是在改变了上下文中的this指向后并当即执行函数。
bind 接收的参数类型与 call 是同样的,给它传递的是一组用逗号分隔的参数列表。
var bar = function(){ console.log(this.x); } var foo = { x: 2 } bar(); // undefined var func = bar.bind(foo); func(); // 2
class PageA { constructor(callBack) { this.className = 'PageA' this.MessageCallBack = callBack this.MessageCallBack('执行了') } } class PageB { constructor() { this.className = 'PageB' this.pageClass = new PageA(this.handleMessage) } // 回调函数 handleMessage(msg) { console.log('处理' + this.className + '的回调 ' + msg) // 处理PageA的回调 执行了 } } new PageB();
运行上面的代码,咱们发现回调函数this丢失了?问题出在这行代码
this.pageClass = new PageA(this.handleMessage)
传递过去的this.handleMessage是一个函数内存地址,没有上下文对象,也就是说该函数没有绑定它的this指向。
解决方案:用bind绑定this的指向
this.pageClass = new PageA(this.handleMessage.bind(this))
这也是为何react的render函数在绑定回调函数的时候,也要使用bind绑定一下this的指向,也是由于一样的问题以及原理。
var bar = function() { console.log(this.x); } var obj1 = { x: 2 } var obj2 = { x: 3 } var obj3 = { x: 4 } var func1 = bar.bind(obj1).bind(obj2); var func2 = bar.bind(obj1).bind(obj2).bind(obj3); func1(); // 2 func2(); // 2
输出结果都为第一个绑定的obj对象的x值。缘由是,在Javascript中,bind()方法返回的外来的绑定函数对象仅在建立的时候记忆上下文(若是提供了参数),屡次 bind() 是无效的。更深层次的缘由, bind() 的实现,至关于使用函数在内部包了一个 call / apply ,第二次 bind() 至关于再包住第一次 bind() ,故第二次之后的 bind 是没法生效的。
call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展示; apply则是把除了改变上下文对象的参数放在一个数组里面做为它的第二个参数,他们俩之间的差异在于参数的区别。
call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数,其内的this指向为建立它时传入bind的第一个参数,而传入bind的第二个及之后的参数做为原函数的参数来调用原函数。bind的参数能够在函数执行的时候再次添加。