浅谈javascript的call()、apply()、bind()的用法


Posted in Javascript onFebruary 21, 2016

JavaScript中的函数不仅是一种类似于Java中方法的语言功能,它还可以作为对象而存在。 本文将要探讨JavaScript中函数的一些特殊用法,包括call、apply、bind三个原型方法。
一、函数基础
JavaScript中的函数是一种类似于Java中方法的语言功能,不过它可以独立于类进行定义。

函数式编程:由于JavaScript支持匿名函数,因此可以将函数作为对象来使用, 所以JavaScript不仅支持过程式编程(面向对象也是过程式编程的一种),还支持函数式编程。
上下文

函数的每次调用都会拥有一个特殊值——本次调用的上下文(context)——这就是this关键字的值。 如果函数挂载在一个对象上,作为对象的一个属性,就称它为对象的方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文, 也就是该函数的this的值。

需要注意,this是一个关键字,不是变量,也不是属性名。JavaScript的语法不允许给this赋值。

函数是一种对象

JavaScript中的函数和Java中的方法或C语言中的函数最大不同在于,JavaScript中的函数也是一种对象。 但这并不意味着,所有的对象都是函数。函数是一种包含了可执行代码,并能够被其他代码调用的特殊的对象。

和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。 - 如果嵌套函数作为方法调用,其this的值指向调用它的对象。 - 如果嵌套函数作为函数调用,其this的值不是全局对象(非严格模式下)就是undefined(严格模式下)。

很多人误以为调用嵌套函数时this会指向掉i用外层函数的上下文。如果你想访问这个外部函数的this值, 需要将this的值保存都在一个变量中,这个变量和内部函数都同在一个作用域内。例如:

var o = {
 m: function() {
  var self = this;
  console.log(this==o); // true
  f();
  
  function f() {
   console.log(this === o); // false,this的值是全局对象或undefined
   console.log(self === o); // true
  }
 }
}

闭包

JavaScript的函数可以嵌套在其他函数中定义,这样它们就可以访问它们被定义时所处的作用域中的任何变量。 这意味着JavaScript函数构成了一个闭包(closure),它给JavaScript带来了非常强劲的编程能力。

作为值的函数
在JavaScript中,函数不仅是一种语法,也是值,也就是说,可以将函数赋值给变量,存储在对象的属性或数组的元素中, 作为参数传入另外一个函数等。

bind、call、apply
每一个函数都包含一个prototype属性,这个属性是指向一个对象的引用,这个对象称作“原型对象”。 每一个函数都包含不同的原型对象。当将函数用作构造函数的时候,新创建的对象会从原型对象上继承属性。

Function.prototype.call()Function.prototype.apply()

call()和apply()可以看作为某个对象的方法,通过调用方法的形式来间接调用函数。 它们的第一个参数是要调用函数的母对象,它是调用上下文,在函数体内通过this来获得对它的引用。 apply()方法和call()方法的作用相同,只不过函数传递的方式不一样,它的实参都放入在一个数组中。

举个例子,以对象o的方法的形式调用函数f(),并传入两个参数,可以使用这样的代码:

var o = {};

function f(a, b) {
 return a + b;
}

f.call(o, 1, 2);    // 将函数f作为o的方法,实际上就是重新设置函数f的上下文
f.apply(o, [1, 2]);

再举一个例子,使用call方法调用匿名函数:

在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。 这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。 当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通参数就可以),目的是为了演示call的用法。

var animals = [
 {species: 'Lion', name: 'King'},
 {species: 'Whale', name: 'Fail'}
];

for (var i = 0; i < animals.length; i++) {
 (function (i) { 
  this.print = function () { 
   console.log('#' + i + ' ' + this.species + ': ' + this.name); 
  } 
  this.print();
 }).call(animals[i], i);
}

Function.prototype.bind()

bind()是在ES5中新增的方法,从名字可以看出,这个方法的主要作用就是将函数绑定到某个对象。 当在函数f()上调用bind()方法并后窜入一个对象o作为参数,这个方法将返回一个新函数: (以函数调用的方式)调用新的函数将会把原始的函数f()作为o的方法来调用。例如:

function f(y) {
 return this.x + y;
}

var o = {
 x: 1
};

var g = f.bind(o); // 通过调用 g(x) 来调用 o.f(x)
g(2); // 3

其实我们可以轻松的实现bind()方法:

// 返回一个函数,通过调用它来调用o中的方法f(),传递它所有的实参
function bind(f, o) {
 if (f.bind) return f.bind(o); // 如果bind()方法存在,使用bind()方法
 else return function () {
  return f.apply(o, arguments);
 }
}

二、函数式编程
JavaScript并非函数式编程语言,但在JavaScript中可以像操控对象一样操控函数,也就是说可以在JavaScript中应用函数式编程技术。

使用函数处理数组

假设有一个数组,数组元素都是数字,我们想要计算这些元素的平均值和标准差。

var data = [1, 1, 3, 5, 5];
var sum = function(x, y) {
 return x + y;
};
var square = function(x) {
 return x * x;
};

var mean = data.reduce(sum)/data.length;
var deviations = data.map(x => x - mean);

var stddev = Math.sqrt(deviations.map(square).reduce(sum)/(data.length - 1));
高阶函数

高阶函数就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数。举个例子:

function not(f) {
 return function () {
  var result = f.apply(this, arguments);
  return !result;
 };
}

// 判断x是否为偶数的函数
var even = function(x) {
 return x % 2 === 0;
};

var odd = not(even);      // 一个新函数,所做的事情和even()相反
[1, 1, 3, 5, 5].every(odd);   // true,每个函数都是奇数

函数not()是个高阶函数,因为它返回一个新的函数,这个新函数将它的实参传入f(),并返回f的返回值的逻辑非。

以上就是关于javascript的call()、apply()、bind()的用法,希望对大家的学习有所帮助。

Javascript 相关文章推荐
用js重建星际争霸
Dec 22 Javascript
js 调用本地exe的例子(支持IE内核的浏览器)
Dec 26 Javascript
基于jquery的文章中所有图片width大小批量设置方法
Aug 01 Javascript
页面载入结束自动调用js函数示例
Sep 23 Javascript
利用js读取动态网站从服务器端返回的数据
Feb 10 Javascript
使用Meteor配合Node.js编写实时聊天应用的范例
Jun 23 Javascript
jQuery过滤HTML标签并高亮显示关键字的方法
Aug 07 Javascript
JavaScript SHA1加密算法实现详细代码
Oct 06 Javascript
微信小程序实现流程进度的图样式功能
Jan 16 Javascript
vue仿element实现分页器效果
Sep 13 Javascript
JavaScript 点击触发复制功能实例详解
Nov 02 Javascript
TypeScript高级用法的知识点汇总
Dec 17 Javascript
EasyUI闪屏EasyUI页面加载提示(原理+代码+效果图)
Feb 21 #Javascript
javascript+HTML5自定义元素播放焦点图动画
Feb 21 #Javascript
JavaScript编程学习技巧汇总
Feb 21 #Javascript
Node.js开发者必须了解的4个JS要点
Feb 21 #Javascript
JSON简介以及用法汇总
Feb 21 #Javascript
javascript实现计时器的简单方法
Feb 21 #Javascript
JS中Eval解析JSON字符串的一个小问题
Feb 21 #Javascript
You might like
深入apache配置文件httpd.conf的部分参数说明
2013/06/28 PHP
php中$_GET与$_POST过滤sql注入的方法
2014/11/03 PHP
又十个超级有用的PHP代码片段
2015/09/24 PHP
jquery multiSelect 多选下拉框
2010/07/09 Javascript
javascript中字符串拼接需注意的问题
2010/07/13 Javascript
jQuery实现类似滑动门切换效果的层切换
2013/09/23 Javascript
JS实现点击图片在当前页面放大并可关闭的漂亮效果
2013/10/18 Javascript
jQuery对html元素取值与赋值的方法
2013/11/20 Javascript
jquery判断元素的子元素是否存在的示例代码
2014/02/04 Javascript
js实现鼠标滑过文字链接色彩变化的效果
2015/05/06 Javascript
JS中frameset框架弹出层实例代码
2016/04/01 Javascript
老生常谈 js中this的指向
2016/06/30 Javascript
JavaScript中误用/g导致的正则test()无法正确重复执行的解决方案
2016/07/27 Javascript
js中获取键盘事件的简单实现方法
2016/10/10 Javascript
react.js 翻页插件实例代码
2017/01/19 Javascript
JavaScript 数据类型详解
2017/03/13 Javascript
Three.js开发实现3D地图的实践过程总结
2017/11/20 Javascript
Vue+jquery实现表格指定列的文字收缩的示例代码
2018/01/09 jQuery
js将键值对字符串转为json字符串的方法
2018/03/30 Javascript
基于JavaScript实现随机点名器
2021/02/25 Javascript
Python中使用动态变量名的方法
2014/05/06 Python
python中pandas.DataFrame排除特定行方法示例
2017/03/12 Python
浅谈python中的__init__、__new__和__call__方法
2017/07/18 Python
使用sklearn之LabelEncoder将Label标准化的方法
2018/07/11 Python
python使用pip安装SciPy、SymPy、matplotlib教程
2019/11/20 Python
Pytorch 定义MyDatasets实现多通道分别输入不同数据方式
2020/01/15 Python
CSS3教程(10):CSS3 HSL声明设置颜色
2009/04/02 HTML / CSS
详解background属性的8个属性值(面试题)
2020/11/02 HTML / CSS
html5视频媒体标签video的使用方法及完整参数说明详解
2019/09/27 HTML / CSS
美国最灵活的移动提供商:Tello
2017/07/18 全球购物
写一个在SQL Server创建表的SQL语句
2012/03/10 面试题
ASP.NET中的身份验证有那些
2012/07/13 面试题
单位消防安全制度
2014/01/12 职场文书
小学数学教学反思范文
2016/02/16 职场文书
演讲稿:​快乐,从不抱怨开始!
2019/04/02 职场文书
导游词之安徽醉翁亭
2020/01/10 职场文书