相信大家在学习JavaScript的时候,this关键字总是会让大家感到很困惑,下面就来给大家详细的介绍有关this的一些知识点。
this
在JavaScript中this总是指向一个对象,而具体指向的那个对象是在运行代码时基于函数的执行环境动态绑定的,而不是函数在声明时的环境。
this的指向问题
在JavaScript中出去特殊的with和eval之外,具体到实际运用中一般包括以下四种情况:
1、作为对象的方法调用;
2、作为普通函数调用;
3、作为构造函数调用;
4、Function.prototype.call和Function.prototype.apply的调用。
1. 作为对象的方法调用
当一个函数当作一个对象的方法调用时,this指向该对象
var obj = {
name : 'iFuhang',
getName : function(){
console.log(this); // obj
console.log(this.name); // iFuhang
}
}
obj.getName();
2. 作为普通函数调用
当一个函数不被当作对象的方法调用时,而是当作普通的函数被调用时,此时的this指向全局对象,在浏览器中也就是指的window对象。
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()赋值给它的,但是此时的getName仍然是当作普通函数来调用的,所以此时this还是指向window的。在这种情况下,有的时候会带给我们一些不必要的麻烦,比如下面的示例:
<!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()函数中打印出来的是undefined,这个结果我想大家并不意外了,是的,getId()仍然是作为一个普通函数的调用。其是我们最后想要的结果是打印出div的id,那么要怎样解决呢?
其实很简单,我们可以在执行getId()函数之前,先将div的引用保存起来,然后调用它就可以了。还是来看代码吧!
<!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保存起来,这里的this是指向div的
console.log(this.id); // app
var getId = function(){
console.log(this.id); // app
}
getId();
}
</script>
</body>
</html>
好了,问题解决了,是不是很简单。其实这里可以还可以通过call方法来改变this的指向,关于call方法的应用我们在后面将会讲述。
需要注意的是在ECMAScript5中的严格模式下,普通函数的this指向并不是window了。
function getThis(){
"use strict";
console.log(this); // undefined
}
getThis();
3. 作为构造函数的调用
构造函数看起来其实和普通函数没啥区别,只是在调用方式上不一样,当使用new运算符调用函数时,此时此函数就称为构造函数,并且总是返回一个对象,通常情况下,构造函数中的this指向返回的这个对象。
var MyName = function(){
this.name = 'iFuhang';
}
var obj= new MyName();
console.log(obj.name); // iFuhang
使用构造函数的时候,需要注意一个问题,如果构造函数显式的返回了一个对象,此时的this指向的是返回的这个对象了。如果返回的不是一个对象,就不会存在下面这种情况了。
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的调用
通过Function.prototype.call和Function.prototype.apply这两种方法可以动态的修改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
点击了解更多关于call和apply的知识点。
以上就是关于this的四种指向问题了,下面讲解一下this丢失的问题。
this丢失
在开发中经常会遇到this丢失的问题,就比如下面这个示例:
var obj = {
name : 'iFuhang',
getName : function(){
return this.name;
}
}
console.log(obj.getName()); // iFuhang
var getName = obj.getName();
console.log(getName()); //undefined
出现这种情况的原因其实之前讲到过的,在调用getName时,其实时普通函数的调用方式,所以此时的this指向window,但是window中并没有name这是属性,所以会返回undefined,如果在严格模式下,则会报错。再来看看下面这个例子:
一般我们用JavaScript获取DOM节点时都会用到document.getElementById等之类的方法,但是为了使用简单我们一般会封装一个函数来简化操。
var getId = function(id){
return document.getElementById(id);
}
getId(id);
还有人思考为什么不能使用下面的方法呢?不是显得更加简单些吗?
var getId = document.getElementById;
getId(id);
此段代码运行时会抛出一个错误。(Uncaught TypeError: Illegal invocation)
这是因为在许多浏览器的引擎中实现document.getElementById时都会用到this,而且this都被期望指向的是document,在getElementById被document调用的时候,this确实时指向document的,但是当用getId来引用document.getElementById调用时,此时的getId是作为普通函数调用的,this会指向window,而不再是document。此时我们可以通过apply来改变this指向document。
document.getElementById = (function(func) {
return function(){
return func.apply(document, arguments);
}
})(document.getElementById );
var getId = document.getElementById;
getId(id);
这段代码中最后执行的其实 func.apply(document, arguments)这个函数,我们将document.getElementById作为参数传递,也就是func,最后在调用func时,利用apply将this指向了document,所以最后可以输出正确结果了。