谈谈JavaScript中的几种借用方法


Posted in Javascript onAugust 09, 2016

前言

通过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。这样写很??拢?芸炀突崃钊松?帷8?行У姆椒ㄊ鞘褂米置媪坷创锏酵??哪康摹?/p>

使用字面量借用方法

字面量是一种遵循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 相关文章推荐
GridView中获取被点击行中的DropDownList和TextBox中的值
Jul 18 Javascript
jquery each的几种常用的使用方法示例
Jan 21 Javascript
js加载读取内容及显示与隐藏div示例
Feb 13 Javascript
jQuery实现手机自定义弹出输入框
Jun 13 Javascript
jQuery插件实现可输入和自动匹配的下拉框
Oct 24 Javascript
canvas知识总结
Jan 25 Javascript
js中new一个对象的过程
Feb 20 Javascript
从零开始搭建webpack+react开发环境的详细步骤
May 18 Javascript
Vue配合iView实现省市二级联动的示例代码
Jul 27 Javascript
js继承的这6种方式!(上)
Apr 23 Javascript
Vue 打包体积优化方案小结
May 20 Javascript
vue 中this.$set 动态绑定数据的案例讲解
Jan 29 Vue.js
Backbone View 之间通信的三种方式
Aug 09 #Javascript
Backbone中View之间传值的学习心得
Aug 09 #Javascript
全面了解函数声明与函数表达式、变量提升
Aug 09 #Javascript
jQuery 生成svg矢量二维码
Aug 09 #Javascript
浅谈JavaScript中变量和函数声明的提升
Aug 09 #Javascript
浅谈js基本数据类型和typeof
Aug 09 #Javascript
js中判断变量类型函数typeof的用法总结
Aug 09 #Javascript
You might like
php获得客户端浏览器名称及版本的方法(基于ECShop函数)
2015/12/23 PHP
详解PHP中的序列化、反序列化操作
2017/03/21 PHP
python进程与线程小结实例分析
2018/11/11 PHP
PHP 对象接口简单实现方法示例
2020/04/13 PHP
JavaScript脚本语言在网页中的简单应用
2007/05/13 Javascript
尝试在让script的type属性等于text/html
2013/01/15 Javascript
JQuery实现鼠标滑过显示导航下拉列表
2013/09/12 Javascript
D3.js实现折线图的方法详解
2016/09/21 Javascript
详解Vue2.X的路由管理记录之 钩子函数(切割流水线)
2017/05/02 Javascript
vue.js的安装方法
2017/05/12 Javascript
jQuery+Ajax实现用户名重名实时检测
2017/06/01 jQuery
Vuex利用state保存新闻数据实例
2017/06/28 Javascript
js中DOM事件绑定分析
2018/03/18 Javascript
微信小程序实现指定显示行数多余文字去掉用省略号代替
2018/07/25 Javascript
Vue数据双向绑定原理实例解析
2020/05/15 Javascript
[02:51]DOTA2 2015国际邀请赛中国区预选赛第一日战报
2015/05/27 DOTA
[37:29]完美世界DOTA2联赛PWL S2 LBZS vs Forest 第二场 11.19
2020/11/19 DOTA
使用Python脚本对Linux服务器进行监控的教程
2015/04/02 Python
操作Windows注册表的简单的Python程序制作教程
2015/04/07 Python
Python中的pass语句使用方法讲解
2015/05/14 Python
Python发送http请求解析返回json的实例
2018/03/26 Python
Python实现简单http服务器
2018/04/12 Python
numpy 进行数组拼接,分别在行和列上合并的实例
2018/05/08 Python
PyTorch中的padding(边缘填充)操作方式
2020/01/03 Python
英国假睫毛购买网站:FalseEyelashes.co.uk
2018/05/23 全球购物
POS解决方案:MUNBYN(热敏打印机、条形码扫描仪)
2020/06/09 全球购物
Myprotein亚太地区:欧洲第一在线运动营养品牌
2020/12/20 全球购物
shell程序中如何注释
2012/02/17 面试题
试用期工作表现自我评价
2015/03/06 职场文书
反邪教警示教育活动总结
2015/05/09 职场文书
新年晚会主持词开场白
2015/05/28 职场文书
八月一日观后感
2015/06/10 职场文书
一个家长教育孩子的心得体会
2016/01/15 职场文书
2019年大学生职业生涯规划书
2019/03/25 职场文书
ES6 解构赋值的原理及运用
2021/05/25 Javascript
Go语言测试库testify使用学习
2022/07/23 Golang