1、this是什么?
this是JavaScript语言中定义的众多关键字之一,它的特殊在于它自动定义于每一个函数域内。
2、this的作用?
看个例子:
function identify() {
return this.name.toUpperCase();
}
function sayHello() {
var greeting = "Hello, I'm " + identify.call( this );
console.log( greeting );
}
var person1= {
name: "Kyle"
};
var person2= {
name: "Reader"
};
identify.call( person1); // KYLE
identify.call( person2); // READER
sayHello.call( person1); // Hello, I'm KYLE
sayHello.call( person2); // Hello, I'm READER
这段代码很简单,我们定义了两个函数,分别为identify和sayHello。并且在不同的对象环境下执行了它们,达到了复用的效果,而不用为了在不同的对象环境下执行而必须针对不同的对象环境写对应的函数了。简言之,this给函数带来了复用。
如果不用this要想达到同样的效果,应该这样写:
function identify(context) {
return context.name.toUpperCase();
}
function sayHello(context) {
var greeting = "Hello, I'm " + identify( context);
console.log( greeting );
}
var person1= {
name: "Kyle"
};
var person2= {
name: "Reader"
};
identify( person1); // KYLE
identify( person2); // READER
sayHello( person1); // Hello, I'm KYLE
sayHello( person2); // Hello, I'm READER
这种解决方法的确也达到了类似的效果。但是,随着代码的增加,函数嵌套、各级调用等变得越来越复杂,那么传递一个对象的引用将变得越来越不明智,它会把你的代码弄得非常乱,甚至你自己都无法理解清楚。而this机制提供了一个更加优雅而灵便的方案,传递一个隐式的对象引用让代码变得更加简洁和复用。
3、关于this的几个误区:
3.1误解一:this指向function本身:
我们都知道,在函数里引用函数可以达到递归和给函数属性赋值的效果。而这在很多应用场景下显得非常有用。所以,很多人都误以为this就是指引function本身。例如:
function fn(num) {
console.log( "fn: " + num );
// count用于记录fn的被调用次数
this.count++;
}
fn.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
fn( i );
}
}
// fn: 6
// fn: 7
// fn: 8
// fn: 9
console.log(count);//NaN
console.log(window.count);//NaN
console.log( fn.count ); // 0 -- 耶?咋不是4捏?
上面我们想要记录fn被调用的次数,可是明显fn被调用了四次但count仍然为0。why?
简单解释下,fn里第4行的自增隐式的创建了一个全局变量count,由于初始值为undefined,所以每一次自增其实依然不是一个数字,在全局环境下打印count(window.count)输出的应该是NaN。而第6行定义的函数熟悉变量count依然没变,还是0。
既然this不是引用function,那么我要实现递归函数,该咋引用呢?
这里简单回答下介个问题,两种方法:①函数体内用函数名来引用函数本身②函数体内使用arguments.callee来引用函数(不推荐)。那么既然第二种方法不推荐,匿名函数咋引用呢?用第一种,并且给匿名函数一个函数名即可(推荐)。
3.2 误解二:this引用的是function的词法作用域
首先,澄清一下,this并没有引用function的词法作用域。的确JS的引擎内对词法作用域的实现的确像是一个对象,拥有属性和函数,但是这仅仅是JS引擎的一种实现,对代码来说是不可见的,也就是说词法作用域“对象”在JS代码中取不到。
看个错误的例子:
function fn1() {
var a = 2;
this.fn2();//以为this引用的是fn1的词法作用域
}
function fn2() {
console.log( this.a );
}
fn1(); //undefined
4、this到底跟什么有关?
this跟函数在哪里定义没有半毛钱关系,函数在哪里调用才决定了this到底引用的是啥。也就是说this跟函数的定义没关系,跟函数的执行有大大的关系。所以,记住,“函数在哪里调用才决定了this到底引用的是啥”。
5、this机制的四种规则
this到底绑定或者引用的是哪个对象环境决定于函数被调用的地方。而函数的调用有不同的方式,在不同的方式中调用决定this引用的是哪个对象是由四种规则确定的。我们一个个来看。
5.1 默认绑定全局变量
这条规则是最常见的,也是默认的。当函数被单独定义和调用的时候,应用的规则就是绑定全局变量。如下:
function fn() {
console.log( this.a );
}
var a = 2;
fn(); // 2 -- fn单独调用,this引用window
5.2 隐式绑定
隐式调用的意思是,函数调用时拥有一个上下文对象,就好像这个函数是属于该对象的一样。
function fn() {
console.log( this.a );
}
var obj = {
a: 2,
fn: fn
};
obj.fn(); // 2 -- this引用obj。
需要说明的一点是,最后一个调用该函数的对象是传到函数的上下文对象(绕懵了)。如:
function fn() {
console.log( this.a );
}
var obj2 = {
a: 42,
fn: fn
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.fn(); // 42 -- this引用的是obj2.
还有一点要说明的是,失去隐式绑定的情况,如下:
function fn() {
console.log( this.a );
}
var obj = {
a: 2,
fn: fn
};
var bar = obj.fn; // 函数引用传递
var a = "全局"; // 定义全局变量
bar(); // "全局"
5.3 显示绑定
学过bind()\apply()\call()函数的都应该知道,它接收的第一个参数即是上下文对象并将其赋给this。看下面的例子:
function fn() {
console.log( this.a );
}
var obj = {
a: 2
};
fn.call( obj ); // 2
如果我们传递第一个值为简单值,那么后台会自动转换为对应的封装对象。如果传递为null,那么结果就是在绑定默认全局变量,如:
function fn() {
console.log( this.a );
}
var obj = {
a: 2
};
var a = 10;
fn.call( null); // 10
5.4 new新对象绑定
如果是一个构造函数,那么用new来调用,那么绑定的将是新创建的对象。如:
function fn(a) {
this.a = a;
}
var bar = new fn( 2 );
console.log( bar.a );//2
注意,一般构造函数名首字母大写,这里没有大写的原因是想提醒读者,构造函数也是一般的函数而已。
几个带this的小实例:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>无标题文档</title>
<script>
// this: 指的是调用 当前 方法(函数)的那个对象
// alert( this ); // object window
// 相当于window.alert( this );
function fn1(){
// this this=>window
}
// fn1(); this => window
// oDiv.onclick = fn1; this => oDiv
/*
oDiv.onclick = function (){
fn1(); //fn1() 里的this => window
};
<div onclick="this fn1();"></div> //onclick=""里面的this指向div,fn1()里的this指的是 window
*/
</script>
</head>
<body>
<input id="btn1" type="button" value="按钮" />
<input id="btn2" type="button" onclick=" fn1(); " value="按钮2" />
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>button1</button>
<button>button2</button>
<button>button3</button>
<!--情况一-->
<!--<script>-->
<!--var btn=document.getElementsByTagName('button');-->
<!--var len=btn.length;-->
<!--for(var i=0;i<len;i++){-->
<!--btn[i].onclick=function(){-->
<!--alert(this); //指向btn[i]-->
<!--this.style.background='yellow';//被点击的按钮背景颜色变成黄色-->
<!---->
<!--fn1();//fn1()中的this指向window-->
<!--}-->
<!--}-->
<!--function fn1(){-->
<!--alert(this);//指向window-->
<!--}-->
<!--</script>-->
<!--怎么让fn1()里面的this指向btn[i]呢?-->
<!--<script>-->
<!--var btn=document.getElementsByTagName('button');-->
<!--var len=btn.length;-->
<!--for(var i=0;i<len;i++){-->
<!--btn[i].onclick=function(){-->
<!--that=this;-->
<!--fn1();//fn1()中的this指向window-->
<!--}-->
<!--}-->
<!--function fn1(){-->
<!--alert(that);//that指向btn[i]-->
<!--that.style.background='yellow';//被点击的按钮背景颜色变成黄色-->
<!--}-->
<!--</script>-->
<!--情况二-->
<script>
var btn=document.getElementsByTagName('button');
var len=btn.length;
for(var i=0;i<len;i++){
btn[i].onclick=fn1;
}
function fn1(){
alert(this);
this.style.background='yellow';//被点击的按钮背景颜色变成黄色
}
</script>
</body>
</html>