当前位置:嗨网首页>书籍在线阅读

12-调用、请求和绑定

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

6.8 调用、请求和绑定

上面已经知道 this 绑定的一般方式(跟其他面向对象语言一样)。然而,JavaScript还允许指定 this 绑定的值,不论函数在何处被如何调用。从 call 讲起, call 方法在所有函数上都可用,它允许使用指定的 this 来调用函数。

const bruce = { name: "Bruce" };
const madeline = { name: "Madeline" };
// this function isn't associated with any object, yet
// it's using 'this'!
function greet() {
    return 'Hello, I'm ${this.name}!';
}
greet();              // "Hello, I'm !" - 'this' 没有绑定任何值
greet.call(bruce);    // "Hello, I'm Bruce!" - 'this' 绑定了 'bruce'
greet.call(madeline); // "Hello, I'm Madeline!" -'this' 绑定了 'madeline'

可以看到 call 方法允许在调用函数时给 this 绑定一个对象,就好像有一个方法在做这件事。 call 方法的第一个参数是想要的绑定的值,剩下的参数则变成了要调用的函数的参数:

function update(birthYear, occupation) {
    this.birthYear = birthYear;
    this.occupation = occupation;
}
update.call(bruce, 1949, 'singer');
    // 现在的bruce是 { name: "Bruce", birthYear:1949,occupation:"singer"}
update.call(madeline, 1942, 'actress');
    // 现在的madeline是 { name: "Madeline", birthYear: 1942,
    // occupation: "actress" }

除了处理函数参数的方式不同, applycall 基本是一致的。 call 会像一般的函数一样直接获取参数。 apply 则以数组的方式获取参数:

update.apply(bruce, [1955, "actor"]);
    // 现在的bruce是 { name: "Bruce", birthYear: 1955,occupation:"actor"}
update.apply(madeline, [1918, "writer"]);
    // 现在的madeline是 { name: "Madeline", birthYear: 1918,
    //    occupation: "writer" }

apply 比较适合用于将数组作为函数参数的场景。一个经典的例子就是找出数组内的最大值和最小值。JavaScript内建的 Math.maxMath.min 函数可以接收任意一串数字作为参数,并分别返回最大值和最小值。可以用 apply 和一个已有的数组调用这些函数:

const arr = [2, 3, -5, 15, 7];
Math.min.apply(null, arr);    // -5
Math.max.apply(null, arr);    // 15

注意,这里给 this 传了一个 null 。这是因为 Math.maxMath.min 并不使用 this ,所用不管传给它什么值都没关系。

通过ES6中的展开运算符(...),可以实现与 apply 同样的功能。在 update 函数的实例中,由于大家关心 this 的值,所以还是要使用 call 方法,不过在 Math.minMath.max 中,大家并不关心 this 的值,所以可以直接用展开操作符来调用函数:

const newBruce = [1940, "martial artist"];
update.call(bruce, ...newBruce);          // 跟apply(bruce, newBruce)等价
Math.min(...arr);                         // -5
Math.max(...arr);                         // 15

最后一个允许指定 this 值的函数是 bindbind 方法可以给一个函数永久地邦定 this 值。想象一下在传递 update 方法时,需要保证该方法在任何时候被调用时 this 的值始终是 bruce (即使用了 call,apply ,或者其他绑定方式)。 bind 可以做到这一点:

const updateBruce = update.bind(bruce);
updateBruce(1904, "actor");
    // 现在的bruce是{ name: "Bruce", birthYear: 1904, occupation:"actor"}
updateBruce.call(madeline, 1274, "king");
    // 现在bruce是 { name: "Bruce", birthYear: 1274, occupation:"king"};
    // madeline跟之前一样

给函数绑定一个永久的 this 值可能会成为一个潜在的并且很难定位的bug:因为使用 bind 之后,函数实际上已经不能再有效的使用 callapply 或者 bind (再次使用)了。想象一下传递一个在其他地方调用了 call 或者 apply 的函数,然后期望它绑定了预期的值的情况。并不是建议不要使用 bind ,毕竟它很有用,但是要留意如何使用被绑定的函数。

调用 bind 方法时也可以传参数,这跟创建一个有固定参数的新函数效果一样。比如,希望 update 函数总是把 bruce 的出生年份设为 1949 ,但却允许修改他的职业,可以这样做:

const updateBruce1949 = update.bind(bruce, 1949);
updateBruce1949("singer, songwriter");
    // bruce现在是 { name: "Bruce", birthYear: 1949,
    //    occupation: "singer, songwriter" }