this
this对象是在运行时基于函数的执行环境绑定的(抛开箭头函数)
当函数被作为某个对象的方法调用时,this等于那个对象
this等于最后调用函数的对象
call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。
————————《JavaScript高级程序设计》
call
call()方法可以指定一个this的值(第一个参数),并且分别传入参数(第一个参数后面的就是需要传入函数的参数,需要一个一个传)call()改变this指向
var first = '大黑刀·夜',
second = '二代鬼彻',
third = '初代鬼彻',
fourth = '时雨';
var zoro = {
first: '和道一文字',
second: '三代鬼彻',
third: '雪走',
fourth: '秋水'
};
function sayYourWeapon(num, num2) {
console.log(`这是我${num}得到的刀"${this[num]}"`)
console.log(`这是我${num2}得到的刀"${this[num2]}"`)
}
sayYourWeapon('first', 'third'); // 这是我first得到的刀"大黑刀·夜";这是我third得到的刀"初代鬼彻"
sayYourWeapon.call(zoro, 'first', 'fourth'); // 这是我first得到的刀"和道一文字";这是我fourth得到的刀"秋水"
上面这段代码很明显的改变了this的指向,如果直接调用sayYourWeapon()必然输出的是全局全局变量first和third的值,而我后面通过sayYourWeapon.call(zoro, ‘first’, ‘fourth’)中的call()方法
因为改变了函数中的this值,就是传入的zoro,把this值从全局对象改成了zoro对象
所以后面输出的也都是对象zoro中的’first’, ‘fourth’的值
apply
apply()方法可以指定一个this的值(第一个参数),并且传入参数数组(参数需要在一个数组或者类数组中)
var first = '大黑刀·夜',
second = '二代鬼彻',
third = '初代鬼彻',
fourth = '时雨';
var zoro = {
first: '和道一文字',
second: '三代鬼彻',
third: '雪走',
fourth: '秋水'
};
function sayYourWeapon(num, num2) {
console.log(`这是我${num}得到的刀"${this[num]}"`)
console.log(`这是我${num2}得到的刀"${this[num2]}"`)
}
sayYourWeapon('first', 'third'); // 这是我first得到的刀"大黑刀·夜";这是我third得到的刀"初代鬼彻"
- sayYourWeapon.call(zoro, 'first', 'fourth'); // 这是我first得到的刀"和道一文字";这是我fourth得到的刀"秋水"
+ sayYourWeapon.apply(zoro, ['first', 'fourth']); // 这是我first得到的刀"和道一文字";这是我fourth得到的刀"秋水"
可以看到,我全篇就只是把call
改成了apply
,并且把之前'first', 'fourth'
这么传进去的参数改成了['first', 'fourth']
一个数组。如果我们是在一个函数当中使用,那我们还可以直接使用arguments
这个类数组对象
var first = '大黑刀·夜',
second = '二代鬼彻',
third = '初代鬼彻',
fourth = '时雨';
var zoro = {
first: '和道一文字',
second: '三代鬼彻',
third: '雪走',
fourth: '秋水'
};
function sayYourWeapon(num, num2) {
console.log(`这是我${num}得到的刀"${this[num]}"`)
console.log(`这是我${num2}得到的刀"${this[num2]}"`)
}
function mySayYourWeapon(num, num2) {
sayYourWeapon.apply(zoro, arguments) // 我们自己声明一个函数,并且在里面调用apply,这是我们只需要传入arguments这个参数,而不需要想call那样一个一个传进去了
}
sayYourWeapon('first', 'fourth'); // 这是我first得到的刀"大黑刀·夜";这是我fourth得到的刀"时雨"
mySayYourWeapon('first', 'fourth'); // 这是我first得到的刀"和道一文字";这是我fourth得到的刀"秋水"
bind
bind()方法不会立即执行目标函数,而是返回一个原函数的拷贝,并且拥有指定this值和初始函数
原函数拷贝
function a() {}
console.log(typeof a.bind() === 'function'); // 返回是true,先证明a.bind()是一个函数
console.log(a.bind()); // 输出function a() {},跟原函数一样
console.log(a.bind() == a); // false
console.log(a.bind() === a); // false 不管是 === 还是 == 都是false,证明是拷贝出来一份而不是原先的那个函数
bind()方法在传参上跟call是一样的,第一个参数是需要绑定的对象,后面一次传入函数需要的参数,如下⬇️
var name = 'Jack Sparrow';
var onePiece = {
name: 'Monkey·D·Luffy'
};
function sayWhoAmI() {
console.log(this.name)
}
var mySayWhoAmI = sayWhoAmI.bind(onePiece)
sayWhoAmI(); // Jack Sparrow
mySayWhoAmI(); // Monkey·D·Luffy
一个简单的实现,本来输出的是全局变量’Jack Sparrow’,后来经过bind以后绑定上了对象onePiece,所以输出的就是对象onePiece中的nodeMonkey·D·Luffy。
那需要传参的时候怎么办
var first = '大黑刀·夜',
second = '二代鬼彻',
third = '初代鬼彻',
fourth = '时雨';
var zoro = {
first: '和道一文字',
second: '三代鬼彻',
third: '雪走',
fourth: '秋水'
};
function sayYourWeapon(num, num2) {
console.log(`这是我${num}得到的刀"${this[num]}"`)
console.log(`这是我${num2}得到的刀"${this[num2]}"`)
}
// 既然我们知道bind是返回一个函数,那我们声明一个变量来接这个函数会看的直观一些
var mySayYourWeapon = sayYourWeapon.bind(zoro, 'first', 'fourth'); // 传入初始参数
var hisSayYourWeapon = sayYourWeapon.bind(zoro); // 只传入目标对象
sayYourWeapon('first', 'third');
mySayYourWeapon(); // 因为我们当时bind绑定函数的时候已经传入了目标对象zoro和指定的参数,所以这里就不需要传参数了
hisSayYourWeapon( 'first', 'fourth'); // 当然我们开始bind绑定函数的时候不传入,在调用的时候再传入参数也是可以的
上面的代码我们可以发现mySayYourWeapon和hisSayYourWeapon在bind的时候一个传入了初始的参数,一个没有传入,但是后续调用的时候可以再传
既然是初始化参数,那我们就可以预设参数一个,然后再传一个——————偏函数(不知道自己理解的对不对,但是肯定是有这么个功能,不懂的可以移步MDN web docs的Function.prototype.bind中的偏函数)