在js中,全部的函数在被调用的时候都会默认传入两个参数,一个是this,还有一个是arguments。在默认状况下this都是指当前的调用函数的对象。可是有时候咱们须要改变this的指向,也就是说使函数能够被其余对象来调用,那么咱们应该怎样作呢?这时候咱们就可使用call,apply和bind方法了。
那么call,apply和bind来自哪里?
在js中全部的函数都是Function的实例,并且对于Function来讲,它的原型即Function.prototype中含有不少东西,其中call,apply和bind方法就是Function原型中的方法,因此根据原型的规则,全部的函数均可以使用原型中属性和方法,因此来讲,对于全部的函数均可以使用call,apply和bind方法。数组
使用 apply, 你能够继承其余对象的方法:app
注意这里apply()的第一个参数是null,在非严格模式下,第一个参数为null或者undefined时会自动替换为指向全局对象,apply()的第二个参数为数组或类数组。函数
function fruits() {} fruits.prototype = { color: "red", say: function() { console.log("My color is " + this.color); } } var apple = new fruits; apple.say(); //My color is red
可是若是咱们有一个对象banana= {color : "yellow"}
,咱们不想对它从新定义say
方法,那么咱们能够经过call
或apply
用apple
的say
方法:ui
banana = { color: "yellow" } apple.say.call(banana); //My color is yellow apple.say.apply(banana); //My color is yellow
因此,能够看出call
和apply
是为了动态改变this
而出现的,当一个object
没有某个方法(本案例中banana
没有say
方法),可是其余的有(本案例中apple
有say
方法),咱们能够借助call
或apply
用其它对象的方法来操做this
call()是apply()的一颗语法糖,做用和apply()同样,一样可实现继承,惟一的区别就在于call()接收的是参数列表,而apply()则接收参数数组。prototype
bind()的做用与call()和apply()同样,都是能够改变函数运行时上下文,区别是call()和apply()在调用函数以后会当即执行,而bind()方法调用并改变函数运行时上下文后,返回一个新的函数,供咱们须要时再调用。code
this.num = 9; var mymodule = { num: 81, getNum: function() { console.log(this.num); } }; mymodule.getNum(); // 81 var getNum = mymodule.getNum; getNum(); // 9, 由于在这个例子中,"this"指向全局对象 var boundGetNum = getNum.bind(mymodule); boundGetNum(); // 81
bind()
方法会建立一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以建立它时传入bind()
方法的第一个参数做为this
,传入bind()
方法的第二个以及之后的参数加上绑定函数运行时自己的参数按照顺序做为原函数的参数来调用原函数。对象
一、call的arg传参需一个一个传,apply则直接传一个数组。继承
function hello(name,age){ console.log(name); console.log(age); } hello.call(this,"tsrot",24); hello.apply(this,["tsrot",24]);
二、call和apply直接执行函数,而bind须要再一次调用。get
var obj = { x: 81, }; var foo = { getX: function() { return this.x; } } console.log(foo.getX.bind(obj)()); console.log(foo.getX.call(obj)); console.log(foo.getX.apply(obj));
三、如何选用
一、实现继承
function Animal(name) { this.name = name; this.showName = function () { console.log(this.name); } } function Cat(name) { Animal.call(this, name); } var cat = new Cat('Black Cat'); cat.showName();
二、数组追加
var array1 = [1 , 2 , 3, 5]; var array2 = ["xie" , "li" , "qun" , "tsrot"]; Array.prototype.push.apply(array1, array2); console.log(array1);
三、获取数组中的最大值和最小值
var num = [1,3,5,7,2,-10,11]; var maxNum = Math.max.apply(Math, num); var minNum = Math.min.apply(Math, num); console.log(maxNum); console.log(minNum);
四、将伪数组转化为数组
var fakeArr = {0:'a',1:'b',length:2}; var arr1 = Array.prototype.slice.call(fakeArr); console.log(arr1[0]); var arr2 = [].slice.call(fakeArr); console.log(arr2[0]); arr1.push("c"); console.log(arr1);
五、保存this变量
var foo = { bar : 1, eventBind: function(){ var _this = this ; $('.someClass').on('click',function(event) { console.log(_this.bar); }); } } var foo = { bar : 1, eventBind: function(){ $('.someClass').on('click',function(event) { console.log(this.bar); }.bind(this)); } }
首先从如下几点来考虑如何实现这几个函数
window
this
指向,让新的对象能够执行该函数,并能接受参数先来实现call
Function.prototype.myCall = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window ; context.fn = this ; const args = [...arguments].slice(1); const result = context.fn(...args) ; delete context.fn ; return result ; }
如下是对实现的分析:
context
为可选参数,若是不传的话默认上下文为window
context
建立一个fn
属性,并将值设置为须要调用的函数call
能够传入多个参数做为调用函数的参数,因此须要将参数剥离出来以上就是实现call
的思路,apply
的实现也相似,区别在于对参数的处理,因此就不一一分析思路了
Function.prototype.myApply = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window ; context.fn = this; let result // 处理参数和 call 有区别 if (arguments[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn ; return result; }
bind
的实现对比其余两个函数略微地复杂了一点,由于bind
须要返回一个函数,须要判断一些边界问题,如下是bind
的实现
Function.prototype.myBind = function (context) { if (typeof this !== 'function') { throw new TypeError('Error') } const _this = this const args = [...arguments].slice(1) // 返回一个函数 return function F() { // 由于返回了一个函数,咱们能够 new F(),因此须要判断 if (this instanceof F) { return new _this(...args, ...arguments) } return _this.apply(context, args.concat(...arguments)) } }
如下是对实现的分析:
bind
返回了一个函数,对于函数来讲有两种方式调用,一种是直接调用,一种是经过new
的方式,咱们先来讲直接调用的方式apply
的方式实现,可是对于参数须要注意如下状况:由于bind
能够实现相似这样的代码f.bind(obj, 1)(2)
,因此咱们须要将两边的参数拼接起来,因而就有了这样的实现args.concat(...arguments)
new
的方式,对于new
的状况来讲,不会被任何方式改变this
,因此对于这种状况咱们须要忽略传入的this
将不断更新完善,期待您的批评指正!