上下文对象
首先我们通过以下一个简单的例子来说明上下文对象的使用情况:
var info = { name:"信息对象", fn:function () { console.log(this.name); }}var test ={ name:"老李头"};test.getName = info.fn;info.fn();//信息对象test.getName();//老李头
我们知道,函数中的this指的是被调用函数运行时的上下文环境,而上下文对象指的就是函数体中的this,它的作用是在函数体内引用调用它的对象本身。我们依据这个原则来分析一下上边这段代码:调用info.fn()时,被调用函数fn()的从属对象是info;而当把info.fn赋值给test.getName变量上后,在执行test.getName()后,this指针指向了test对象。顺着这个思路,我们来看一下下边这段代码:
var info = { name:"信息对象", fn:function () { console.log(this.name); }}var test ={ name:"老李头"};test.getName = info.fn;var name="全局变量";var fn = info.fn;info.fn();//信息对象test.getName();//老李头fn();//全局变量 此时依赖对象为window
综上我们可以说:使用不同的引用来调用同一个函数时,this永远是这个引用所属的对象。
改变上下文对象
事实上,函数与对象的从属关系,只有在函数被调用时才体现出来!我们在实际的开发过程中,经常会遇到一个对象调用另外一个对象的成员函数的现象。处理方法有很多。call和apply就是用不同的对象作为上下文对象来调用函数,也就是我们平时所说的改变this的指向问题。
call 和 apply
call 和 apply 的功能是一致的,两者细微的差别在于 call 以参数表来接受被调用函数的参数,而 apply 以数组来接受被调用函数的参数。call 和 apply 的语法分别是:
被调用函数.call(上下文对象,参数1,参数2,参数3……);被调用函数.apply(上下文对象,[参数1,参数2,参数3……])
话不多说,通过下边一段代码我们来理解一下call的工作原理:
var person= { name:"老李头", tell:function (txt) { console.log(this.name+" says: "+txt) }}var lee = { name:"henyulee"}person.tell.call(lee,"hello world!");//henyulee says: hello world!
bind
上边说过,可以通过call和apply来绑定上下文对象,但是每次使用都会把上下文作为参数传递,这样会很麻烦,那么有没有一劳永逸的办法呢?我们可以使用bind方法来永久的绑定调用函数的上下文对象。
通过下边一段代码来理解一下bind的使用场景:
var person = { name:"老李头", fn:function (job) { console.log(this.name+"是一个:"+job); }}var user1 = { name:"henyulee"};user1.fn= person.fn;user1.fn("北漂");//henyulee是一个北漂user1.fn = person.fn.bind(person,"前端开发");user1.fn();//老李头是一个:前端开发var getJob = person.fn.bind(person,"思想漫步者");getJob();//老李头是一个:思想漫步者
我们来解读一下上边这段代码:首先user1.fn直接赋值为person.fn,调用user1.fn()时,此时this指向的是引用fn的user1对象,所以我们拿到的是:henyulee。当用bind方法时,将person作为this指针绑定到user1.fn和getJob2个被调用函数上。所以得到的是person的属性信息。
bind的参数绑定
在使用bind绑定上下文对象时,要注意其参数的传递和之前不太一样,如果参数过多不想一次性传递完整,可以分开传递。
var love = { act:"love", fn:function (man,woman) { console.log(man +" "+this.act+" "+ woman); }}var user = love.fn.bind(love,"Romeo");user("Juliet");//Romeo love Juliet
观察上边代码,我们不难看出,我们可以把那些参数值固定的首先传递,后续有不一样的参数,作为单独传递即可。这样可以提高开发效率,较少冗余代码!
bind的原理
首先看下边这段代码:
var love = { act:"love", fn:function (man,woman) { console.log(man +" "+this.act+" "+ woman); }}var person ={ act:"hate"}person.fn = love.fn.bind(person,"Romeo","Juliet");person.fn();//Romeo hate Julietvar fn = person.fn.bind(love);fn();//Romeo hate Juliet
person.fn通过love.fn.bind方法将this绑定到了person,调用时返回person的信息。这个时候我们后悔了,想要再绑定回去,发现并不是我们理想中的那样,那么问题来了:发生了什么?要想解释这个现象,我们必须了解 bind 方法的原理。
函数.bind = function (self) { return this.call(self);}
那么我们将上边令我们疑惑的步骤分解如下:
//省略的参数person.fn = function () { return love.fn.call(person)}fn = function () { person.fn.call(love)}