this对象
this总是返回一个对象,简单说,就是返回属性或方法“当前”所在的对象。
var book = { name :'flydean', getName : function (){ return '书名:'+ this.name; } } console.log(book.getName()); //书名:flydean
这里this的指向是可变的,我们看一个例子 :
var book = { name :'flydean', getName : function (){ return '书名:'+ this.name; } } var car ={ name :'car' } car.getName = book.getName; console.log(car.getName()); //书名:car
当 A 对象的方法被赋予 B 对象,该方法中的this就从指向 A 对象变成了指向 B 对象
上面的例子中,我们把book中的getName方法赋值给了car对象,this对象现在就指向了car。
如果某个方法位于多层对象的内部,这时this只是指向当前一层的对象,而不会继承更上面的层。
var book1 = { name :'flydean', book2: { getName : function (){ return '书名:'+ this.name; } } } console.log(book1.book2.getName()); //书名:undefined
上面的例子中,this是定义在对象中的函数中,如果是在函数中的函数中定义的this,代表什么呢?
var book3 = { name :'flydean', book4: function(){ console.log('book4'); var getName = function (){ console.log(this); //Window }(); } } book3.book4();
如果在函数中的函数中使用了this,那么内层的this指向的是全局的window对象。
所以我们在使用的过程中要避免多层 this。由于this的指向是不确定的,所以切勿在函数中包含多层的this。
如果在全局环境使用this,它指的就是顶层对象window。
数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。
var book5 ={ name : 'flydean', author : ['max','jacken'], f: function (){ this.author.forEach(function (item) { console.log(this.name+' '+item); }) } } book5.f(); //undefined max //undefined jacken
foreach方法的回调函数中的this,其实是指向window对象,因此取不到o.v的值。原因跟上一段的多层this是一样的,就是内层的this不指向外部,而指向顶层对象。
怎么解决呢?我们使用一个中间变量:
var book6 ={ name : 'flydean', author : ['max','jacken'], f: function (){ var that = this; this.author.forEach(function (item) { console.log(that.name+' '+item); }) } } book6.f(); //flydean max //flydean jacken
或者将this当作foreach方法的第二个参数,固定它的运行环境:
var book7 ={ name : 'flydean', author : ['max','jacken'], f: function (){ this.author.forEach(function (item) { console.log(this.name+' '+item); },this) } } book7.f(); //flydean max //flydean jacken
绑定this的方法
JavaScript提供了call、apply、bind这三个方法,来切换/固定this的指向.
call
函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数.
var book = {}; var f = function () { return this; } f() === this ; //true f.call(book) === book; //true
上面例子中,如果直接调用f(),那么返回的就是全局的window对象。如果传入book对象,那么返回的就是book对象。
call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象。
如果call方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call方法。
var f = function () { return this; } console.log(f.call(100)); //[Number: 100]
call方法还可以接受多个参数.
func.call(thisValue,arg1,arg2, ...);
call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数。
call一般用在调用对象的原始方法:
var person = {}; person.hasOwnProperty('getName');//false //覆盖person的getName方法 person.getName = function(){ return true; } person.hasOwnProperty('getName');//true Object.prototype.hasOwnProperty.call(person,'getName');//false
apply
apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数.
func.apply(thisValue,[arg1,arg2,...])
bind
call和apply是改变this的指向,然后调用该函数,而bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数.
var d = new Date(); console.log(d.getTime()); //1600755862787 var getTime= d.getTime; console.log(getTime());//TypeError: this is not a Date object.
上面的例子中,getTime方法里面调用了this,如果直接把d.getTime赋值给getTime变量,那么this将会指向全局的window对象,导致运行错误。
我们可以这样修改:
var d = new Date(); console.log(d.getTime()); //1600755862787 var getTime2= d.getTime.bind(d); console.log(getTime2());
bind比call方法和apply方法更进一步的是,除了绑定this以外,还可以绑定原函数的参数。
var add = function(x,y){ return x +this.m + y + this.n; } var addObj ={ m: 10, n: 10 } var newAdd = add.bind(addObj,2); console.log(newAdd(3));//25
上面的例子中,bind将两个参数的add方法,替换成了1个参数的add方法。
注意,bind每次调用都会返回一个新的函数,从而导致无法取消之前的绑定。