浅谈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 相关文章推荐
JavaScript全局函数使用简单说明
Mar 11 Javascript
jquery datatable后台封装数据示例代码
Aug 07 Javascript
JavaScript常用的返回,自动跳转,刷新,关闭语句汇总
Jan 13 Javascript
javascript判断变量是否有值的方法
Apr 20 Javascript
vue+vux实现移动端文件上传样式
Jul 28 Javascript
使用watch监听路由变化和watch监听对象的实例
Feb 24 Javascript
jQuery+ajax读取json数据并按照价格排序示例
Mar 28 jQuery
Angular路由ui-router配置详解
Aug 01 Javascript
在vue中解决提示警告 for循环报错的方法
Sep 28 Javascript
JavaScript中BOM对象原理与用法分析
Jul 09 Javascript
javascript json对象小技巧之键名作为变量用法分析
Nov 11 Javascript
JS实现简单贪吃蛇小游戏
Oct 28 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
php做下载文件的实现代码及文件名中乱码解决方法
2011/02/03 PHP
使用PHP实现下载CSS文件中的图片
2015/12/06 PHP
jquery中.add()的使用分析
2013/04/26 Javascript
全面了解JavaScirpt 的垃圾(garbage collection)回收机制
2016/07/11 Javascript
BootStrap实现轮播图效果(收藏)
2016/12/30 Javascript
深入解析nodejs HTTP服务
2017/07/25 NodeJs
vue-cli3 DllPlugin 提取公用库的方法
2019/04/24 Javascript
解决vue单页面修改样式无法覆盖问题
2019/08/05 Javascript
vue input标签通用指令校验的实现
2019/11/05 Javascript
Vue实例的对象参数options的几个常用选项详解
2019/11/08 Javascript
基于JS判断对象是否是数组
2020/01/10 Javascript
python with statement 进行文件操作指南
2014/08/22 Python
python编写暴力破解FTP密码小工具
2014/11/19 Python
python检测某个变量是否有定义的方法
2015/05/20 Python
python实现unicode转中文及转换默认编码的方法
2017/04/29 Python
Python读取文件内容的三种常用方式及效率比较
2017/10/07 Python
利用Python2下载单张图片与爬取网页图片实例代码
2017/12/25 Python
python爬取基于m3u8协议的ts文件并合并
2019/04/26 Python
Python画图实现同一结点多个柱状图的示例
2019/07/07 Python
jupyter修改文件名方式(TensorFlow)
2020/04/21 Python
解决pycharm导入numpy包的和使用时报错:RuntimeError: The current Numpy installation (‘D:\\python3.6\\lib\\site-packa的问题
2020/12/08 Python
python 数据类型强制转换的总结
2021/01/25 Python
波兰家居和花园家具专家:4Home
2019/05/26 全球购物
德国领先的大尺码和超大尺码男装在线零售商:Bigtex
2019/06/22 全球购物
金融专业大学生自我评价
2014/01/09 职场文书
新年抽奖获奖感言
2014/03/02 职场文书
8和9的加减法教学反思
2014/05/01 职场文书
cf战队收人口号
2014/06/21 职场文书
2015年感恩父亲节活动策划方案
2015/05/05 职场文书
未婚证明范本
2015/06/15 职场文书
行政处罚事先告知书
2015/07/01 职场文书
微信早安问候语
2015/11/10 职场文书
2016年秋季运动会加油稿
2015/12/21 职场文书
创作书写之导游词实用技巧分享(干货)
2019/12/20 职场文书
python爬取企查查企业信息之selenium自动模拟登录企查查
2021/04/08 Python
解决Django transaction进行事务管理踩过的坑
2021/04/24 Python