谈谈JavaScript中的几种借用方法

 更新时间:2016年08月09日 11:18:48   投稿:daisy  
我们下面来看一篇关于JavaScript中借用方法的一个详细介绍,注意:本文假设你已经掌握使用 call()、apply()和bind()的相关知识和它们之间的区别,希望这篇文章能够让各位了解到JavaScript中的借用方法吧。

前言

通过call()、apply()和bind()方法,我们可轻易地借用其它对象的方法,而无须从这些对象中继承它。

在JavaScript中借用方法

在JavaScript中,有时可以重用其它对象的函数或方法,而不一定非得是对象本身或原型上定义的。通过 call()、apply() 和 bind() 方法,我们可轻易地借用其它对象的方法,而无须继承这些对象。这是专业 JavaScript 开发者常用的手段。

原型方法

在 JavaScript 中,除了不可更改的原始数据类型,如 string、number 和 boolean,几乎所有的数据都是对象。Array 是一种适用于遍历和转换有序数列的对象,其原型上有 slice、join、push 和 pop 等好用的方法。

一个常用的例子是,当对象和数组都是列表类型的数据结构时,对象可以从数组“借用”方法。最常借用的方法是 Array.prototype.slice

function myFunc() {
 
  // error, arguments is an array like object, not a real array
  arguments.sort();
 
  // "borrow" the Array method slice from its prototype, which takes an array like object (key:value)
  // and returns a real array
  var args = Array.prototype.slice.call(arguments);
 
  // args is now a real Array, so can use the sort() method from Array
  args.sort();
 
}
 
myFunc('bananas', 'cherries', 'apples');

借用方法之所以可行,是因为 call 和 apply 方法允许在不同上下文中调用函数,这也是重用已有功能而不必继承其它对象的好方法。实际上,数组在原型中定义了很多常用方法,比如 join 和 filter 也是:

// takes a string "abc" and produces "a|b|c
Array.prototype.join.call('abc', '|');
 
// takes a string and removes all non vowels
Array.prototype.filter.call('abcdefghijk', function(val) {
  return ['a', 'e', 'i', 'o', 'u'].indexOf(val) !== -1;
}).join('');

可以看出,不仅对象可以借用数组的方法,字符串也可以。但是因为泛型方法是在原型上定义的,每次想要借用方法时都必须使用 String.prototype Array.prototype。这样写很啰嗦,很快就会令人生厌。更有效的方法是使用字面量来达到同样的目的。

使用字面量借用方法

字面量是一种遵循JavaScript规则的语法结构,MDN 这样解释:

在JavaScript中,使用字面量可以代表值。它们是固定值,不是变量,就是在脚本中按字面给出的。
字面量可以简写原型方法:

[].slice.call(arguments);
[].join.call('abc', '|');
''.toUpperCase.call(['lowercase', 'words', 'in', 'a', 'sentence']).split(',');

这样看上去没有那么冗长了,但是必须直接在 [] 和 "" 上操作以借用方法,仍然有点丑。可以利用变量保存对字面量和方法的引用,这样写起来更简便些:

var slice = [].slice;
slice.call(arguments);
var join = [].join;
join.call('abc', '|');
 
var toUpperCase = ''.toUpperCase;
toUpperCase.call(['lowercase', 'words', 'in', 'a', 'sentence']).split(',');

有了借用方法的引用,我们就可以轻松地使用 call() 调用它了,这样也可以重用代码。秉着减少冗余的原则,我们来看看可否借用方法却不用每次调用都要写 call() 或者 apply():

var slice = Function.prototype.call.bind(Array.prototype.slice);
slice(arguments);
 
var join = Function.prototype.call.bind(Array.prototype.join);
join('abc', '|');
 
var toUpperCase = Function.prototype.call.bind(String.prototype.toUpperCase);
toUpperCase(['lowercase', 'words', 'in', 'a', 'sentence']).split(',');

如你所见,现在可以使用 Function.prototype.call.bind 来静态绑定从不同原型“借来的”方法了。但是 var slice = Function.prototype.call.bind(Array.prototype.slice) 这句话实际是如何起作用的呢?

理解 Function.prototype.call.bind

Function.prototype.call.bind 乍一看有些复杂,但是理解它是如何起作用的会非常有益。

Function.prototype.call 是一种引用,可以“call”函数并将设置其“this”值以在函数中使用。
注意“bind”返回一个存有其“this”值的新函数。因此 .bind(Array.prototype.slice) 返回的新函数的“this”总是 Array.prototype.slice 函数。

综上所述,新函数会调用“call”函数,并且其“this”为“slice”函数。调用 slice() 就会指向之前限定的方法。

自定义对象的方法

继承很棒,但是开发者通常在想要重用一些对象或模块间的通用功能时才会使用。没必要仅为代码重用使用继承,因为在多数情况下简单的借用方法会很复杂。

之前我们只讨论了借用原生方法,但是借用任何方法都是可以的。比如下面的代码可以计算积分游戏的玩家分数:

var scoreCalculator = {
  getSum: function(results) {
    var score = 0;
    for (var i = 0, len = results.length; i < len; i++) {
      score = score + results[i];
    }
    return score;
  },
  getScore: function() {
    return scoreCalculator.getSum(this.results) / this.handicap;
  }
};
var player1 = {
  results: [69, 50, 76],
  handicap: 8
};
 
var player2 = {
  results: [23, 4, 58],
  handicap: 5
};
 
var score = Function.prototype.call.bind(scoreCalculator.getScore);
 
// Score: 24.375
console.log('Score: ' + score(player1));
 
// Score: 17
console.log('Score: ' + score(player2));

虽然上面的例子很生硬,但是可以看出,就像原生方法一样,用户定义的方法也可以轻松借用。

总结

Call、bind 和 apply 可以改变函数的调用方式,并且经常在借用函数时使用。多数开发者熟悉借用原生方法,但是较少借用自定义的方法。

近几年 JavaScript 的函数式编程发展不错,怎样使用 Function.prototype.call.bind 借用方法才更加简便?估计这样的话题会越来越常见。

以上就是JavaScript中的借用方法总结的全部内容,希望对大家了解到JavaScript中的借用方法有所帮助吧。

相关文章

  • 深入探密Javascript数组方法

    深入探密Javascript数组方法

    这篇文章主要详细介绍了Javascript数组方法,十分的全面详尽,有需要的小伙伴可以来研究下,希望对大家能有所帮助
    2015-01-01
  • 浅谈Javascript 执行顺序

    浅谈Javascript 执行顺序

    这篇文章主要介绍了Javascript 执行顺序,有需要的朋友可以参考一下
    2013-12-12
  • 深入理解JS异步编程-Promise

    深入理解JS异步编程-Promise

    Promise 是 ES6 新增的一个异步解决方案,它同 await 一样,是异步的解决方案,但是相比于 await,Promise 可以同时统一处理多个异步操作。这一点是很方便的。下面我们来一起学习一下吧
    2019-06-06
  • 提取字符串中年月日的函数代码

    提取字符串中年月日的函数代码

    提取字符串中年月日的函数代码,需要的朋友可以过来参考下。希望对大家有所帮助
    2013-11-11
  • 判断浏览器的js代码

    判断浏览器的js代码

    判断浏览器的js代码...
    2007-01-01
  • JavaScript中的this关键字使用详解

    JavaScript中的this关键字使用详解

    这篇文章主要介绍了JavaScript中的this关键字使用,是JavaScript入门学习中的基础知识,需要的朋友可以参考下
    2015-08-08
  • 实例分析javascript中的call()和apply()方法

    实例分析javascript中的call()和apply()方法

    因项目需求去研究了下javascript中的call和apply方法,去百度看了几篇介绍JS中call和apply的文章,总觉得不是很好懂,这里写下我自己的理解,供网友们参考。
    2014-11-11
  • javascript基础数据类型转换教程示例

    javascript基础数据类型转换教程示例

    总结了javascipt中的数据类型之后,我们就了解了,prompt()接收的全是字符串,因此数据类型转换就显得尤为重要,一起来学习一下吧
    2022-03-03
  • JavaScript 学习笔记之数据类型

    JavaScript 学习笔记之数据类型

    javascript数据类型非常简单,仅仅包含undefined、null、string、Boolean、number以及object,今天我们就针对这几个数据类型,一一进行讲解,方便大家理解记忆
    2015-01-01
  • element $notify 悬浮通知使用详解

    element $notify 悬浮通知使用详解

    这篇文章主要介绍了element $notify 悬浮通知使用详解,主要讲解如何使用this.$notify做一个悬浮通知,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2020-06-06

最新评论