이 기사는 이 문제에 대해서만 논의합니다. 독자가 JavaScript에서 이 질문이 무엇인지 정확하게 답할 수 있다면, 저자로서 저는 그러한 기사를 작성하는 데 많은 노력을 기울일 가치가 있다고 느낄 것입니다.
한 문장을 기억해야 합니다. 이것은 항상 함수가 실행되는 개체를 가리킵니다! 함수가 생성된 개체가 아니라 즉, 전화하는 사람은 누구를 가리킨다. 기억하세요…
이 기사에서는 세 가지 상황에서 이 개체가 어디에 있는지 분석합니다.
1. 이
일반적인 기능에서는
이것이 어디에 있든, 기능이 실행될 때 위치를 찾는 것이 최우선입니다.
var name="全局"; function getName(){ var name="局部"; return this.name; }; alert(getName());
전역 환경의 getName 함수에 이것이 나타날 때 getName 함수의 런타임 위치는
입니다.
alert(getName());
분명히 getName 함수가 위치한 객체는 전역 객체, 즉 윈도우이므로 이것의 홈은 윈도우에 있어야 합니다. 이때 this는 window 객체를 가리키므로 getName이 반환한 this.name은 실제로는 window.name이므로 경고가 "global"로 나옵니다!
그렇다면 이것이 지구환경의 기능으로 나타나지 않고 지역환경의 기능으로 나타난다면 어디로 떨어질 것인가?
var name="全局"; var xpg={ name:"局部", getName:function(){ return this.name; } }; alert(xpg.getName());
이것이 위치한 getName 함수는 글로벌 환경이 아닌 xpg 환경에 있습니다. 이것이 어디에 있든 간에, 기능이 실행될 때 위치를 찾아야 합니다. 이때 getName 함수가 실행 중일 때의 위치
alert(xpg.getName());
분명히 getName 함수가 위치한 개체는 xpg이므로 이것의 홈은 xpg에 있어야 합니다. 즉, xpg 개체를 가리킵니다. 그런 다음 getName이 반환한 this.name은 실제로 xpg.name입니다. 그래서 경고는 "부분"으로 나옵니다!
통합할 예를 들어보세요.
var someone = { name: "Bob", showName: function(){ alert(this.name); } }; var other = { name: "Tom", showName: someone.showName } other.showName(); //Tom
this 키워드는 someone.showName에 선언되어 있지만 실행 시에는 other.showName이므로 이는 other인 other.showName 함수의 현재 개체를 가리키므로 최종 경고는 other.name입니다.
2. 마무리
탕자와 악당의 종결이 섞여 있는데, 이는 결코 평화가 없을 것임을 보여줍니다!var name="全局"; var xpg={ name:"局部", getName:function(){ return function(){ return this.name; }; } }; alert(xpg.getName()());
이것이 어디에 있든 간에, 기능이 실행될 때 위치를 찾아야 합니다. 이때 getName 함수의 런타임 위치를 기준으로 판단할 수 없고 익명 함수의 런타임 위치를 기준으로 판단합니다.
function (){ return this.name; };
그럼 클로저에서 XPG로 이걸 어떻게 만들까요? —캐시
var name="全局"; var xpg={ name:"局部", getName:function(){ var that=this; return function(){ return that.name; }; } }; alert(xpg.getName()());
에서 실행됩니다.
alert(xpg.getName());
3. new 키워드는 새로운 객체를 생성합니다
생성자에서 new 키워드 뒤의 이 내용은 생성자로 생성된 새 개체를 가리킵니다.
function Person(__name){ this.name = __name; //这个this指向用该构造函数构造的新对象,这个例子是Bob对象 } Person.prototype.show = function(){ alert(this.name); //this 指向Person,this.name = Person.name; } var Bob = new Person("Bob"); Bob.show(); //Bob
4. 전화로 신청하세요
JavaScript에서 이를 관리할 수 있는 유일한 것은 호출 및 적용입니다.
부르고 신청하는 것은 이 사람의 부모와 같아서 이것을 살게 하는 곳이면 어디든지 살 것이며 순종해야 합니다! 매개변수가 없으면 현재 객체는 window
var name="全局"; var xpg={ name:"局部" }; function getName(){ alert(this.name); } getName(xpg); getName.call(xpg); getName.call();
getName(xpg);
그럼 그들의 명령을 들어야 하기 때문에 전화해서 신청할 시간입니다!
getName.call(xpg);
그 중 call은 this의 홈이 xpg 개체임을 지정합니다. 이는 강제로 xpg에서만 해결되므로 this는 이때 xpg 개체를 가리키며 this.name은 실제로 xpg.name이므로 경고가 나옵니다. "로컬"로!
5. 평가판에서는 이
eval 함수의 경우 실행 시 현재 개체가 지정되지 않은 것처럼 보이지만 실제로는 함수 실행 시 범위가 현재 범위이므로 창을 가리키지 않습니다. 해당 줄 안의 코드를 채우는 것입니다. 다음 예에서는 이 문제를 보여줍니다.
var name = "window"; var Bob = { name: "Bob", showName: function(){ eval("alert(this.name)"); } }; Bob.showName(); //Bob
6、没有明确的当前对象时的this
当没有明确的执行时的当前对象时,this指向全局对象window。
例如对于全局变量引用的函数上我们有:
var name = "Tom"; var Bob = { name: "Bob", show: function(){ alert(this.name); } } var show = Bob.show; show(); //Tom
你可能也能理解成show是window对象下的方法,所以执行时的当前对象时window。但局部变量引用的函数上,却无法这么解释:
var name = "window"; var Bob = { name: "Bob", showName: function(){ alert(this.name); } }; var Tom = { name: "Tom", showName: function(){ var fun = Bob.showName; fun(); } }; Tom.showName(); //window
在浏览器中setTimeout、setInterval和匿名函数执行时的当前对象是全局对象window,这条我们可以看成是上一条的一个特殊情况。
var name = "Bob"; var nameObj ={ name : "Tom", showName : function(){ alert(this.name); }, waitShowName : function(){ setTimeout(this.showName, 1000); } }; nameObj.waitShowName();
所以在运行this.showName的时候,this指向了window,所以最后显示了window.name。
7、dom事件中的this
(1)你可以直接在dom元素中使用
<input id="btnTest" type="button" value="提交" onclick="alert(this.value))" />
分析:对于dom元素的一个onclick(或其他如onblur等)属性,它为所属的html元素所拥有,直接在它触发的函数里写this,this应该指向该html元素。
(2)给dom元素注册js函数
a、不正确的方式
<script type="text/javascript"> function thisTest(){ alert(this.value); // 弹出undefined, this在这里指向?? } </script> <input id="btnTest" type="button" value="提交" onclick="thisTest()" />
分析:onclick事件直接调用thisTest函数,程序就会弹出undefined。因为thisTest函数是在window对象中定义的,
所以thisTest的拥有者(作用域)是window,thisTest的this也是window。而window是没有value属性的,所以就报错了。
b、正确的方式
<input id="btnTest" type="button" value="提交" /> <script type="text/javascript"> function thisTest(){ alert(this.value); } document.getElementById("btnTest").onclick=thisTest; //给button的onclick事件注册一个函数 </script>
分析:在前面的示例中,thisTest函数定义在全局作用域(这里就是window对象),所以this指代的是当前的window对象。而通过document.getElementById(“btnTest”).onclick=thisTest;这样的形式,其实是将btnTest的onclick属性设置为thisTest函数的一个副本,在btnTest的onclick属性的函数作用域内,this归btnTest所有,this也就指向了btnTest。其实如果有多个dom元素要注册该事件,我们可以利用不同的dom元素id,用下面的方式实现:
document.getElementById("domID").onclick=thisTest; //给button的onclick事件注册一个函数。
因为多个不同的HTML元素虽然创建了不同的函数副本,但每个副本的拥有者都是相对应的HTML元素,各自的this也都指向它们的拥有者,不会造成混乱。
为了验证上述说法,我们改进一下代码,让button直接弹出它们对应的触发函数:
<input id="btnTest1" type="button" value="提交1" onclick="thisTest()" /> <input id="btnTest2" type="button" value="提交2" /> <script type="text/javascript"> function thisTest(){ this.value="提交中"; } var btn=document.getElementById("btnTest1"); alert(btn.onclick); //第一个按钮函数 var btnOther=document.getElementById("btnTest2"); btnOther.onclick=thisTest; alert(btnOther.onclick); //第二个按钮函数 </script> 其弹出的结果是: //第一个按钮 function onclick(){ thisTest() } //第二个按钮 function thisTest(){ this.value="提交中"; }
从上面的结果你一定理解的更透彻了。
By the way,每新建一个函数的副本,程序就会为这个函数副本分配一定的内存。而实际应用中,大多数函数并不一定会被调用,于是这部分内存就被白白浪费了。所以我们通常都这么写:
<input id="btnTest1" type="button" value="提交1" onclick="thisTest(this)" /> <input id="btnTest2" type="button" value="提交2" onclick="thisTest(this)" /> <input id="btnTest3" type="button" value="提交3" onclick="thisTest(this)" /> <input id="btnTest4" type="button" value="提交4" onclick="thisTest(this)" /> <script type="text/javascript"> function thisTest(obj){ alert(obj.value); } </script>
这是因为我们使用了函数引用的方式,程序就只会给函数的本体分配内存,而引用只分配指针。这样写一个函数,调用的地方给它分配一个(指针)引用,这样效率就高很多。当然,如果你觉得这样注册事件不能兼容多种浏览器,可以写下面的注册事件的通用脚本:
//js事件 添加 EventUtil.addEvent(dom元素,事件名称,事件触发的函数名) 移除EventUtil.removeEvent(dom元素,事件名称,事件触发的函数名) var EventUtil = new eventManager(); //js事件通用管理器 dom元素 添加或者移除事件 function eventManager() { //添加事件 //oDomElement:dom元素,如按钮,文本,document等; ****** oEventType:事件名称(如:click,如果是ie浏览器,自动将click转换为onclick);****** oFunc:事件触发的函数名 this.addEvent = function(oDomElement, oEventType, oFunc) { //ie if (oDomElement.attachEvent) { oDomElement.attachEvent("on" + oEventType, oFunc); } //ff,opera,safari等 else if (oDomElement.addEventListener) { oDomElement.addEventListener(oEventType, oFunc, false); } //其他 else { oDomElement["on" + oEventType] = oFunc; } } this.removeEvent = function(oDomElement, oEventType, oFunc) { //ie if (oDomElement.detachEvent) { oDomElement.detachEvent("on" + oEventType, oFunc); } //ff,opera,safari等 else if (oDomElement.removeEventListener) { oDomElement.removeEventListener(oEventType, oFunc, false); } //其他 else { oDomElement["on" + oEventType] = null; } } }
正像注释写的那样,要注册dom元素事件,用EventUtil.addEvent(dom元素,事件名称,事件触发的函数名)即可, 移除时可以这样写:EventUtil.removeEvent(dom元素,事件名称,事件触发的函数名),这是题外话,不说了。
以上就是本文的全部内容,希望通过这篇文章大家更加了解javascript的this关键字,大家共同进步。