在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
特权方法可以访问私有属性和方法