1. 全局代码中的this

this在全局上下文中,它的值是全局对象本身(Global Object),在浏览器中就是Window  Object,如下图示。

看下面几个例子:

//Global scope

//The implicit property of the global object 

var foo1 = "abc";

console.log(this.foo1 == window.foo1); console.log(foo1); console.log(this.foo1); console.log(window.foo1);  //true abc abc abc

 

//The implicit property of the global object 

foo2 = "def";  

console.log(this.foo2 == window.foo2); console.log(foo2); console.log(this.foo2); console.log(window.foo2);      //true def def def

 

//The explicit property of the global object 

this.foo3 = "gh";  

console.log(this.foo3 == window.foo3); console.log(foo3); console.log(this.foo3); console.log(window.foo3); //true gh gh gh

2. 函数代码中的this

共有四种函数调用方式:

1)方法调用模式:作为对象属性调用,obj.func();(详见8.对象中的this)

2)函数调用模式:指向全局,直接调用func();     (详见1.全局代码中this)

3)构造器调用模式:使用new调用;                   (详见6.作为构造器调用的函数中的this)

4)call/apply调用模式:动态改变this指向。         (详见7.手动设置函数调用时this的值)

函数代码中this值的第一个特性,同时也是最主要的特性,即:它并非静态地绑定在函数上。

this的值是在进入执行上下文(Context Excution)的阶段确定的,在函数代码中,其值每次都不尽相同。然而进入执行代码阶段,其值就不能改变。

如果想给this赋一个新值是不可能的,因为在那时this根本就不是变量了。

结合例子来分析:

var foo = {

    name: "Foo"

};

var bar= {

    name: "ting",

    sayName: function() {

        console.log(this === bar);

        console.log("My name is: " + this.name);

    }

};

bar.sayName();   //true My name is: ting

foo.sayName = bar.sayName;

foo.sayName();      //false My name is: Foo

通过上述结果分析可知:调用bar对象的sayName方法时,this指向bar对象。通过赋值方式使得foo的sayName方法指向bar的sayName方法,再调用foo对象的sayName方法时,this指向foo对象。Why?

this的值在函数中是非静态的,它的值确定是在执行代码阶段(函数调用时,具体代码执行前)。this的值是由激活上下文代码的调用者提供的,比如调用函数的外层上下文,更重要的是,this的值是由调用表达式的形式决定的。因此说,this并非静态绑定在函数上。

由于this非静态地绑定在函数上,那么我们是否可以在函数中动态地修改this的值呢?

var foo = {

    name: "Foo"

};

var person = {

    name: "ting",

    sayName: function() {

        console.log(this == person);

        this = foo;

        console.log("My name is: " + this.name);

    }

};

person.sayName();   //My name is Foo

foo.sayName = person.sayName;

foo.sayName();      //My name is ting

在sayName方法中,动态地修改this的值,重新执行以上的代码,发现this的值引用错误。这是由于一旦进入代码执行阶段(函数调用时,执行代码前),this的值确定,不能被改变。

影响函数代码中this值变化的因素在通常的函数调用中,this是由激活上下文代码的调用者来提供的,即调用函数的父上下文(parent context)。this值取决于调用函数的方式。

误区:一些文章或JavaScript书籍中提到:this值取决于函数定义的方式,如果它是全局函数,this设置为全局对象,如果它是一个对象的方法,那么this总是指向这个对象。这是绝对不正确的。

切记一点:正是调用函数的方式影响了上下文中this的值。

eg: 全局函数被调用方式的不同形式激活,不同的调用方式导致了不同的this值。

function foo() {

    console.log(this);

}

foo();  //Window

console.log(foo == foo.prototype.constructor);  //true

foo.prototype.constructor();  //foo {}

eg: 对象定义的方法,执行该方法,this值未必指向该对象。

var foo = {

    bar: function() {

        console.log(this);

        console.log(this === foo);

    }

};

foo.bar(); //foo true

var fooTest = foo.bar;

console.log(fooTest === foo.bar);

//同一个function,不同的调用表达式,this值不同

fooTest(); //Window false

调用方式如何影响this的值? 为充分理解this值的确定,需要详细分析其内部类型之一,即引用类型(Reference type)。

3. 引用类型(Reference Type)

使用伪代码将引用类型的值表示为拥有两个属性的对象,包括base(拥有属性的那个对象),对象中的propertyName(属性名)。

var valueofRefereceType = {

    base: <base object>,

    propertyName: <property name>

};

引用类型的值只有两种情况:

1)当我们处理一个标识符时

2)或属性访问器

标识符的处理过程另作讨论,目前只需知道,该算法的返回值中,总是一个引用类型的值,这对于this来说非常重要。

标识符是:变量名、函数名、函数参数名或全局对象中未识别的属性名。例如,下面标识符的值:

var foo;

function bar() { } 

在操作中间结果的过程中,引用类型对应的值如下:

var fooReference = {

    base: global,

    propertyName: 'foo'

};

var barReference = {

    base: global,

    propertyName: 'bar'

};

伪代码GetValue描述从一个引用类型中得到对象真正的值。

function GetValue(value) {

    if( Type(value) != Reference) {

        return value;

    }

    var base = GetBase(value);

    if(base === null) {

        throw return new ReferenceTypeError;

    }

    return base.[[Get]](GetPropertyName(value));

}

内部的[[Get]]方法返回对象属性真正的值,包括对原型链中继承的属性分析。

GetValue(fooReference); // 10

GetValue(barReference); // function object "bar"

属性访问器:点(.)语法或括号([])语法

foo.bar();

foo['bar']();

在中间计算的返回值中,引用类型的值为:

var fooReference = {

    base: foo,

    propertyName: 'bar'

};

GetValue(fooReference); // function object "bar"

引用类型的值域函数上下文中的this如何相关? --从最重要的意义上来说。  这个关联的过程是这篇文章的核心。

一个函数上下文中,确定this值的通用规则如下:

1)在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。

2)如果调用括号()的左边是引用类型的值,this值将设为引用类型值的base对象(base object)。

3)如果调用括号()的左边不是引用类型的值(与引用类型类型不同的任何其它属性),this值为null,不过实际不存在this值为null的情况,因为当this值为null的时候,其值会被隐示转换为全局对象。(注:第5版的ECMAScript,已经不强迫转换为全局变量,而是赋值为undefined。)

看些例子,理解函数上下文中this值。

function foo() {

    console.log(this);

}

foo(); //window

调用括号()的左边是引用类型的值,因为foo是一个标识符。

var fooReference = {

    base: global,

    propertyName: 'foo'

};

相应地,this设置为引用类型的base对象,即全局对象。

使用属性访问器:

var foo = {

    bar: function() {

        console.log(this);

    }

};

foo.bar(); //foo

我们再次拥有一个引用类型,其base是foo对象,在函数bar激活时用作this。

var fooBarReference = {

    base: foo,

    propertyName: 'bar'

};

但是,使用另外一种形式激活相同的函数,我们得到其它的this值。

var foo = {

  bar: function () {

    console.log(this);

  }

};

var test = foo.bar;

test(); 
//window

因为test作为标识符,生成了引用类型的其它值,其base(全局对象)用作this的值。

var testReference = {

    base: global,

    propertyName: 'test'

};

从上述例子中可以分析得知:使用不同的表达式激活函数为何会导致不同的this值,原因在于生成了引用类型(Type Reference)不同的中间值

再看两个例子,加深印象。

var foo = function() {

    console.log(this);

};

foo(); //window,because

var fooReference = {

    base: global,

    propertyName: 'foo'

};

console.log(foo = foo.prototype.constructor);

//另外一种形式的调用表达式

foo.prototype.constructor(); //foo.prototype,because

var fooPrototypeConstructor = {

    base: foo.prototype,

    propertyName: 'constructor'

};

通过调用方式动态确定this值的经典例子。

function foo() {

    console.log(this.bar);

}

var x = {bar: 10};

var y = {bar: 20};

x.test = foo;

y.test = foo;

x.test(); //

y.test(); //

4. 函数调用和非引用类型

正如前面所提到的,当调用括号的左边不是引用类型而是其它类型,这个值自动设置为null,结果为全局对象。

思考如下这种表达式:

(function () {

    console.log(this);

})(); //null ---> global

在这个例子中,我们有一个函数对象但不是引用类型的对象(它不是标识符,也不是属性选择器),相应地,this值最终设为全局对象。

更多复杂的例子:

var foo = {

    bar: function() {

        console.log(this);

    }

};

foo.bar(); //foo

(foo.bar)(); //foo

(foo.bar = foo.bar)(); //Window

(false || foo.bar)(); //Window

(foo.bar, foo.bar)(); //Window

一个属性访问器,它的中间值应该是引用类型的值,但是在某些调用中,我们得到的this值不是base对象,而是global对象。Why?

原因在于后面3个的调用中,应用一定的操作符之后,调用括号左边的值不再是引用类型的值。

第一个例子 --- 调用括号左边是引用类型的值,this为base对象,即foo;

第二个例子 --- 组运算符并不适用,如上面所提到的,从引用类型获取一个对象真正值的方法,如GetValue。 相应地,在组运算的返回中,得到的仍是一个引用类型。因此this值再次被设为base对象,即foo;

第三个例子 --- 与组操作符不同,赋值运算符触发了GetValue方法,返回的结果是函数对象,而不是引用类型的值,这意味着this值会被设置为null,最终会被变成全局对象。

第四个例子 --- 逻辑或运算符触发了GetValue方法,返回的结果......

第五个例子 --- 逗号运算符触发了GetValue方法,返回的结果是......

5. 引用类型和this为null

例外的情况:调用括号左侧是引用类型的值,但this却为null,最终被隐示转换为全局对象(global object)。情况成立的条件是:当引用类型值的base对象恰好为活跃对象(activiation object)。例如:内部子函数在父函数中被调用。局部变量、内部函数、形式参数存储在给定函数的激活对象中。

function foo() {

  function bar() {

    console.log(this); // Window

  }

  bar(); // the same as AO.bar()

}
foo(); 

活跃对象总是作为this返回,值为null(用伪代码表示AO.bar()相当于null.bar())。正如上面例子提到的,this的值最终由null隐示转换为全局对象。

上述例子的变形:

function foo() {

  function bar() {

    console.log(this); // Window

  }

  return bar(); // the same as AO.bar()

}

foo(); 

有一种情况与上述情况又不一样:使用with。

如果with对象包含函数属性,并且在with语句块中调用该函数。with语句会将该对象添加到作用域链的最前面,即在活跃对象之前。相应地,有了引用类型的值(标识符或属性访问器),其base对象不再是活跃对象,而是with语句对象。另外,值得一提的是,它不仅仅只针对内部函数,也针对全局函数,原因在于with对象比作用域链最前面的对象(全局对象或活跃对象)还要靠前。

var x =10;

with({

    foo: function() {

        console.log(this.x);

    },

    x: 20

}) {

    foo();  //

}

//because

var fooReference = {

    base: _withObject,

    propertyName: 'foo'

};

同样的情况出现在catch语句的实际参数中函数调用,catch对象添加到作用域的最前端,即活动对象或全局对象的前面。但是,这个特定的行为被确认为ECMAScript-262-3的bug,在ECMAScript-262-5中被修复了。这样,在特定的活动对象中,this不是指向catch对象,而是指向全局对象。

try {

    throw function() {

        console.log(this);

    };

} catch (e) {

    e();  //ES3标准里是_catchObject,ES5标准里是Window

}

//ES3

var eReference = {

    base: _catchObject,

    propertyName: 'e'

};

//ES5

var eReference = {

    base: global,

    propertyName: 'e'

};

同样的情况出现在命名函数的递归调用中。在第一次递归调用中,base对象是父活跃对象或全局对象,在递归调用中,base对象应该是存储着函数表达式可选名称的特定对象。但是,在这种情况下,this总是指向全局对象。

(function foo(bar) {
  alert(this);
  !bar && foo(1);
})();

6. 作为构造器调用的函数中的this

函数作为构造函数时,通过new操作符创建对象实例的过程包括:首先调用Foo()函数内部的[[construct]]方法,其次,在对象创建之后,调用内部的[[call]]方法。所有相同的函数“Foo”将this设置为新创建的对象

function Foo() {

    console.log(this);

    this.x = 10;

}

var a = new Foo();

console.log(a.x);

再看一例:Person不做为构造函数调用,而是作为普通函数在全局作用域中被调用,this指向Window,因此全局对象中的name被修改为mars。至于Person.name的值为空字符串,有点奇怪? 更奇怪的是将代码中Person函数的name全部换为zzz或其它标识,Person.zzz为undefined?? 原因是:name是函数的一个内部参数,标识函数的名字,由于赋给Person的函数为匿名函数,因此name为空字符串,但是用其它标识符,由于Person中没有定义该属性,因此值为undefined。

var name = 'window_mars';

var Person = function (name) {

    this.name = name;

};

Person('mars');

console.log(name);         //mars

var s1 = Person.name;      //""

var o = new Person("ting");

console.log(o.name);       //ting

下面的例子可以很好地说明name属性为函数自带的属性值,且其值不能被覆盖。

var name = 'window_mars';

var Person = function (name) {

    this.name = name;

};

Person.name = name;

Person.name1 = name;

var s1 = Person.name;      //""

var s2 = Person.name1;     //"window_mars"

var o = new Person("ting");

console.log(o.name);       //ting

7. 手动设置函数调用时this的值(apply()、call())

Function.prototype原型上定义了两个方法,允许手动设置函数调用时的值,这两个方法分别是:apply()和call()。这两个方法都接受第一个参数作为调用上下文中this的值,而这两个方法的区别是传递参数,对于apply()方法,第二个参数是数组类型(或类数组对象,比如arguments),而对于call()方法接受任意多的参数(逗号分隔)。这两个方法的第一个参数如果不传或为null,那this的值为Window。

例一:

var b = 10;

function a(c) {

    console.log(this.b);

    console.log(c);

}

a(20); //this.b is 10, c is 20

a.apply({b:20},30); //this.b is 20, c is 30

b.apply({b:40},[40]); //this.b is 40, c is 40

例二:

var myObject = {};

var myFunction = function(param1, param2) {

    this.x = param1;

    this.y = param2;

    console.log(this); //Object {x: "kkk", y: "ooo"} 

};

myFunction.apply(myObject, ["kkk", "ooo"]);

console.log(myObject); //Object {x: "kkk", y: "ooo"} 

myFunction.call(myObject, "kkk", "ooo");

console.log(myObject); //Object {x: "kkk", y: "ooo"} 

8. 对象中的this

对象中this值的判断,其实最终也牵涉到函数中this值的判断,因此可按照函数上下文中this值的判断规则来确定this值。 看下面几个例子。

函数getName()上下文中this值的判断,引用类型中间值的base对象是person对象,this值为person对象,如下面代码所示。

var person = {

    name: "ting",

    getName: function() {

        console.log(this); //Object {name: "ting", getName: function}

        console.log(this.name); //ting

    }

};

person.getName();

函数getName()和getGlobalName()上下文中this值的判断,this值分别为person对象和全局对象。

var name = "global-ting";

function getGlobalName () {

    return this.name;

}

var person = {

    name: "ting",

    getName: function() {

        return this.name;

    }

};

var s1 = person.getName(); //"ting"

var s2 = getGlobalName();  //"global-ting"

上述例子的变形:

var name = "global-ting";

function getGlobalName () {

    return this.name;

}

var person = {

    name: "ting",

    getName: getGlobalName

};

var s1 = person.getName(); //"ting"

var s2 = getGlobalName();  //"global-ting"

上述例子的变形:

function getGlobalName () {

    return this.name;

}

var person = {

    name: "ting",

    getName: getGlobalName

};

var hehe = {

    name: "haha",

    getName: getGlobalName

}

var s1 = person.getName(); //"ting"

var s2 = getGlobalName();  //"global-ting"

var s3 = hehe.getName(); //"haha"

9. DOM事件中的this

示例一:el指向绑定该事件的dom元素,事件处理程序中的this指向Window

<script type="text/javascript">

        function test_this(el) {

            var element = el;

            var scope = this;

        }

</script>

<div id="news" class="overlay" onclick="test_this(this);">点击我</div>

示例二:event指向触发的事件MouseEvent,this指向绑定该事件的DOM元素。

<script type="text/javascript">

    function test_this(event) {

        var e = event;

        var scope = this;

    }

    document.getElementById("news").onclick = test_this;

</script>

<div id="news" class="overlay">点击我</div>

   

示例三:与示例二的结果一致,即event指向事件MouseEvent,this为绑定该事件的DOM元素。

<script type="text/javascript">

    function test_this(event) {

        var e = evemt;

        var scope = this;

    }

    document.getElementById("news").addEventListener("click", test_this, false);

</script>

<div id="news" class="overlay">点击我</div>

10. 检测一下

exam1:baz为全局变量,函数执行完后,没有被释放;bar为局部变量,函数立即执行完,该变量被释放,因此报错。

(function () {

    baz = 5;

    var bar = 10;

})();

console.log(baz); //5
console.log(bar); //报错,ReferenceError:bar is not defined.

exam2:严格模式下会报错:ReferenceError:bar is not defined.

"use strict";

(function () {

    baz = 5;   //报错

    var bar = 10;

})();

console.log(baz);

exam3:两处的a都是在全局域中被操作。

a = 2;

var a = 1;

console.log(a); //1

终于整理完了^-^.

时间:2014-10-19

地点:合肥

引用:http://www.cnblogs.com/yexiaochai/archive/2013/04/22/3034949.html

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

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

JavaScript的this用法的更多相关文章

  1. #Javascript:this用法整理

    常用Javascript的人都知道,[this這個關鍵字在一個函式內究竟指向誰]的這個問題很令人頭大,本人在這裡整理了一下Javascript中this的指向的五種不同情況,其中前三種屬於基本的情況, ...

  2. 好程序员web前端分享javascript关联数组用法总结

    好程序员web前端分享javascript关联数组用法总结,有需要的朋友可以参考下. Hash关联数组定义 代码如下 // 定义空数组 myhash = { } // 直接定义数组 myhash = ...

  3. JS的javascript:void(0)用法

    javascript:void(0)用法如下: <a href="javascript:void(0)"></a> // 执行js函数,0表示不执行函数. ...

  4. Javascript的this用法---阮一峰

    Javascript的this用法   作者: 阮一峰 日期: 2010年4月30日 this是Javascript语言的一个关键字. 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比 ...

  5. javascript:void(0);用法及常见问题解析

    void 操作符用法格式: javascript:void (expression) 下面的代码创建了一个超级链接,当用户以后不会发生任何事.当用户链接时,void(0) 计算为 0,但 Javasc ...

  6. [JS]Javascript的this用法

    转自:阮一峰 this是Javascript语言的一个关键字. 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比如, function test(){ this.x = 1; } 随着 ...

  7. javascript的setTimeout()用法总结,js的setTimeout()方法

    引子 js的setTimeout方法用处比较多,通常用在页面刷新了.延迟执行了等等.但是很多javascript新手对setTimeout的用法还是不是很了解.虽然我学习和应用javascript已经 ...

  8. javascript array类型用法

    javascript高级编程-Array引用类型用法总结  2016-09-17   |    357 引用类型-Array类型 引用类型是一种数据结构,用于将数据和功能联系起来. 创建对象的方式: ...

  9. javascript typeof()的用法与运算符用法

    typeof 运算符 返回一个用来表示表达式的数据类型的字符串. typeof[()expression[]] ; expression 参数是需要查找类型信息的任意表达式. 说明 typeof 运算 ...

  10. Javascript的this用法及jQuery中$this和$(this)的区别

    this是Javascript语言的一个关键字. 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比如, function test(){ this.x = 1; } 1.this就是全 ...

随机推荐

  1. 《zw版·Halcon-delphi系列原创教程》 Halcon分类函数·简明中文手册 总览

    <zw版·Halcon-delphi系列原创教程> Halcon分类函数·简明中文手册 总览 Halcon函数库非常庞大,光HALCONXLib_TLB.pas文件,源码就要7w多行,但核 ...

  2. 锁表 for update

    select for update 是为了在查询时,避免其他用户以该表进行插入,修改或删除等操作,造成表的不一致性. 举几个例子:select * from t for update 会等待行锁释放之 ...

  3. 整理课堂笔记 pl/sql orcale异常

      1>>>>>异常错误处理 1 >预定义的异常处理 预定义说明的部分 ORACLE 异常错误对这种异常情况的处理,只需在PL/SQL块的异常处理部分,直接引用相应 ...

  4. Java 基础知识 练习

    1.在DOS命令下输入:java Hello出现以下结果:Bad command or the file name可能是什么原因? (错误的命令或文件名) 输入的命令不存在,或者不在指定的路径中 2. ...

  5. 如何从github上面拷贝源码

    有好奇心的朋友们一定都想看一看很多开源项目的源码,那么github就不用说啦,太多的开源项目都把源码放到上面. 博主最近为了学习angularjs也不得不去github上面弄源码,下面将会介绍如何做: ...

  6. asp.net core 使用 StaticFiles 中间件 (不完整翻译)

    原文地址:https://docs.asp.net/en/latest/fundamentals/static-files.html 设置静态资源根目录 在 Startup.cs 中的 Configu ...

  7. [SDN] mininet walkthrough

    本次学习使用的是mininet的VM-image,所以安装过程就先忽略掉了,主要学习使用方法. 同时完成了在虚拟机上配置minient和Wireshark, 可以直接在虚拟机上操作. 1. Every ...

  8. python学习笔记七 初识socket(进阶篇)

    socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...

  9. QT笔记之VS开发添加类

    1. 2. 3.

  10. Android 性能分析工具dumpsys的使用(自己增加一部分在后面)

    Android提供的dumpsys工具可以用于查看感兴趣的系统服务信息与状态,手机连接电脑后可以直接命令行执行adb shell dumpsys 查看所有支持的Service但是这样输出的太多,可以通 ...