Javascript中,this是一个非常有用的关键字, this是在运行时基于函数的运行环境绑定的,但是,如果使用的时候不注意,很容易就出错了。

ECMAScript Standard对this的定义看起来非常简单: The this keyword evaluates to the value of the ThisBinding of the current execution context.

其中,对于ThisBinding和execution context, ECMAScript Standard 有另外的详细说明:

执行环境(引用http://ecmascript.cn/)

当控制器转入 ECMA 脚本的可执行代码时,控制器会进入一个执行环境。当前活动的多个执行环境在逻辑上形成一个栈结构。该逻辑栈的最顶层的执行环境称为当前运行的执行环境。任何时候,当控制器从当前运行的执行环境相关的可执行代码转入与该执行环境无关的可执行代码时,会创建一个新的执行环境。新建的这个执行环境会推入栈中,成为当前运行的执行环境。

执行环境包含所有用于追踪与其相关的代码的执行进度的状态。精确地说,每个执行环境包含如下表列出的组件。

执行环境的状态组件

组件

作用目的

词法环境

指定一个词法环境对象,用于解析该执行环境内的代码创建的标识符引用。

变量环境

指定一个词法环境对象,其环境数据用于保存由该执行环境内的代码通过 变量表达式 和 函数表达式 创建的绑定。

ThisBinding

指定该执行环境内的 ECMA 脚本代码中 this 关键字所关联的值。

其中执行环境的词法环境和变量环境组件始终为 词法环境 对象。当创建一个执行环境时,其词法环境组件和变量环境组件最初是同一个值。在该执行环境相关联的代码的执行过程中,变量环境组件永远不变,而词法环境组件有可能改变。

在本标准中,通常情况下,只有正在运行的执行环境(执行环境栈里的最顶层对象)会被算法直接修改。因此当遇到“词法环境”,“变量环境”和“ThisBinding”这三个术语时,指的是正在运行的执行环境的对应组件。

执行环境是一个纯粹的标准机制,并不代表任何 ECMA 脚本实现的工件。在 ECMA 脚本程序中是不可能访问到执行环境的。

ThisBinding

对于ThisBinding的指定,则分为几个情况,一般主要关注在函数中的绑定情况:

1. 初始化全局执行环境时,将 thisBinding设置为 全局对象。

2. 进入函数代码的执行环境时,控制流根据一个函数对象 F、调用者提供的 thisArg 以及调用者提供的 argumentList,执行以下步骤:

A.如果 函数代码 是 严格模式下的代码 ,设 ThisBinding为 thisArg。

B. 否则如果 thisArg 是 null 或 undefined,则设 ThisBinding为 全局对象 。

C. 否则如果 Type(thisArg) 的结果不为 Object,则设 tThisBinding为 ToObject(thisArg)。

D.否则设 this 绑定为 thisArg。

因此,最终决定ThisBinding的是调用者提供的 thisArg那么,如何知道thisArg的值呢?这就要看函数的调用方式了。

一.通过call或者apply调用

通过call或者apply调用函数时,thisArg的值比较明显,为传入的第一个参数。

Function.prototype.apply (thisArg, argArray)

Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

如下:

function a(){

    console.log(this);

}

function b(){ }

a.apply(a); // function a

a.apply(b); // function b

二. 作为对象方法调用

当函数作为对象方法调用时,this指向对象。

var a = {};

a.b = b;

a.b(); // object a

function b(){

    console.log(this);

}

三.直接调用函数

直接调用函数时,this为全局变量Window(thisArg为null)。

function a(){

console.log(this);

}

a(); // Window

要注意,直接调用函数时,无论嵌套多少层,无论是在哪个对象里面调用,this始终指向Window。

function a(){

    console.log(this); // Window

    b(); // Window

    function b(){

        console.log(this);

        c(); // Window

        function c(){

            console.log(this);

        }

    }

}

a();

再看另一个例子:

var a = {

    b : function(){

        console.log(this); // Object a

        c(); // Window

        function c(){

            console.log(this);

        }

        d(); // Window

    }

};

function d(){

    console.log(this);

}

a.b();

四.作为构造函数调用

当函数作为构造函数调用时,this指向新建的对象。

var A = function(){

    console.log(this);

    this.c = 1;

    this.d = 2;

    this.b = function(){

        console.log(this);

        console.log(this.c);

        console.log(this.d);

    };

};

var a = new A(); // A{}

a.d = 3;

a.b(); // A {c: 1, b: function}

// 1

// 3

以上几种便是基本函数调用方式了,后面的几种调用方式中,只要识别函数基于上面哪种方式调用,便可以辨别this对象。

五. 作为回调函数调用

当函数通过回调函数调用时,其调用方式可能会让人感到疑惑,先来看一个例子:

var a = {

    b : b

};

function b(){

    console.log(this);

}

setTimeout(b, 500); // window

setTimeout(a.b, 1000); // window

第一处为this为window应该很好理解,因为看起来是直接调用函数,因此this指向window对象。但是第二处看起来似乎是作为对象的方法调用?事实上,回调函数往往是作为一个参数传入,无论它本身结构如何,最终它将赋给一个新的变量。因此,上面第二处如果写成这样应该更好理解:

var f = a.b;

f();// window

setTimeout(f, 1000); //window

另外一种回调函数的方式是用一个匿名函数:

setTimeout(function(){

    a.b(); // Object a

}, 1000);

六.DOM节点相关的函数调用

在HTML代码中直接使用onclick事件处理时,分为几种情况:

1. 直接在事件处理中使用this,则this指向当前元素。

2. 间接在事件处理中使用this,则this指向全局变量Window。

3. 通过方法调用间接使用this,则this取决于使用上面何种方法调用。

<button id="testBtn1" onclick="console.log(this);" >testBtn1</button> <!-- testBtn1 -->

<button id="testBtn2" onclick="(function(){console.log(this);})()" >testBtn2</button> <!-- Window -->

<button id="testBtn3" onclick="b()" >testBtn3</button> <!-- Window -->

<button id="testBtn4" onclick="a.b()" >testBtn4</button> <!-- Object a -->

在Javascript代码中通过事件绑定DOM元素时,也分为几种情况:

1. 通过onclick、addEventListener等函数绑定事件,调用函数中的this指向当前DOM元素。

2. IE中,通过attachEvent函数绑定事件时,调用函数中的this指向全局对象Window。

3.大部分Javascript框架都对事件绑定进行了封装,以兼容IE与其他浏览器的区别。

<button id="testBtn5" >testBtn5</button>

<button id="testBtn6" >testBtn6</button>

<button id="testBtn7" >testBtn7(except IE)</button>

<button id="testBtn8" >testBtn8(Only for IE)</button>

<button id="testBtn9" >testBtn9(jQuery)</button>
var a = {

    b : b

};

function b(){

    console.log(this);

}

document.getElementById("testBtn5").onclick = b; // testBtn5

document.getElementById("testBtn6").onclick = a.b; // testBtn6

document.getElementById("testBtn7").addEventListener("click", b, false); // testBtn7

document.getElementById("testBtn8").attachEvent('onclick', b); // Window

jQuery("#testBtn9").click(b); // testBtn9

七.闭包中的this

由于this对象是在运行时基于函数的运行环境绑定的。在匿名函数中,其运行环境具有全局性,因此this对象通常指向window。然而,由于闭包的特殊性,这种差异可能在一定程度上引起意想不到的结果。

var student = {

    name : "Ken",

    id : "2020",

    getId : function(){

        return function(){

            if(this.id == "2020"){

                this.id = "1010";

           }

           return this.id;

        };

    }

}

console.log(student.getId()());//undefined

如上代码,getId()方法返回了一个匿名函数,匿名函数对this.id处理后并返回。但是由于调用student.getId()()的时候会立刻执行并调用它返回的匿名函数,而此时匿名函数的this对象指向window,因此无法取得其外部作用域的this对象。

如果想要在闭包中访问到外部作用域的变量,可以将其外部作用域的this对象保存到一个闭包变量中。

var student = {

    name : "Ken",

    id : "2020",

    getId : function(){

    var that = this;

    return function(){

            if(that.id == "2020"){

                that.id = "1010";

            }

            return that.id;

        };

    }

}

console.log(student.getId()());//1010

八.严格模式下的this

在ECMAScript的严格模式下,thisArg不会强制转化为一个对象。 因此this的值为null或undefined时不会转化为全局对象,并且基本类型的值不会转化为包装类型对象。

function b(){

    "use strict";

    console.log(this);

}

b(); //undefined

Javascript this 解析的更多相关文章

  1. javascript如何解析json对javascript如何解析json对象并动态赋值到select列表象并动态赋值到select列表

    原文 javascript如何解析json对象并动态赋值到select列表 JSON(JavaScriptObject Notation)一种简单的数据格式,比xml更轻巧.JSON是JavaScri ...

  2. Javascript URI 解析介绍

    URI 在维基百科中对于URI的解释是这样子的: 在计算机术语中,统一资源标识符(Uniform Resource Identifier,或URI)是一个用于标识某一互联网资源名称的字符串. 该种标识 ...

  3. 42套JavaScript深度解析教学视频!合集

    本文首发于:风云社区SCOEE(社区旨在普惠软件.图片.音乐.视频.素材.文档等互联网资源.为大众提供多样化的服务,以及主要涵盖学术科学.电脑技术.文化人文.体育健身等领域的知识和信息,获得用户的支持 ...

  4. javascript的解析顺序

    一.javascript的解析顺序 我们大家所理解的代码的执行顺序都是从上到下的,但是实际上确不是这样的.我们看一下下面的代码. 1 alert(a);2 var a = 1;如果执行顺序是从上到下的 ...

  5. JavaScript预解析

    定义:JavaScript"预解析",可以理解为把变量或函数预先解析到它们被使用的环境中. 通俗点讲,即认为浏览器在正式运行JavaScript代码前, 第一步,会预先根据关键字v ...

  6. javascript的解析过程

    引言: javascript是一种解释型的脚本语言,它不同于java或者c#这种编译语言,不需要编译成游览器可识别的语言,而是由游览器动态解析和执行的.(本身就是游览器可以直接识别,javascrip ...

  7. JavaScript中解析JSON --- json.js 、 json2.js 以及 json3.js的使用区别

    JSON官方(http://www.json.org/)提供了一个json.js,json.js是JSON官方提供的在JavaScript中解析JSON的js包,json.js.json2.js.js ...

  8. 简述javascript的解析与执行

    我们知道浏览器中javascript程序的执行是基于变量与函数的.那么浏览器是如何保存数据,又是如何执行的呢?今天我们一起来探究一下! 0.写在前 最新的 ECMAScript 标准定义了 8 种数据 ...

  9. JavaScript 预解析机制

    首先我们来看一段代码: <script> console.log(a); var a = 10; </script> 此时运行结果为   为什么会显示undefined呢?这就 ...

  10. javascript预解析和作用域

    JavaScript解析过程分为两个阶段: 一是:编译阶段.就是JavaScrip预解析阶段,在这个阶段JavaScript解析器将完成把JavaScript脚本代码转换到字节码; 二是:执行阶段.在 ...

随机推荐

  1. php调试工具总结

    一:XDebug+Webgrind 二:XHProf

  2. spring security源码分析之core包

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring I ...

  3. WPF 之 布局(三)

    六.DockPanel DockPanel定义一个区域,在此区域中,您可以使子元素通过描点的形式排列,这些对象位于 Children 属性中.停靠面板其实就是在WinForm类似于Dock属性的元 素 ...

  4. 【Android 界面效果30】Android中ImageSwitcher结合Gallery展示SD卡中的资源图片

    本文主要是写关于ImageSwitcher结合Gallery组件如何展示SDCard中的资源图片,相信大家都看过API Demo 中也有关于这个例子的,但API Demo 中的例子是展示工程中Draw ...

  5. java 解析XML文档

    Java 解析XML文档 一.解析XML文档方式: 1.DOM方式:将整个XML文档读取到内存中,按照XML文件的树状结构图进行解析. 2.SAX方式:基于事件的解析,只需要加载XML中的部分数据,优 ...

  6. 元数据metadata 对IO有多大影响

    日志文件系统(journaling file systems)可防止系统崩溃时导致的数据不一致问题.对文件系统元数据(metadata)的更改都被保存在一份单独的日志里,当发生 系统崩溃时可以根据日志 ...

  7. Python中的字符串处理

    Python转义字符 在需要在字符中使用特殊字符时,python用反斜杠(\)转义字符.如下表: 转义字符 描述 \(在行尾时) 续行符 \\ 反斜杠符号 \' 单引号 \" 双引号 \a ...

  8. 转: 视频相关的协议族介绍(rtsp, hls, rtmp)

    转自: http://www.zhihu.com/question/20621558   作者:杨华链接:http://www.zhihu.com/question/20621558/answer/1 ...

  9. 4560 NOIP2015 D2T2 子串

    4560 NOIP2015 D2T2 子串  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description 有两 ...

  10. Java程序修改文件名

    package script; import java.io.File; import java.io.IOException; public class Realname { public stat ...