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. MySQL无视密码进入Server

    在[mysqld]的段中加上一句:skip-grant-tables 如下 [mysqld] skip-grant-tables 即可不输入密码就可以进入mysql server,然后就可以随便修改数 ...

  2. 网络流最经典的入门题 各种网络流算法都能AC。 poj 1273 Drainage Ditches

    Drainage Ditches 题目抽象:给你m条边u,v,c.   n个定点,源点1,汇点n.求最大流.  最好的入门题,各种算法都可以拿来练习 (1):  一般增广路算法  ford() #in ...

  3. iOS 关于流媒体 的初级认识与使用

    1.流媒体指在Internet/Intranet中使用流式传输技术的连续时基媒体,如:音频.视频或多媒体文件.流式媒体在播放前并不下载整个文件,只将开始部分内容存入内存,流式媒体的数据流随时传送随时播 ...

  4. 【Linux/Ubuntu学习3】解决ubuntu解压windows生成的zip文件时乱码问题

    在windows上压缩的文件,是以系统默认编码中文来压缩文件.由于zip文件中没有声明其编码,所以linux上的unzip一般以默认编码解压,中文文件名会出现乱码. 虽然2005年就有人把这报告为bu ...

  5. 要检测两个C文件的代码的抄袭情况

    将抄袭部分输出 如果只是变量名替换了 也算抄袭 如果输入了一些干扰代码以防止被检测出来 也算抄袭专业程序代写c++程序代写

  6. 推荐一款好用的项目管理工具:project

    Microsoft Project (MSP)是微软开发的一个国际上享有盛誉的通用的项目管理工具软件. 在项目管理的时候,这个软件可以帮你定制时间计划,还有其它很多好用的功能. 2010版本的下载传送 ...

  7. Visual Studio 扩展包(.vsix)制作

    前言:上篇介绍了 Visual Studio扩展工具添加与卸载,本编要介绍的是Visual Studio 扩展包(.vsix)的制作. 方法: ①.下载并安装Visual Studio 2010 SD ...

  8. .NET中的Queue和Stack

    1.ArrayList类 ArrayList类主要用于对一个数组中的元素进行各种处理.在ArrayList中主要使用Add.Remove.RemoveAt.Insert四个方法对栈进行操作.Add方法 ...

  9. (转)Linux IO模式及 select、poll、epoll详解

    本文为转载,并作了部门调整.修改. [原文出处:https://segmentfault.com/a/1190000003063859] 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么 ...

  10. ionic Modal

    在ionic中,modal也是添加控制器写服务的~ 在modal.html页面中增加控制器:ng-controller="aboutCtrl"记住要给这个添加控制器.头部使其关闭按 ...