Fork me on GitHub
代码改变世界

浅谈js之this对象

2015-05-05 14:34  todayhappy  阅读(1611)  评论(0编辑  收藏  举报

  this对象真的是有时让我们头疼,我们真是分不清它到底指向哪个作用域,它有时的指向真的是不可思议,它本应该这样指向却那样指向。

虽然有这么句话,this是调用执行当前代码的对象的所有者,也就是谁调用它,它就指向谁,那问题来,如果我们分不清谁调用的它,那怎么办呢?

理论部分

0X00

js中的圣经红宝书和犀牛书对这部分说的很少,但是很精辟;要真正的深刻理解书上说的,那么你对this就一点也不难,但是像我这种菜鸟怎么可能理解呢???

红宝书

this引用的是函数据以执行的环境对象,this对象是在运行时基于函数执行环境绑定的,在全局中,this等于window,当函数被作为木个对象的方法调用时,this等于那个对象,不过,由于匿名函数的执行环境具有全局性,因此,this对象通常指向window,还有就是每个函数在调用的时候都会自动获取两个特殊变量:thisarguments,放在活动对象中,内部在要使用这两个变量时,只是会在本函数的活动对象中搜索,永远不会沿着作用域链访问外面的活动对象。

犀牛书

this对象没有作用域的限制,嵌套的函数不会从调用它的函数中继承this,如果嵌套函数作为方法调用,其this值指向调用它的对象,如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式)就是undefined(严格模式);很多人误以为调用嵌套函数时this会指向调用外层函数的上下文。

这就是解释的。当你真正理解了this后看这些话,你感觉他们说的this判断就是这么回事很有感觉,但是对于我来说,读过这些话没太有感觉,不理解。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

声明以下我说的都是在我们判断this对象指向时可以这么想,其他时候你可别这样想呀!有小部分是不太适合的,那样说的话;

      我说的都是自己的理解,如果你感觉我说的不对,欢迎指正!

      我说的这些例子中有很多是类似的,只是为了让我们加深理解!

0X01

this往大了说无非就是全局中的和函数(方法)中的,只是这函数(方法)中的太过于复杂;

其实可以大致划分为五类:

  1. 全局下中【this】
  2. 函数中调用【foo(foo2(this))】(这里是指在函数中执行的函数即函数嵌套函数)
  3. 方法调用【obj.foo(this)】(这是是指对象调用函数时)
  4. 构造函数中【new Foo(this)】
  5. apply和call中【foo.apply(obj)】

有时候也不好说是在五种的哪一种;只是大致这样吧;                     参考:http://bonsaiden.github.io/JavaScript-Garden/zh/#function.this

实战部分

0x00:在全局中的情况

全局中:

0x01:

var a = 1;
console.log(this);//window
console.log(this.a);//1

这就是全局中的this它是指向window对象的,这种情况很简单

0x01:函数调用中的

如果一个函数的调用者是一个函数,那么那个被调用的函数中的this是指向window的

我们就是在对象的方法里调用函数,有时让我们分不清,其实在this眼里,所有的函数都是跟普通函数差不多;

在javascript语言精粹上说,在此模式中this被绑定到全局对象,这是语言设计上的一个错误。

001:setTimeout和setInterval中的this

这个是要单独拿出来的,他们其实都是来自window的方法,这两个函数不管是在哪里调用,;其执行上下文环境是window对象下的环境;

var num = 1;
var obj = {
    num:2,
    show:function() {
        console.log(this.num);
    },
    showNum:function() {
        setTimeout(this.show,1000);
    }
};
obj.showNum();//1

参数中的this是传递引用的过来的,是和showNum中的this是一样的,其实这个this不是重点,在这里,show方法里面的this才是重点

我的理解是当程序执行到setTimeout的时候通过this.show获取对obj对象中show函数引用,show函数里面的this应该指向谁呢?(你不要误认为这个方法定义在obj中就和obj有关系(确实有关系,但是在this对象看来:函数定义在哪里都一样,在它看来你们都是一个个孤立的函数,就看你是在哪里调用的),你就记住这句话:在this对象看来:函数不管定义在哪里,都是一样的,没有区别,不管你是在对象中还是函数中,还是全局中的,都是一样的,我就是看你在哪里调用了,this是和javascript中执行环境有关,而非声明环境有关),

,不要认为show函数是obj的,就和obj有关系,这里就是想当于setTimeout(function(){console.log(this.num)},1000);其实这就是函数中嵌套函数,所以里面的this指向window;如果你感觉setTimeout有特别不和其他函数一样,你就看下面的,一样的,只不过这个是普通函数:

var num = 2;
var obj = {
    num:1,
    foo2:function() {
        function foo3(a) {
            console.log(this);//window
            console.log(a);//1
        }
        foo3(this.num);//this =>obj
    }
}
obj.foo2();

在调用foo3时他参数this就是obj,跟setTimeout中的是一样的;它里面的this也是window,这就是foo3是函数调用的,

 002:

var x = 2;
var obj = {
    x:1,
    a:function () {
        var x = 3;
        var b = function () {
            console.log(this.x);
        };
        b();
    }

}
obj.a();//2

这个和上面那个例子差不多,就是函数中嵌套函数的情况,一定是指向window了

003,简单的:

function foo() {
  function bar() {
    alert(this);
  }
  bar(); 
}
foo();//window
View Code

0x02:方法中的调用

一定指向调用它的那个对象

001

var num = 1;
function foo() {
    console.log(this.num);
}
foo();//1 

这其实就是window对象下的foo()方法的调用 ==》window.foo();只是在全局下可以省去罢了;

002

var obj = {
    num:1,
    foo:function() {
    console.log(this.num);
    }
}
obj.foo();//1

003

var obj1 = {
    num:1,
    showNum1:function() {
        console.log(this.num);
    }
};
var obj2 = {
    num:2,
    showNum2:obj1.showNum1
}
obj2.showNum2();//2

其实obj2等价与这样:

var obj2 = {
num:2,
showNum2:function(){
console.log(this.num);
}
}

只不过这个函数我是引用的obj1中的showNum2函数罢了,这样看你就知道了,再次说明了我说setTimeout时所说的,只不过我们showNum2:obj1.showNum1这样用就给我们一种感觉,它就是跟obj1有关系,其实在this看来没有关系,this就是个有奶便是娘的东西,它不管哪个娘生的它,谁是它亲娘(在哪个地方声明(定义)的它),只要给的奶喝,它就认谁为娘(谁执行调用的它);你一定要抛开声明地方不同,对你的影响;

 004

var x= 2;
var obj = {
    x:1,
    fn:function() {
        alert(this.x);
    }
};
// obj.fn();
var test = obj.fn;
test();//2

也是那种情况,

等价与

var test = function(){
    alert(this.x);           
}
test();//2

005:

function foo() {
  alert(this.bar);
}
 
var x = {bar: 10};
var y = {bar: 20};
 
x.test = foo;
y.test = foo;
 
x.test(); // 10
y.test(); // 20
View Code

006

var x = 10;
var obj = {
    foo:function() {
        alert(this.x);
    },
    x:20
};
with(obj) {
    foo();//20
}

调用foo时就是相当于obj.foo();

007

var x = 3;
var foo = {
    x:2,
    baz:{
        x:1,
        bar:function() {
            return this.x;
        }
    }
}
var go = foo.baz.bar;
console.log(go());//3
console.log(foo.baz.bar());//1

这就是变量go引用了函数function(){return this.x};这个就是foo.baz调用了bar()函数所以this指向调用者foo.baz这个对象

008

function Foo() {}
Foo.prototype.method = function() {console.log(this)};
function Bar() {}
Bar.prototype = Foo.prototype;
var obj = new Bar();
obj.method();//Bar

 

0x03:构造函数中

 

function Person() {
    console.log(this);
    this.x = 1;
}
var people = new Person();
console.log(people.x);//10

上面没有显式的创建对象;直接将属性赋值给this对象;没有return语句;

其实在你使用new操作符,调用构造函数其实经历了以下四个过程:

  1. 自动创建一个对象
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
  3. 执行构造函数中的代码
  4. 把这个新对象返回出去(隐式的return)

其实就是这样差不多:

function person() {
    var o = {};
    o.x = 1;
    return o;
}
var people = person();
console.log(people.x);
View Code

 这个this是有用处的,它可以使每个new实例化的对象都有属于自己的属性,而不全都是从原型中继承而来的。

0x04:apply和call方法中

他们都是在特定作用域中调用函数,实际上等于设置了函数体内的this对象的值,第一个参数在ES3或非严格模式中,传入的的null和undefined都会被全局对象代替,其他的原始值则会被相应的包装对象所代替,在严格模式下,第一个实参是null或undefined时,里面的this就是null或者是undefined,

001

function f() {
    console.log(this.x + " " + this.y);
}
var obj = {
    x:1,
    y:2
}
f.call(obj);//1 2

这里的this就是obj

002

function show() {
    console.log(this);
    function test() {
        console.log(this);
    }
    test();
}
show.call({x:1});//{X:1},window

这里就是首先由于call方法所以改变了作用域,show()函数的this指向了{x:1},test()属于函数调用函数的情况所以是window对象;

 上面说的都是javascript语言中this;

现在说说DOM对象中的this

001.

一种是DOM0级事件绑定中的this,

obj.onclick = function () {
console.log(this);//obj
}

上面说了这么多你,你在看这里为什么这个this是obj你知道了吗?因为当你触发事件的时候,是obj对象就会检测它的onclick属性有没有绑定函数,如果绑定了就调用。

002.

DOM2级中的

先来个这样的:

<script type="text/javascript">
var x = 2;
var obj = {
    x:1,
    fn:function(f) {
        f();
    }
}

obj.fn(function(){console.log(this.x);});//2
</script>

它是会显示2的,也就是说这个this是window,和上面我罗嗦了半天的函数调用的情况,函数中调用函数

但是你看这里:

 

var x = 2;
var test = document.getElementById("test");
test.x = 1;
test.addEventListener('click',function() {
    console.log(this.x);//1
    console.log(this);//test对象
});

这是怎么回事???要是按照上面的来应该是window对象呀,这就是addEventListener方法的特别之处,它不是不同的函数呀,简单说这个函数的作用就是把第二个参数的的函数,绑定在调用它的那个对象(这里是test)下的,有跟addEventListener的第一个参数的名字相同的属性名下。就类似这样:

test = {
//这里是很多的属性名
//.....
//......
click:function(){
  console.log(this);  
}
}

这个函数大体就是这个作用;

而IE的attachEvent就跟上面说的就差不多了,它的this是window;

不写了就写这么多吧。总结就是,this是和执行环境有关,不是和声明函数有关,在你用this的时候要小心点,实在不确定是什么,你就alert一下,不就知道了

 

 

 

 

下面这些是我的参考,我的很多很多例子就是从他们那里的,要不然那么多情况我怎么一时半会也想不出出来呀,他们写的这些都是精化呀,如果你感觉我写的把你搞晕了的话,还没有明白的话,我建议你看看这些,我相信在你看明白这些后你就对this有自己的理解和认识;

javascript秘密花园:http://bonsaiden.github.io/JavaScript-Garden/zh/#function.this

大叔的:http://www.cnblogs.com/TomXu/archive/2012/01/17/2310479.html

大额_skylar:http://www.cnblogs.com/skylar/p/4042663.html

http://www.cnblogs.com/justany/archive/2012/11/01/the_keyword_this_in_javascript.html

http://www.cnblogs.com/KevinYang/archive/2009/07/14/1522915.html

http://www.cnblogs.com/rush/archive/2012/07/31/2617429.html

javascript高级程序设计

javascript权威指南