this对象以及call、apply、bind方法
在现在前端面试中,最喜欢考的两点,第一,原型和原型链;第二,作用域和闭包。在作用域这一块,this对象一般都是必问的环节。JS所有的函数作用域内都有一个this对象代表该函数的对象。在全局作用域中,this代表全局对象。当一个函数作为对象的方法调用时,默认的this的值等于那个对象。
1.也就是说,谁调用这个方法,this就指向谁。请看下面一个例子
var name = "Tom"; function sayNameAll(){ console.log(this.name); } var person = { name:"xiaoming", sayName:sayNameAll }; person.sayName(); //outputs "xiaoming" sayNameAll(); //outputs "Tom"
在上面的例子中,我们发现一个现象,在普通函数中,this指向window对象,所以我们调用sayNameAll方法,输出的是全局变量Tom。
2.构造函数中this指向实例化的对象,而普通函数中this指向的window对象
var name = "博客园"; function frintName(){ var name = "网易"; console.log(this.name); } frintName(); //outputs "博客园" function Frint(){ this.name = "GitHub"; this.frint = function(){ console.log(this.name); } } var obj = new Frint(); obj.frint(); //outputs"GitHub"
3.定时器中的this指向的是window对象。因为setTimeout和setInterval这两个方法,是window的方法,只能window才能调用,所以这两个方法中,this都是指向window。
var name = "博客园"; function Frint(){ this.name = "GitHub"; this.frint1 = function(){ console.log(this.name); } this.frint2 = function(){ setTimeout(this.frint1,1000); } } var obj = new Frint(); obj.setTimeout(function(){ console.log(100); },100); //Uncaught TypeError: obj.setTimeout is not a function at <anonymous>,报错,只能window调用,当然也可以借用call等方法 obj.frint1(); //outputs"GitHub" obj.frint2(); //outputs"博客园",定时器中this指向window对象
4.一般this会被自动设置,但是我们也可以改变它的指向来完成不同的任务。有三种函数方法来改变this,这就是call,apply,bind。
先看call方法
var name = "博客园"; function frintName(){ console.log(this.name); } var name1 = { name:'Github' } var name2 = { name:'Html5' } frintName.call(this); //outputs "博客园",这里的this不写也可以,默认指向window frintName.call(name1); //outputs"Github" frintName.call(name2); //outputs"Html5"
由于我们显示的指定了this的值,而不是让js引擎自动指定,所有结果发生改变。
apply方法和call方法几乎一样,只是apply只接受两个参数,第一个参数是this的值,第二个是类数组对象,里面是要被传入函数的参数。而call方法中,参数一列排开。如果传入的参数很多,且是一个数组,那建议用apply,如果只是一个变量,那么用call方法
var name = "博客园"; function frintName(url1,url2){ console.log(url1+url2+this.name); } var name1 = { name:'Github' } frintName("web1","web2"); //outputs "web1web2博客园" frintName.call(name1,'web1',"web2"); //outputs "web1web2Github" frintName.apply(name1,['web1','web2']); //outputs"web1web2Github"
5.bind方法。
bind方法是ES5中增加的,所以它的用法和call、apply有一点区别。同样,他的第一个参数是传给this的值。
function frintName(url1){ console.log(url1+this.name); } var name1 = { name:'Github' } var name2 = { name:'Html5' } var nameFrint1 = frintName.bind(name1); nameFrint1('web1'); //outputs"web1Github" var nameFrint2 = frintName.bind(name2,'web2'); nameFrint2(); //outputs"web2Html5" name2.sayName = nameFrint1; name2.sayName('web2');//outputs"web2Github"
在上面倒数第二行,我们将nameFrint1设置为name2的sayName的方法,按照惯例,它应该输出name2 中的name值,可是我们发现,竟然输出了name1中的值,为何,他的this值已经绑定。所以,即使现在他是name2的方法,但是依然输出name1中的值。