javascript基础三:this

Posted by 徐璇 on October 7, 2013

在JAVA,C,C#,C++等语言中,this很专一,总是指向当前的运行对象。
但是javascript的this却很花心,在哪个对象的家里,它就是那个对象的!

请牢牢记住这句话:
this变量永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。如果处在匿名函数中或者不处于任何对象中,this都指向宿主的根对象(在浏览器里面就是 window)。

另外,javascript中还用 call()apply() 来调用函数,再记住下面这两句话:
如果是call()、apply()、with(),指定的this是谁,就是谁。
普通的函数调用,函数被谁调用,this就是谁。

注意:ECMAScript 5 的严格模式中,严禁使用with()函数,并且ECMAScript 3 中并不推荐使用with()。

下面介绍下js函数的几种调用形式:

1.纯粹的函数调用

这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global。

var x = 1;

function test(){
    console.log(this);    // window 证明this就是全局对象
    console.log(this.x);  // 1
    this.x = 0;           // 改变window.x,此时若window没有变量x,则在window下创建一个x变量
    console.log(this.x);  // 0
}

console.log(this.x);     // 1
test();
console.log(this.x);     // 0

以下情况会意外创建全局变量,要避免

function makeNoSense(x) {
     this.x = x;     // this被绑定到全局对象
}
makeNoSense(5);
console.log(x);      // x已经成为一个值为5的全局变量

2.作为对象方法的调用

函数还可以作为某个对象的方法调用,这时this就指这个上级对象

function test(){
    console.log(this.x);  //this指函数的调用者
}
var o = {
    x: 1,
    m: test
};
test();  //undefined
o.m();   //1
var obj = {
    that: this,
    fn: function(){
        console.log(this);
    }
}
console.log(obj.that); //window, this不存在Object对象中的时候,this就指向window全局对象
obj.fn();              //obj, this指向调用其所在函数的对象

3.作为构造函数调用

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

var o = new test();  // 没有改变global的x
console.log(o.x);    // 1
console.log(x);      // 2

test();              // 改变了global的x
console.log(o.x);    // 1
console.log(x);      // 1

4.apply调用

var x = 2;
function test(){
    console.log(this.x);
}

var o = {};
o.x = 1;
o.m = test;

o.m();          //1
o.m.apply();    //2  参数为空时,默认调用全局对象
o.m.apply(o);   //1
function changeStyle(type, value){
    this.style[type] = value;
}
var one = document.getElementById( 'one' );
changeStyle.call(one, 'fontSize', '100px');
changeStyle.apply(one, ['fontSize', '100px']);
changeStyle('fontSize' , '300px');  //出现错误,因为此时changeStyle中this引用的是window对象,而window并无style属性。

私有属性和方法、特权属性和方法、共有属性和方法、共有静态属性和方法

私有属性和方法:函数有作用域,在函数内用var关键字声明的变量在外部无法访问, 私有属性和方法本质就是你希望在对象外部无法访问的变量。

特权属性和方法:创建属性和方法时使用的this关键字,因为这些方法定义在构造器的作用域中,所以它们可以访问到私有属性和方法; 只有那些需要直接访问私有成员的方法才应该被设计为特权方法。

共有属性和方法:直接链在prototype上的属性和方法,不可以访问构造器内的私有成员,可以访问特权成员,子类会继承所有的共有方法。

共有静态属性和方法:最好的理解方式就是把它想象成一个命名空间,实际上相当于把构造器作为命名空间来使用。

var _packaging= function (){
    //私有属性和方法
    var name= 'Darren';
    var method1= function(){...}

    //特权属性和方法
    this.title= 'JavaScript Design Patterns' ;
    this.getName= function(){return name;}
 }

 //共有静态属性和方法
 _packaging._name= 'Darren code';
 _packaging.alertName= function (){
    alert(_packaging._name);
 }

 //共有属性和方法
 _packaging.prototype= {
    init:function (){...}
}

小栗子1:

//全局变量
name = "xuxuan";

var Pet = function(){

    //私有属性和方法
    var name = "xxx";
    function showname(){
        console.log("2 "+this.name); // 这里的this->window 'xuxuan'
        console.log("3 "+name);      // 'xxx'
    }

    //特权属性和方法
    this.name = "xx";
    this.show = function(){
        console.log("1 "+this.name); // 这里的this->pet  'xx'
        showname();
        console.log("4 "+name);      // 'xxx'
    }
}

var cat = new Pet();
console.log("0 "+cat.name); //xx
cat.show();

/*
输出如下:
0 xx
1 xx
2 xuxuan
3 xxx
4 xxx
*/

私有方法的this分两种情况,单独调用的指向window,有调用者的指向调用者
特权方法的this指向当前实例

小栗子2:

//全局变量
name = "xuxuan";

var Pet = function(){

    //私有属性和方法
    var name = "xxx";
    function showname(){
        console.log(this.name); // 这里的this->window 'xuxuan'
    }

    //特权属性和方法
    this.name = "xx";
    this.show = function(){
        console.log(this.name); // 这里的this->pet  'xx'
        showname();
    }
}

Pet.prototype.setnamePet = function(str){   // 共有方法,可以访问特权成员
    this.name = str;   // 这里的name->特权属性name
}

Pet.prototype.setnameGlobal = function(str){ // 共有方法
    name = str;        // 这里的name->全局变量name
}

var cat = new Pet();
console.log(cat.name); //xx
cat.show();   //xx xuxuan

cat.setnamePet("Penguin1");
cat.show();   //Penguin1 xuxuan
cat.setnamePet("wind1");
cat.show();   //wind1 xuxuan

cat.setnameGlobal("Penguin2");
cat.show();   //wind1 Penguin2
cat.setnameGlobal("wind2");
cat.show();   //wind1 wind2

特权方法可以访问私有属性和方法