×

Function.prototype之“三剑客”:call、apply和bind

作者:Terry2019.01.03来源:Web前端之家浏览:9457评论:0
关键词:callapplybind

500.jpg

2019年第一篇文章哦,聊聊JS的知识吧,学习下call、apply和bind三种方法,用实例分析下他们一些区别和应用。

众所周知,javascript中的call(), apply()和bind()是Function.prototype下的方法,都是用于改变函数运行时上下文,最终的返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined。

apply()

使用 apply, 你可以继承其他对象的方法:

var max = Math.max.apply(null, [1, 2, 3, 4, 5]);
console.log(max); // 输出5

注意这里apply()的第一个参数是null,在非严格模式下,第一个参数为null或者undefined时会自动替换为指向全局对象,
apply()的第二个参数为数组或类数组。

call()

call()是apply()的一颗语法糖,作用和 apply() 一样,同样可实现继承,唯一的区别就在于call()接收的是参数列表,而apply()则接收参数数组。

var max = Math.max.call(null, 1, 2, 3, 4, 5);
console.log(max); // 输出5

常见的call 和 apply 用法

数组之间追加

let arr1 = [12, 'foo', {name: 'fanchao'}, -1024];
let arr2 = ['copyes', '22', 1024];

Array.prototype.push.apply(arr1, arr2);
console.log(arr1);
// [ 12, 'foo', { name: 'fanchao' }, -1024, 'copyes', '22', 1024 ]

获取数组中的最大值和最小值

let numbers = [5,665,32,773,77,3,996];
let maxNum = Math.max.apply(Math, numbers);
let maxNum2 = Math.min.call(Math, 5,665,32,773,77,3,996);

console.log(maxNum);
console.log(maxNum2);

验证是否是数组(前提是toString()方法没有被重写过)

function isArray(obj){
    return Object.prototype.toString.call(obj)  === '[object Array]';
}

console.log(isArray(1));
console.log(isArray([1,2]));

类(伪)数组使用数组方法

var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

call和apply的作用和区别

call和apply用来调用函数,并用指定对象(第一个参数)替换函数的 this 值,同时用指定数组替换函数的参数。注:也可以不指定参数,此时只是单纯的调用函数,如:fun.call()

语法:

fun.call(thisobj,arg1,arg2) ;当后面参数个数确定时用call,参数之间用逗号连接

fun.apply(thisobj,[arg1,arg2]);当后面参数个数不确定时用apply,参数通过数组形式输入

call和apply的作用:

改变this的指向,第一个参数为你要传入的对象,传入后函数的this就指向了这个对象,后面的参数为你为函数传递的参数值

简单案例:

var str='js';
	function fo(){
		  var str='jq';
		  console.log(this.str);//此时this指向window
		  console.log(this===window);
		  console.log(this===obj);
		  //输出js,true,false
	}
	fo();
	var obj={
		  str:'html'
	};
fo.call(obj)//输出html,false,true,这就证明了call将fo的this指向改为了obj;
	var ob={
		  str:'css',
	}
var get=function(){
		  console.log(str);//js 此时读取的是全局变量的值
		  console.log(this.str);//css 此时this指向了ob,返回的就是ob.str
	}
	get.call(ob)

再来一个复杂点的例子:

function log(){
    var args=Array.prototype.slice.call(arguments);//将参数转为数组
  //slice(start,end(非必须) )方法可从已有的数组中返回选定的元素。
  //Javascript函数中的参数对象arguments是个对象,而不是数组。但它可以类似数组那样通过数字下表访问其中的元素,而且它也有length属性标识它的元素的个数。通常我们把它转换成数组用Array的slice函数,示例代码如下:function
     fn() { var arr = Array.prototype.slice.call(arguments,0);}
  //所以Array.prototype.slice.call(arguments)就是将参数转为数组然后返回数组
		  console.log(args);//Array [ "hello", "world" ]
		  args.unshift('(gykj)');//unshift(a,b,c)向数组的开头添加至少一个元素
		  console.log(args);//Array [ "(gykj)", "hello", "world" ]
		  console.log.apply(console,args);//(gykj) hello world 这里的apply可以将数组元素作为字符参数输出
  //由于参数个数不确定这里只能使用apply
}
log('hello','world')

bind()

MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

bind()的作用与call()和apply()一样,都是可以改变函数运行时上下文,区别是call()和apply()在调用函数之后会立即执行,而bind()方法调用并改变函数运行时上下文后,返回一个新的函数,供我们需要时再调用。

var person = {
  name: 'person',
  getName: function() {
    return this.name;
  }
}
var boy = {
   name: 'twy'
}
// bind()返回一个新函数,供以后调
var getName = person.getName.bind(boy);

// 现在调用
console.log(getName());    // 输出wy

一个有意思的事:

var bar = function() {
    console.log(this.x);
}
var foo = {
    x: 3
}
var sed = {
    x: 4
}
var func = bar.bind(foo).bind(sed);
func(); //3

var fiv = {
    x: 5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3

在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

用apply()模拟实现bind():

Function.prototype.bind = function(context) {
  // 保存调用函数的引用,这里是getName()
  var self = this;
  // 返回一个新函数
  return function(){
    return self.apply(context, arguments);
  }
}
var person = {
  name: 'twy'
}
var getName = function(){
  console.info(this.name);
}.bind(person);
// 执行bind()方法内返回的新函数
getName();

在返回的新函数内部,self.apply(context, arguments)才是执行原来的getName函数,相当于执行getName.apply(person);

  • 如果不需要关心具体有多少参数被传入函数,选用apply();

  • 如果确定函数可接收多少个参数,并且想一目了然表达形参和实参的对应关系,用call();

  • 如果我们想要将来再调用方法,不需立即得到函数返回结果,则使用bind();

  • call()、apply()和bind()都是用来改变函数执行时的上下文,可借助它们实现继承;

  • call()和apply()唯一区别是参数不一样,call()是apply()的语法糖;

  • bind()是返回一个新函数,供以后调用,而apply()和call()是立即调用


您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/prototype20190103.html

网友评论文明上网理性发言 已有0人参与

发表评论: