Javascript this 解析

时间:2022-09-26 00:09:11

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&period;js 、 json2&period;js 以及 json3&period;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. CentOS 7&period;0 部署 Django 到运行起来第一个web service

    最近在学习Python,今天发现Django如此强大的web框架,不得不来试一试. 1. 安装Python,官网建议用Python3:

  2. java核心数据结构总结

    JDK提供了一组主要的数据结构的实现,如List.Set.Map等常用结构,这些结构都继承自java.util.collection接口. List接口 List有三种不同的实现,ArrayList和 ...

  3. HybridApp iOS ATS解决方案

    苹果在最近的一次WWDC上提出将在2017年1月1日起强制我们用HTTPS,否则提交App可能会被拒绝.很多ios应用的已经放出支持HTTPS的SDK了.本文主要针对混合式IOS应用提供相关的解决方案 ...

  4. Oracle中的AS和IS

    Oracle中的AS和IS是ORACLE为了方便而设置的同义词基本上没有不同 . 使用规则: 1.在创建存储过程(PROCEDURE)/函数(FUNCTION),以及自定义类型(TPYE)和包(PAC ...

  5. oracle 绑定变量

    “绑定变量”这个词也许对于某些人来说看以来陌生,其实我们在很早的时候就已经开始运用它了. 在java中使用的PrepareStatement对象,大家一定会说这不是将sql语句做预编译操作嘛,被封装的 ...

  6. 高性能IO模型浅析&lpar;转&rpar;

    转自:http://www.cnblogs.com/fanzhidongyzby/p/4098546.html 是我目前看到的解释IO模型最清晰的文章,当然啦,如果想要详细的进一步了解还是继续啃蓝宝书 ...

  7. 一种类似Retrofit声明接口即可实现调用的WebApi客户端框架

    为.Net出力 java有okhttp,还在okhttp这上搞了一个retrofit,.net有HttpClient,但目前我没有发现有类似的retrofit框架.最近在搞mqtt的webApi封装, ...

  8. php使用openssl进行数字签名验证

    <?php /** * Created by PhpStorm. * User: hanks * Date: 6/2/2017 * Time: 6:03 PM */ /* [数字签名] 使用完全 ...

  9. Unicode 与 utf8 utf16 utf32的关系

    Unicode是计算机领域的一项行业标准,它对世界上绝大部分的文字的进行整理和统一编码,Unicode的编码空间可以划分为17个平面(plane),每个平面包含2的16次方(65536)个码位.17个 ...

  10. SSM单元测试时出现:Failed to load ApplicationContext的一种可能解决办法

    SSM单元测试时出现: 严重: Caught exception while allowing TestExecutionListener [org.springframework.test.cont ...