回想当初在切图网做前端项目,用到js,经常用到this;那我们就谈谈js中this,如果说你没弄清this指向的问题,那基本可以说js没入门。
this指向的问题的文章,上网搜资料能搜一大把,基本上都是说this总是指向某个对象的,指向函数的调用者的。
如果某个函数没有调用者时,this是指向window的。
先来看看是怎么总结出来,“this总是指向函数的调用者的”
console.log(this)//window
var fun=function(){
console.log(this);
};
fun();//打印window
// 等价于如下的调用方式
this();
window();
// 匿名函数 可以理解成window创建某个变量保存这个匿名函数,然后删除了这个变量
(function(){
console.log(this);
})();// 打印window
这里window调用函数,函数里的this指向window。
对象也是一样
数组、函数、自定义对象、以及dom对象都是对象,this是指向这个对象的。其他对象的例子不一一
举了,这里再举一下dom对象的例子
var btn=document.getElementById('btn');
// 点击按钮时弹出myBtn
btn.onclick=function(){
alert(this.name);
}
我们点击按钮,触发点击事件,这个onclick函数是btn调用,this指向btn没问题。
再看一下构造函数中的this
var Person=function(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
alert(this.name);
}
};
Persontotype.sayAge=function(){
alert(this.age);
};
var p=new Person("切图网",18);
p.sayName();//弹出 切图网
p.sayAge();//弹出 18
new一个函数时,函数也会执行的。可以看做p调用了Person这个构造函数,里面的this.name、this.age 、this.sayName中的this都是指向p的;
p.sayName()、p.sayAge()是p调用函数,这两个里面的this当然指向对象p了。
从上面的代码中,基本可以总结了,this总是函数调用者的。那么再看看如果函数没用调用者呢?
var o={
fn : function(){
console.log(this); // 标记1
var a=function(){
console.log(this);// 标记2
};
a();
}
};
o.fn();//标记1 打印对象o,标记2 打印window
你说上述代码中执行语句a(),a调用者是谁?是对象o吗,是window吗,都不是,如不信,你改成o.a()或者window.a(),浏览器肯定会报错的。
a是没有调用者,你只能说在fn的作用域中,a被调用了。而此时函数没有调用者时,里面的this指向window的。
this的指向的问题我是说明白了,再总结一下:如果函数有调用者时,this指向调用者,如果没有调用者,this指向window的。上述代码中,都没有涉及闭包问题。
这里举个例子
var b=function(){
console.log(this); // 标记1
return function(){
console.log(this);// 标记2
}
}
var c=b();//标记1 打印window
c();// 标记2 打印window c是被window调用的,打印window没问题
var a={
fn : c
}
a.fn();// 标记2 打印对象a,没问题
//如下方式呢
var e={
fn : function(){
c();
}
}
e.fn();// 标记2 打印window 因为c()是没有调用者的
可以看出,不管是不是闭包,还是那句话,能找到调用者就指向调用者,找不到就指向window。
《js高级程序设计》里面是这么说的,匿名函数的执行环境具有全局性的。我是这么看的,找不到调用者时,执行函数就具有全局性。跟你这个函数是不是有名匿名没有任何关系。
第二部分
如何保证this,是我们想要的this呢
有几个方法,这里大致说下
1.不把某个函数名,直接赋值某个变量,而是外面加一层函数,然后再调用,换句话说,给你机会来让这个函数执行时指定调用者或者不设置调用者,直接上代码
var name="切图网";
var btn=document.getElementById('btn');
var f=function(){
alert(this.name);
};
btn.onclick=f;
上述代码,点击按钮时弹出的是btn对象的name:mybtn。如果想要弹出游戏账号拍卖window中的name,可以改成如下代码
var name="切图网";
var btn=document.getElementById('btn');
var f=function(){
alert(this.name);
};
btn.onclick=function(){
f();
};
2.设置变量缓存this,如
var name="yyy";
var Person=function(name){
this.name=name;
(function(){alert(this.name)})();
}
new Person("xxx");
上代码运行会弹出window的name:yyy,如果想弹出对象的name:xxx,可以改成
var name="yyy";
var Person=function(name){
this.name=name;
var self=this;
(function(){alert(self.name)})();
}
new Person("xxx");
3.应用call、apply方法来指定this
var name="yyy";
var o={name: "xxx"};
var fun=function(x,y){
alert(this.name)
return x+y;
}
fun(3,4);
// 等于于如下的调用
fun.call(window,3,4);
fun.call(this,3,4);
fun.apply(window,[3,4]);
fun.apply(this,[3,4]);
上述代码运行弹出window的name:yyy,如果要弹出o的name,可以改成
var name="yyy";
var o={name: "xxx"};
var fun=function(x,y){
alert(this.name)
return x+y;
}
fun.call(o,3,4);
fun.apply(o,[3,4]);
4.应用框架函数,一般的框架都有给函数绑定上下文的函数(基本也都是应用apply和call),比如jquery的$xy().这里写个函数
function proxy(fn,context){
return function(){
fn.apply(context,arguments);
}
}
var o={name: "xxx"};
var fun=function(x,y){
alert(this.name)
return x+y;
}
var fun2=proxy(fun,o);
fun2(3,4);
文/丁向明
做一个有博客的web前端自媒体人,专注web前端开发,关注用户体验,加我qq/微信交流:6135833
dingxiangming