相信大家在學(xué)習(xí)JavaScript的時(shí)候,this關(guān)鍵字總是會(huì)讓大家感到很困惑,下面就來(lái)給大家詳細(xì)的介紹有關(guān)this的一些知識(shí)點(diǎn)。
this
在JavaScript中this總是指向一個(gè)對(duì)象,而具體指向的那個(gè)對(duì)象是在運(yùn)行代碼時(shí)基于函數(shù)的執(zhí)行環(huán)境動(dòng)態(tài)綁定的,而不是函數(shù)在聲明時(shí)的環(huán)境。
this的指向問(wèn)題
在JavaScript中出去特殊的with和eval之外,具體到實(shí)際運(yùn)用中一般包括以下四種情況:
1、作為對(duì)象的方法調(diào)用;
2、作為普通函數(shù)調(diào)用;
3、作為構(gòu)造函數(shù)調(diào)用;
4、Function.prototype.call和Function.prototype.apply的調(diào)用。
1. 作為對(duì)象的方法調(diào)用
當(dāng)一個(gè)函數(shù)當(dāng)作一個(gè)對(duì)象的方法調(diào)用時(shí),this指向該對(duì)象
var obj = {
name : 'iFuhang',
getName : function(){
console.log(this); // obj
console.log(this.name); // iFuhang
}
}
obj.getName();
2. 作為普通函數(shù)調(diào)用
當(dāng)一個(gè)函數(shù)不被當(dāng)作對(duì)象的方法調(diào)用時(shí),而是當(dāng)作普通的函數(shù)被調(diào)用時(shí),此時(shí)的this指向全局對(duì)象,在瀏覽器中也就是指的window對(duì)象。
window.name = 'smile';
var getName = function(){
var name = 'iFuhang';
return this.name;
}
console.log(getName()); // smile
還有一種情況
window.name = 'smile';
var myObject= {
name : 'iFuhang',
getName : function(){
return this.name;
}
}
var getName = myObject.getName;
console.log(getName());
這種情況下,雖然getName是myObject.getName()賦值給它的,但是此時(shí)的getName仍然是當(dāng)作普通函數(shù)來(lái)調(diào)用的,所以此時(shí)this還是指向window的。在這種情況下,有的時(shí)候會(huì)帶給我們一些不必要的麻煩,比如下面的示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script>
document.getElementById('app').onclick = function(){
console.log(this.id); // app
var getId = function(){
console.log(this.id); // undefined
}
getId();
}
</script>
</body>
</html>
大家可以看到,最后getId()函數(shù)中打印出來(lái)的是undefined,這個(gè)結(jié)果我想大家并不意外了,是的,getId()仍然是作為一個(gè)普通函數(shù)的調(diào)用。其是我們最后想要的結(jié)果是打印出div的id,那么要怎樣解決呢?
其實(shí)很簡(jiǎn)單,我們可以在執(zhí)行g(shù)etId()函數(shù)之前,先將div的引用保存起來(lái),然后調(diào)用它就可以了。還是來(lái)看代碼吧!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script>
document.getElementById('app').onclick = function(){
var self = this; // 提前將this保存起來(lái),這里的this是指向div的
console.log(this.id); // app
var getId = function(){
console.log(this.id); // app
}
getId();
}
</script>
</body>
</html>
好了,問(wèn)題解決了,是不是很簡(jiǎn)單。其實(shí)這里可以還可以通過(guò)call方法來(lái)改變this的指向,關(guān)于call方法的應(yīng)用我們?cè)诤竺鎸?huì)講述。
需要注意的是在ECMAScript5中的嚴(yán)格模式下,普通函數(shù)的this指向并不是window了。
function getThis(){
"use strict";
console.log(this); // undefined
}
getThis();
3. 作為構(gòu)造函數(shù)的調(diào)用
構(gòu)造函數(shù)看起來(lái)其實(shí)和普通函數(shù)沒(méi)啥區(qū)別,只是在調(diào)用方式上不一樣,當(dāng)使用new運(yùn)算符調(diào)用函數(shù)時(shí),此時(shí)此函數(shù)就稱為構(gòu)造函數(shù),并且總是返回一個(gè)對(duì)象,通常情況下,構(gòu)造函數(shù)中的this指向返回的這個(gè)對(duì)象。
var MyName = function(){
this.name = 'iFuhang';
}
var obj= new MyName();
console.log(obj.name); // iFuhang
使用構(gòu)造函數(shù)的時(shí)候,需要注意一個(gè)問(wèn)題,如果構(gòu)造函數(shù)顯式的返回了一個(gè)對(duì)象,此時(shí)的this指向的是返回的這個(gè)對(duì)象了。如果返回的不是一個(gè)對(duì)象,就不會(huì)存在下面這種情況了。
var MyName = function(){
this.name = 'iFuhang';
return {
name : 'smile';
}
}
var obj= new MyName();
console.log(obj.name); // smile
4. Function.prototype.call和Function.prototype.apply的調(diào)用
通過(guò)Function.prototype.call和Function.prototype.apply這兩種方法可以動(dòng)態(tài)的修改this的指向。
var obj1 = {
name : 'iFuhang',
getName : function(){
return this.name;
}
}
var obj2 = {
name : 'smile'
}
console.log(obj1.getName()); // iFuhang
console.log(obj1.getName.call(obj2)); // smile
點(diǎn)擊了解更多關(guān)于call和apply的知識(shí)點(diǎn)。
以上就是關(guān)于this的四種指向問(wèn)題了,下面講解一下this丟失的問(wèn)題。
this丟失
在開(kāi)發(fā)中經(jīng)常會(huì)遇到this丟失的問(wèn)題,就比如下面這個(gè)示例:
var obj = {
name : 'iFuhang',
getName : function(){
return this.name;
}
}
console.log(obj.getName()); // iFuhang
var getName = obj.getName();
console.log(getName()); //undefined
出現(xiàn)這種情況的原因其實(shí)之前講到過(guò)的,在調(diào)用getName時(shí),其實(shí)時(shí)普通函數(shù)的調(diào)用方式,所以此時(shí)的this指向window,但是window中并沒(méi)有name這是屬性,所以會(huì)返回undefined,如果在嚴(yán)格模式下,則會(huì)報(bào)錯(cuò)。再來(lái)看看下面這個(gè)例子:
一般我們用JavaScript獲取DOM節(jié)點(diǎn)時(shí)都會(huì)用到document.getElementById等之類的方法,但是為了使用簡(jiǎn)單我們一般會(huì)封裝一個(gè)函數(shù)來(lái)簡(jiǎn)化操。
var getId = function(id){
return document.getElementById(id);
}
getId(id);
還有人思考為什么不能使用下面的方法呢?不是顯得更加簡(jiǎn)單些嗎?
var getId = document.getElementById;
getId(id);
此段代碼運(yùn)行時(shí)會(huì)拋出一個(gè)錯(cuò)誤。(Uncaught TypeError: Illegal invocation)
這是因?yàn)樵谠S多瀏覽器的引擎中實(shí)現(xiàn)document.getElementById時(shí)都會(huì)用到this,而且this都被期望指向的是document,在getElementById被document調(diào)用的時(shí)候,this確實(shí)時(shí)指向document的,但是當(dāng)用getId來(lái)引用document.getElementById調(diào)用時(shí),此時(shí)的getId是作為普通函數(shù)調(diào)用的,this會(huì)指向window,而不再是document。此時(shí)我們可以通過(guò)apply來(lái)改變this指向document。
document.getElementById = (function(func) {
return function(){
return func.apply(document, arguments);
}
})(document.getElementById );
var getId = document.getElementById;
getId(id);
這段代碼中最后執(zhí)行的其實(shí) func.apply(document, arguments)這個(gè)函數(shù),我們將document.getElementById作為參數(shù)傳遞,也就是func,最后在調(diào)用func時(shí),利用apply將this指向了document,所以最后可以輸出正確結(jié)果了。