使用
call()、apply()、bind()都是用来改变this的指向的。
一、A.a.call(B, x, y)
A是一个对象,a是对象A里的方法函数,x和y是a函数的参数,B也是一个对象,但对象B想调用对象A的a方法,那么call()的作用就是改变函数a的this指向,让函数a的this指向B,对象B就能调用a方法。
let A = {
num: 1,
a: function(x, y){
console.log('我是A对象的a方法,参数:', x, y, 'num:', this.num)
}
}
let x = 2
let y = 3
let B = {
num: 4,
}
console.log(A.a(x, y)) // 我是A对象的a方法,参数: 2 3 num: 1
console.log(A.a.call(B, x, y)) // 我是A对象的a方法,参数: 2 3 num: 4
二、A.a.apply(B, [x, y])
可以看到call和apply的用法和作用基本一致,只是接受参数的方式不太一样,即call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。
let A = {
num: 1,
a: function(x, y){
console.log('我是A对象的a方法,参数:', x, y, 'num:', this.num)
}
}
let x = 2
let y = 3
let B = {
num: 4,
}
console.log(A.a(x, y)) // 我是A对象的a方法,参数: 2 3 num: 1
console.log(A.a.apply(B, [x, y])) // 我是A对象的a方法,参数: 2 3 num: 4
三、A.a.bind(B, x, y)()
bind()除了返回是函数以外,它的参数和call()一样。bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj
let A = {
num: 1,
a: function(x, y){
console.log('我是A对象的a方法,参数:', x, y, 'num:', this.num)
}
}
let x = 2
let y = 3
let B = {
num: 4,
}
console.log(A.a(x, y)) // 我是A对象的a方法,参数: 2 3 num: 1
console.log(A.a.bind(B, x, y)()) // 我是A对象的a方法,参数: 2 3 num: 4
// 关于bind()的一些题目
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即是f函数的y参数;
// 最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1+2+3=6
// 分步处理参数的过程其实是一个典型的函数柯里化的过程(Curry)
var a = document.write;
a('hello'); // error
a.bind(document)('hello'); // hello
a.call(document,'hello'); // hello
// 分析:直接调用a的话,this指向的是global或window对象,所以会报错;
// 通过bind或者call方式绑定this至document对象即可正常调用
常见用法
// 数组之间追加
var arr1 = [1 , 2 , 3];
var arr2 = [4 ,5];
console.log(Array.prototype.push.apply(arr1, arr2))
// 获取数组中的最大值和最小值
let list = [1, 2, 3, -4]
console.log(Math.max.apply(Math, list)) // 3
console.log(Math.min.apply(Math, list)) // -4
// 判断数据类型
var whichDataType = {
isObj:function(o){
if (Object.prototype.toString.call(o)==="[object Object]")
return true;
else
return false
},
isArray:function(o){
if(Object.prototype.toString.call(o)==="[object Array]")
return true;
else
return false
},
isNULL:function(o){
if (Object.prototype.toString.call(o)==="[object Null]")
{return true;}
else
return false
},
isNumber:function(o){
if (Object.prototype.toString.call(o)==="[object Number]")
{return true;}
else
return false
},
isFunction:function(o){
if (Object.prototype.toString.call(o)==="[object Function]")
{return true;}
else
return false
},
isUndefine:function(o){
if (Object.prototype.toString.call(o)==="[object Undefine]")
{return true;}
else
return false
},
isString:function(o){
if (Object.prototype.toString.call(o)==="[object String]")
{return true;}
else
return false
},
isBoolen:function(o){
if (Object.prototype.toString.call(o)==="[object Boolen]")
{return true;}
else
return false
},
isSymbol:function(o){
if (Object.prototype.toString.call(o)==="[object Symbol]")
{return true;}
else
return false
},
}
console.log(whichDataType.isObj({a:1}))
// 伪数组转真数组
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"))
console.log(domNodes)
// 绑定函数
let A = {
num: 1,
a: function(){
console.log(this.num)
}
}
this.num = 2
let a = A.a
A.a() // 1
a() // 2
a.bind(A) // 1
// 闭包经典题目
for(var i = 1; i <= 5; i++) {
setTimeout(console.log.bind(console, i), i * 1000);
}
// 顺便贴一下其他方法
for(var i = 1; i <= 5; i++) {
!function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}(i);
}
for(let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
// 没有对比没有伤害啊
实现
一、call()
// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.mycall = function (context) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
context = context || window
context.fn = this
let arg = [...arguments].slice(1)
let result = context.fn(...arg)
delete context.fn
return result
}
二、apply()
// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.myapply = function (context) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
context = context || window
context.fn = this
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
三、bind()
// 思路:类似call,但返回的是函数
Function.prototype.mybind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this
let arg = [...arguments].slice(1)
return function F() {
// 处理函数使用new的情况
if (this instanceof F) {
return new _this(...arg, ...arguments)
} else {
return _this.apply(context, arg.concat(...arguments))
}
}
}