一、call、apply的区别 接受的参数不一样,call是一个一个参数接受,apply是接受一个数组
二、call、apply和bind的区别
1、三者都是用来改变函数的this对象的指向
2、三者第一个参数都是this要指向的对象
3、三者都可以利用后续参数传参
4、bind返回对应函数,便于稍后调用;apply、call立即调用
1、Function.prototype.call()方法、Function.prototype.apply()方法
// 数组追加
var arr1 = [12, 'foo', {name: 'joe'}, -2]
var arr2 = ['Doe', 55, 100]
Array.prototype.push.apply(arr1, arr2) // arr1 [12, 'foo', {name: 'joe'}, -2, 'Dow', 55, 100]
// 获取数组中最大最小值
var numbers = [5, 458, 123, -12]
console.log(Math.max.apply(Math, numbers)) // 458
console.log(Math.min.call(Math, ...numbers)) // -12
//验证是否为数组
function functionIsArray (obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
// 定义一个log方法,让它可以代理console.log方法
function log () {
console.log.apply(console, arguments)
}
log(1, 2, 3) // 1 2 3
//给每个log消息添加一个“(app)”的前缀
function log () {
// arguments是个类(伪)数组,通过array.prototype.slice.call转化为标准数组,再使用数组方法unshift
var args = Array.prootype.slice.call(arguments)
args.unshift('(app)')
console.log.apply(console, args)
}
log('hello world') // (app) hello world
2、Function.prototype.bind()方法
bind()方法,主要就是将函数绑定到某个对象上
bind()会创建一个函数,该函数体内的this对象的值,会被绑定到传入bind()的第一个参数
列如,f.bind(obj),实际可以理解为obj.f(),这时,f函数体内的this指向obj
列子1
var a = {
b: function () {
var func = function () {
console.log(this.c)
}
func();
},
c: 'Hello!'
}
a.b(); // undefined ==> 这里的this指向的是全局作用域,所以返回undefined
var a = {
b: function () {
var that = this;
var func = function () {
console.log(that.c)
}
func();
},
c: 'Hello!'
}
a.b(); // Hello! ==> 可以通过赋值的方式将this赋值给that
var a = {
b: function () {
var func = function () {
console.log(this.c)
}.bind(this);
func();
}
c: 'Hello!'
}
a.b(); // Hello! ==> 通过bind方式绑定this
var a = {
b: function () {
var func = function () {
console.log(this.c)
}
func.bind(this)();
},
c: 'Hello!'
}
a.b(); // Hello! ==> 通过bind的方式绑定this
例子2
function f (y, z) {
return this.x + y + z
}
var m = f.bind({x: 1}, 2)
console.log(m(3))
// 6 ==> bind()方法会把它的第一个实参绑定给 f函数体内的this,所以this指向{x: 1}对象
从第二个参数起,会依次传递给原函数,第二个参数2
最后调用m(3),最后一个参数是3
例子3
var a = document.write
a('hello'); // Error ==> 直接调用a,this指向的global或window对象,所以会报错
a.bind(document)('hello') // hello
a.call(document, 'hello') // hello
==> 通过bind或者call方式绑定this至document对象,即可正常调用
例子4:偏函数,使用bind的方式预定义参数
function list () {
// arguments是类数组,使用Array.prototype.slice.call转换为真正的数组,可调用数组方法
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3) // [1, 2, 3]
// a的第一个参数undefined表示this指向,第二个参数10表示list中真正的第一个参数,依次类推var a = list.bind(undefined, 10)
var list2 = a(); // [10]
var list3 = a(1, 2, 3) // [10, 1, 2, 3]
new调用绑定函数时,bind的第一个参数无效
// 使用bind返回的结果是个function,function可被new调用。
// 当new调用绑定函数时,bind的第一个参数无效
function Person (name, age) {
this.name = name;
this.age = age;
}
var _Person = Person.bind({});
var p = new _Person('zs', 30); // Person {name:'zs', age: 30}
bind配合setTimeout,setInterval
// setTimeout、setInterval 很容易把this指向window
// 当使用对象的方法时,需要this引用对象,需要显式地把this绑定到回调函数以便继续使用对象
var canvas = {
render: function () {
this.update();
this.draw();
},
update: function() { // ... },
draw: function() { // ... }
}
window.setInterval(canvas.render, 100/60) // render方法中的this其实被指向了window
window.setInterval(canvas.render.bind(canvs), 1000) // 使用bind,显式地把this绑定到回调函数
// 类似的还有dom的事件监听
bind实现
// bind返回的函数如果作为构造函数,搭配new关键字出现的话,我们绑定的this就需要被忽略
// 处理构造函数场景下的兼容
Function.prototype.bind = Function.prototype.bind || function (context) {
if (typeof this !== 'function') { // 确保调用bind方法的一定要是一个函数
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')
}
var args = Array.prototype.slice.call(arguments, 1);
var self = this;
var F = function() {};
F.prototype = this.prototype;
var bound = function () {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply(this instanceof F ? this : context || this, finalArgs);
}
bound.prototype = new F();
return bound;
}