JS 中函数的 this 指针

发布于 2024-03-21 00:45:48 字数 9423 浏览 33 评论 0

this 关键字对于 javascript 初学者,即便是老手可能都是一个比较容易搞晕的东西。本文试图理顺这个问题。

一. this 和自然语言的类比

实际上 js 中的 this 和我们自然语言中的代词有类似性。比如英语中我们写:John is running fast because he is trying to catch the train

注意上面的代词"he",我们当然可以这样写:John is running fast because John is trying to catch the train,这种情况下我们没有使用 this 去重用代替 John。

在 js 中 this 关键字就是一个引用的 shortcut,他指代一个 object,是执行的代码 body 的 context 环境。看下面的代码:

var person = {
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {
        // Notice we use "this" just as we used "he" in the example sentence earlier?:
        console.log(this.firstName + " " + this.lastName);
    	// We could have also written this:​
        console.log(person.firstName + " " + person.lastName);
    }
}

如果我们使用 person.firstName、person.lastName 的话,我们的代码可能会产生歧义。比如,如果有一个全局变量,名字就是 person,那么 person.firstName 可能试图从全局的 person 变量来访问其属性,这可能导致错误,并且难以调试。因此,我们使用 this 关键字,不仅为了代码更美(作为以个 referent),而且为了更加精确而不会产生歧义。类似于刚刚举例的自然语言中,因为代词 he 使得句意更清晰,因为更加清晰地表明我们正在引用一个特定的 John 这个人。

正如代词 he 用于引用存于 context 中无歧义的名词一样,this 关键字用于引用一个对象,而这个对象就是 function(在该函数中,使用了 this 指针) 所绑定的对象。(the this keyword is similarly used to refer to an object that the function(where this is used) is bound to.)

二. this 基础

2.1 什么是执行上下文(execution context)?

在 js 中,所有的函数体(function body) 都能访问 this 关键字. this keyword 就是函数执行的 context.默认情况下,this 引用着调用该函数的那个对象(也就是 thisObj.thisFunction 中的 thisObj)(this is a reference to the object on which a particular function is called),在 js 中,所有的函数都会被绑定到一个 object.然而,我们可以使用 call(), apply() 来在运行时变更这种 binding 关系。

首先,我们需要澄清的是:js 中所有的函数实际上都是"methods".也就是说所有的 function 都是某个对象的属性。虽然我们可以定义看起来像是独立自由的函数,但是实际上这时候,这些含糊被隐式地被创建为 window object 的属性 properties(在 node 环境中是其他的 object,可能是 process)。这也就意味着,即使我们创建一个自由独立的 function 而没有显然指定其 context,我们也可以以 window 属性的方式来调用该函数。

// Define the free-floating function.
function someMethod(){ ... }

// Access it as a property of Window.
window.someMethod();

既然我们知道了 js 的所有函数度作为一个对象的属性而存在,我们可以具体探讨下执行上下文的默认绑定这个概念了。默认情况下,一个js 函数在该函数所属的对象的上下文中执行(js function is executed in the context of the object for which it is a property).也就是说,在函数 body 中,this 关键词就是对父亲对象的引用,看看下面的代码:

//  "this" keyword within the sayHello() method is a reference to the sarah object
sarah.sayHello();
// "this" keyword within the getScreenResolution() function is a reference to the window object (since unbound functions are implicitly bound to the global scope)
getScreenResolution();

以上是默认的情况,除此之外,js 提供了一种变更任何一个 method 的执行上下文的机制: call() 或者 apply().这两个函数都能用于绑定"this"关键字到一个明确的 context(object) 上去。

method.call( newThisContext, Param1, ..., Param N )
method.apply( newThisContext, [ Param1, ..., Param N ] );

再看一个复杂一点的代码案例:

<!DOCTYPE html>
<html>
<head>
    <title>Changing Execution Context In JavaScript</title>

    <script type="text/javascript">
        // Create a global variable for context (this lives in the
        // global scope - window).
        var context = "Global (ie. window)";
        // Create an object.
        var objectA = {
            context: "Object A"
        };
        // Create another object.
        var objectB = {
            context: "Object B"
        };
        // -------------------------------------------------- //
        // -------------------------------------------------- //
        // Define a function that uses an argument AND a reference
        // to this THIS scope. We will be invoking this function
        // using a variety of approaches.
        function testContext( approach ){
            console.log( approach, "==> THIS ==>", this.context );
        }
        // -------------------------------------------------- //
        // -------------------------------------------------- //
        // Invoke the unbound method with standard invocation.
        testContext( "testContext()" );
        // Invoke it in the context of Object A using call().
        testContext.call(
            objectA,
            ".call( objectA )"
        );
        // Invoke it in the context of Object B using apply().
        testContext.apply(
            objectB,
            [ ".apply( objectB )" ]
        );
        // -------------------------------------------------- //
        // -------------------------------------------------- //
        // Now, let's set the test method as an actual property
        // of the object A.
        objectA.testContext = testContext;
        // -------------------------------------------------- //
        // -------------------------------------------------- //
        // Invoke it as a property of object A.
        objectA.testContext( "objectA.testContext()" );
        // Invoke it in the context of Object B using call.
        objectA.testContext.call(
            objectB,
            "objectA.testContext.call( objectB )"
        );
        // Invoke it in the context of Window using apply.
        objectA.testContext.apply(
            window,
            [ "objectA.testContext.apply( window )" ]
        );
    </script>
</head>
<body>
    <!-- Left intentionally blank. -->
</body>
</html>

以上代码的输出如下:

testContext() ==> THIS ==> Global (ie. window)
.call( objectA ) ==> THIS ==> Object A
.apply( objectB ) ==> THIS ==> Object B
objectA.testContext() ==> THIS ==> Object A
objectA.testContext.call( objectB ) ==> THIS ==> Object B
objectA.testContext.apply( window ) ==> THIS ==> Global (ie. window)

三. 另一个角度来看 this-functionObj.this

首先,需要知道的是 js 中的所有函数都有 peroperties,就像 objects 都有 properties 一样,因为 function 本身也是一个 object 对象。this 可以看作是 this-functionObj 的属性,并且只有当该函数执行时,该函数对象的 this 属性将会被赋值,it gets the this property ---- a variable with the value of the object that invokes the function where this is used。

this 总是指向或者说引用(并包含了对应的 value) 一个对象,并且this 往往在一个 function 或者说 method 中来使用。注意:虽然在 global scope 中我们可以不在 function body 中使用 this,而是直接在 global scope 中使用 this(实际上指向了 window),但是如果我们在 strict mode 的话,在 global function 中,或者说 没有绑定任何 object 的匿名函数中,如果使用 this, 那么这个 this 将是 undefined 值

假设 this 在一个 function A 中被使用,那么 this 就将引用着调用 function A 的那个对象。我们需要这个 this 来访问调用 function A 对象的 method 和 property.特别地,有些情况下我们不知道调用者对象的名称,甚至有时候调用者对象根本没有名字,这时就必须用 this 关键字了!

var person = {
	firstName   :"Penelope",
	lastName    :"Barrymore",
	showFullName:function () {
		console.log (this.firstName + " " + this.lastName);
	}
}
person.showFullName (); // Penelope Barrymore

再看一个 jquery 事件处理函数中使用 this 关键字的常见例子:

// A very common piece of jQuery code
$ ("button").click (function (event) {
	// $(this) will have the value of the button ($("button")) object
	// because the button object invokes the click () method, this 指向 button
	console.log ($ (this).prop ("name"));
});

3.1 深入一步理解 this

我们先抛出一个心法: this 不会有 value,直到一个 object invoke 了这个函数(this 在这个函数中使用).为了行文方便,我们将使用 this 关键字的函数称为 thisFunction.

虽然默认情况下,this 都会引用定义了 this(也就是有 this 引用)的对象,但是只到一个对象调用了 thisFunction,这个 this 指针才会被赋值。而这个 this value 只决定于调用了 thisFunction 的对象。尽管默认情况下 this 的值就是 invoking ojbect(xxObj.thisFunction),但是我们也可以通过 xxObj.thisFunction.call(yyObj,parameters), apply() 等方式来修改 this 的默认值!

在 global scope 中使用 this

在 global scope 中,当代码在浏览器中执行时,所有的全局 variable 和 function 都被定义在 window object 上,因此,在一个全局函数中当使用 this 时,this 是引用了全局的 window 对象的(注意必须是非 stric mode 哦),而 window 对象则是整个 js 应用或者说 web page 的容器

四. DOM 事件处理函数中的 this & 内联事件中的 this

4.1 DOM 事件处理函数

当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于 addEventListener 事件)

// 被调用时,将关联的元素变成蓝色
function bluify(e){
  //在控制台打印出所点击元素
  console.log(this);
  //阻止事件冒泡
  e.stopPropagation();
  //阻止元素的默认事件
  e.preventDefault();      
  this.style.backgroundColor = '#A5D9F3';
}

// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');

// 将 bluify 作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){
  elements[i].addEventListener('click', bluify, false);
}

4.2 内联事件

当代码被内联处理函数调用时,它的 this 指向监听器所在的 DOM 元素

button onclick="hello(this)">
    Show inner this
</button>
function hello(obj){
	console.info(obj); //输出 DOM 对象
}

五. 函数中的 this 指针

function Hello(a, b) {
    this.a = a;
    this.b = b;
    console.info(this);
}

let hello1 = new Hello('1','2');//当作为构造函数时,this 指向当前创建的对象
Hello(); // 作为函数调用时 this 指向 window

最后,需要牢记:Always remember that this is assigned the value of the object that invoked the this Function。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

π浅易

暂无简介

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文