在JavaScript中借用方法

在JavaScript中,有时候需要在一个不同的对象上重用一个函数,而不是在定义它的对象或者原型中。通过使用call(),applay()和bind(),我们可以很方便地从不同的对象借用方法,而不需要继承它们 – 这是一个在专业JavaScript开发者的工具箱中很有用的工具。

这篇文章假设你已经充分了解了call()apply() 和 bind() 以及它们的不同点。

在JavaScript中,你接触的几乎所有东西都是一个对象,除了string,number 和 booleans这样不可变的原始值。一个数组是一种对象类型,适合用于有序数据列表的遍历和修改,在它的原型中有很多有用的方法,例如slice,join,push 和 pop。

我们看到对象最常见的使用情况就是从一个数组中借用方法,因为它们都是列表类型的数据结构。最常被借用的方法是 Array.prototype.slice

function myFunc() {

    // 错误, arguments是一个类数组对象, 不是一个真实的数组
arguments.sort(); // 借用 Array 原型中的方法 slice, 它接受一个类数组对象 (key:value)
// 并返回一个真实的数组
var args = Array.prototype.slice.call(arguments); // args 现在是一个真正的数组, 所以可以使用Array的sort()方法
args.sort(); } myFunc('bananas', 'cherries', 'apples');

借用方法是可行的,因为call和apply允许我们在一个不同的上下文中调用方法,是一个很好的方式来重用已经存在的函数,而无需让一个对象扩展自另一个对象。数组实际上在它的原型中定义了很多方法,而且一般是可重用的,下面再举两个例子是join和filter:

// 接收一个字符串 "abc" 并输出 "a|b|c
Array.prototype.join.call('abc', '|');

// 接收一个字符串并移除所有的非元音字母
Array.prototype.filter.call('abcdefghijk', function(val) {
return ['a', 'e', 'i', 'o', 'u'].indexOf(val) !== -;
}).join('');

正如你所看到的,不仅仅对象可以得益于从数组中借用方法,字符串也可以。然而,因为方法一般被定义在了原型上,所以每次我们借用方法都要写String.prototype 或者 Array.prototype,这是很多余并且很烦人的事。一种有效的方法的是使用Literals(字面量)。

Literal是一种语法语言结构, 遵循 JavaScript 的规则,MDN 上这样解释:

你可以使用literals来表示JavaScript中的值。这些都是固定的值,不是变量,你可以再脚本中按照字面写出来。

Literals允许我们以缩略的形式访问原型方法:

[].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是一个引用,用来调用一个函数并且把它的“this”值设置为使用内部提到的方法。

  • 记住“bind”返回一个新的函数,这个函数总是会牢记它的“this”值。因此,.bind(Array.prototype.slice)会返回一个新的函数,它的“this”被永久地设置成了 Array.prototype.slice 函数。

通过结合以上两个,我们现在有了一个新的函数,它将会调用“call”函数并且“this”限定为了“slice”函数。简单地调用slice()便可以引用之前限定的方法。

继承很棒,但是当程序员想要重用一些对象或者模块中的常见功能时会经常求助于它。如果你正在单独使用继承来重用代码,你可能会做错事,在很多情况下,简单的借用一个方法会变得非常麻烦。

到目前为止,我们仅仅讨论了借用本地方法,但其实可以借用任何方法!使用下面的代码来计算一个运动员所得的比赛分数:

var scoreCalculator = {
getSum: function(results) {
var score = ;
for (var i = , len = results.length; i < len; i++) {
score = score + results[i];
}
return score;
},
getScore: function() {
return scoreCalculator.getSum(this.results) / this.handicap;
}
}; var player1 = {
results: [, , ],
handicap:
}; var player2 = {
results: [, , ],
handicap:
}; 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来借用方法将变得越来越普遍。

链接:https://www.zcfy.cc/article/borrowing-methods-in-javascript-by-david-shariff-794.html

Function.prototype.call.bind的更多相关文章

  1. 箭头函数表达式和声名式函数表达式的区别以及 Function.prototype的bind, apply,call方法

    箭头函数不能用做构造函数 箭头函数没有arguments参数 箭头函数没有自己的this,是从作用域链上取this,是与箭头函数定义的位置有关的,与执行时谁调用无关,所以用call,apply,bin ...

  2. Function.prototype.bind接口浅析

    本文大部分内容翻译自 MDN内容, 翻译内容经过自己的理解. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Glo ...

  3. 一起Polyfill系列:Function.prototype.bind的四个阶段

    昨天边参考es5-shim边自己实现Function.prototype.bind,发现有不少以前忽视了的地方,这里就作为一个小总结吧. 一.Function.prototype.bind的作用 其实 ...

  4. JavaScript 函数绑定 Function.prototype.bind

    ECMAScript Edition5 IE9+支持原生,作用为将一个对象的方法绑定到另一个对象上执行. Function.prototype.bind = Function.prototype.bi ...

  5. Function.prototype.bind

    解析Function.prototype.bind 简介 对于一个给定的函数,创造一个绑定对象的新函数,这个函数和之前的函数功能一样,this值是它的第一个参数,其它参数,作为新的函数的给定参数. b ...

  6. 解析Function.prototype.bind

    简介 对于一个给定的函数,创造一个绑定对象的新函数,这个函数和之前的函数功能一样,this值是它的第一个参数,其它参数,作为新的函数的给定参数. bind的作用 bind最直接的作用就是改变this的 ...

  7. javascript Function.prototype.bind

    语法: fn.bind(obj,arg1,arg2,arg3...) bind是es5新增的方法,顾名思义,它的作用是将函数绑定到某个对象上,就像是某个对象调用方法一样.其本质还是改变了该函数的上下文 ...

  8. 理解javascript中的Function.prototype.bind

    在初学Javascript时,我们也许不需要担心函数绑定的问题,但是当我们需要在另一个函数中保持上下文对象this时,就会遇到相应的问题了,我见过很多人处理这种问题都是先将this赋值给一个变量(比如 ...

  9. 浅析 JavaScript 中的 Function.prototype.bind() 方法

    Function.prototype.bind()方法 bind() 方法的主要作用就是将函数绑定至某个对象,bind() 方法会创建一个函数,函数体内this对象的值会被绑定到传入bind() 函数 ...

随机推荐

  1. Java连载20-复习、switch语句

    一.复习 1.标识符(自己定义的,下划线.美元符号) 2.驼峰命名(变量名,方法名首字母小写) 3.关键字(就是固定的那几个) 4.字面值(数据.有类型.八种基本类型从小到大,byte\char=sh ...

  2. LeetCode 2:两数相加 Add Two Numbers

    ​给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字.如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和 ...

  3. (转)dnSpy 强大的.Net反编译软件

    目录 1. Debug外部引用的Dll文件2. 调试应用程序3. 修改exe文件的内容 作者:D.泡沫 一说起.net的反编译软件,大家首先想到的就是Reflector,ILSpy,dotPeek等等 ...

  4. matplotlib的使用——pie(饼图)的使用

    在我们进行数据分析的时候需要对得出的数据进行可视化,因此我们需要引入第三方包来帮助我们进行可视化分析,在这里使用matplotlib 一.安装 使用指令[pip install matplotlib] ...

  5. CentOS安装etcd和flannel实现Docker跨物理机通信

    1.安装etcd yum install etcd systemctl stop etcd systemctl start etcd systemctl status etcd systemctl e ...

  6. Asp.Net SignalR 使用记录

    工作上遇到一个推送消息的功能的实现.本着面向百度编程的思想.网上百度了一大堆.主要的实现方式是原生的WebSocket,和SignalR,再次写一个关于Asp.Net SignalR 的demo 这里 ...

  7. annyconnect掉线之后重新链接

    sudo service vpnagentd restart /opt/cisco/anyconnect/bin/vpnui 重启服务+重新登录 deepin的优点之一是它的程序不会安装到各个角落里, ...

  8. 轻量级手绘软件openCanvas免费版,手绘板CG手绘软件

    轻量级手绘软件openCanvas免费版,手绘板CG手绘软件 手绘软件通俗一点来说就是用手来绘画的软件,应用很宽泛如建筑,服饰陈列设计.橱窗设计.家居软装设计.空间花艺设计.美术.园林.环艺.摄影.工 ...

  9. java--正则校验

    java--正则校验 // boolearn matches(String regex):判断当前字符串是否匹配指定的正则表达式true/false demo: String qq = "1 ...

  10. vue学习指南:第三篇(详细) - vue的生命周期

    今天小编给大家详细讲解一下 vue 的生命周期.希望大家多多指教,哪里有遗漏的地方,也请大家指点出来 谢谢. 一. 怎么理解 Vue 的生命周期的? 生命周期:从无到有,到到无的一个过程.Vue的生命 ...