谈谈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 相关文章推荐
javascript中对对层的控制
Dec 29 Javascript
使用RequireJS优化JavaScript引用代码的方法
Jul 01 Javascript
javascript数据类型验证方法
Dec 31 Javascript
jQuery树形插件jquery.simpleTree.js用法分析
Sep 05 Javascript
微信小程序开发图片拖拽实例详解
May 05 Javascript
微信小程序中button组件的边框设置的实例详解
Sep 27 Javascript
Vue中 key keep-alive的实现原理
Sep 18 Javascript
详解JS实现系统登录页的登录和验证
Apr 29 Javascript
Vue对象赋值视图不更新问题及解决方法
Jun 03 Javascript
layui 关闭open弹出框 刷新table表格页面的方法
Sep 16 Javascript
微信小程序实现菜单左右联动
May 19 Javascript
vue使用svg文件补充-svg放大缩小操作(使用d3.js)
Sep 22 Javascript
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 adodb介绍
2009/03/19 PHP
用PHP实现 上一篇、下一篇的代码
2012/09/29 PHP
搭建自己的PHP MVC框架详解
2017/08/16 PHP
thinkphp框架表单数组实现图片批量上传功能示例
2020/04/04 PHP
escape、encodeURI、encodeURIComponent等方法的区别比较
2006/12/27 Javascript
E3 tree 1.6在Firefox下显示问题的修复方法
2013/01/30 Javascript
ExtJS判断IE浏览器类型的方法
2014/02/10 Javascript
JavaScript中的typeof操作符用法实例
2014/04/05 Javascript
javascript bom是什么及bom和dom的区别
2015/11/26 Javascript
javascript实现右侧弹出“分享到”窗口效果
2016/02/01 Javascript
js密码强度实时检测代码
2016/03/02 Javascript
JS实现unicode和UTF-8之间的互相转换互转
2017/07/05 Javascript
vue proxyTable 接口跨域请求调试的示例
2017/09/12 Javascript
vue router 组件的高级应用实例代码
2019/04/08 Javascript
详解小程序毫秒级倒计时(适用于拼团秒杀功能)
2019/05/05 Javascript
通过实例解析JavaScript for in及for of区别
2020/06/15 Javascript
vue+element UI实现树形表格
2020/12/29 Vue.js
[01:23:35]Ti4主赛事胜者组 DK vs EG 1
2014/07/19 DOTA
python数据类型_字符串常用操作(详解)
2017/05/30 Python
20个常用Python运维库和模块
2018/02/12 Python
python3利用Dlib19.7实现人脸68个特征点标定
2018/02/26 Python
什么是python的函数体
2020/06/19 Python
HTML5 input placeholder 颜色修改示例
2014/05/30 HTML / CSS
详解Canvas 跨域脱坑实践
2018/11/07 HTML / CSS
Jo Malone美国官网:祖玛珑香水
2017/03/27 全球购物
Dr.Jart+美国官网:韩国药妆品牌
2019/01/18 全球购物
电信营业员自我评价分享
2014/01/17 职场文书
社会调查研究计划书
2014/05/01 职场文书
体育节口号
2014/06/19 职场文书
新教师培训心得体会
2014/09/02 职场文书
家庭贫困证明范本(经典版)
2014/09/22 职场文书
2014年售票员工作总结
2014/11/19 职场文书
新郎结婚感言
2015/07/31 职场文书
认识实习感想
2015/08/10 职场文书
聊聊JS ES6中的解构
2021/04/29 Javascript
Keras在mnist上的CNN实践,并且自定义loss函数曲线图操作
2021/05/25 Python