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); // falseconsole.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 SparrowmySayWhoAmI(); // 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中的偏函数)
