JavaScript 中的this关键字通常是该语言初学者的困惑之源。这种混淆部分源于这样一个事实,即JavaScript 中的this与其他语言(如 Java 或 Python 中的 self)的处理方式不同。为了理解 JavaScript 中更高级的概念或读写 JavaScript 代码,
理解这一点是绝对必要的,这就是为什么我们将在本文中试图阐明这在 JavaScript 中的真正含义。
我们将在本文的大部分时间里参考 JavaScript 中的函数来学习这方面的知识,这就是为什么我们首先要了解一个关于 JavaScript 函数的事实,它可以帮助我们更好地做到这一点。
this 和函数
JavaScript 中的函数本质上是对象。像对象一样,它们可以分配给变量,传递给其他函数并从函数返回。就像对象一样,它们也有自己的属性。这些属性之一是this。this
存储的值是 JavaScript 程序的当前执行上下文。因此,当在函数内部使用时,this的值将根据该函数的定义方式、调用方式和默认执行上下文而改变。注意:这始终包含对单个对象的引用,该对象定义了当前代码行的执行上下文。
在我们深入研究 this 在函数中的行为之前,让我们看看它在函数之外的行为:
全局上下文:
在函数外部编写的一行代码被称为属于全局上下文,并且在这个全局上下文中 this 的值是与全局对象相同。
例如,如果您打开浏览器控制台并在其中输入以下行,然后按回车/回车键:
console.log(this)
您会看到 Window 对象正在登录到控制台。这是因为全局对象在浏览器运行时(例如 Chrome 的运行时)是 Window 对象。
然而,在函数内部,全局上下文可能不再存在,函数可能有自己定义的上下文,因此 this 的值不同。为了理解这一点,让我们把注意力转向函数:
函数,在 JavaScript 中可以通过多种方式调用:
1.函数调用
2.方法调用
3. 构造函数调用
函数调用是指使用函数名称或表达式调用函数的过程,该表达式计算为函数对象,后跟一组打开和关闭的第一个括号(包含括号表示我们正在要求 JavaScript 引擎立即执行该函数)。
例如:
function doSomething() {
// do something here
}
// function invocation
doSomething();
|
doSomething函数里面的this,如果是通过上面的函数调用来调用的话,它的值就是全局对象,也就是浏览器环境中的window对象:
function doSomething(a, b) {
// adds a propone property to the Window object
this .propone = "test value" ;
}
// function invocation
doSomething();
document.write(window.propone);
|
输出:
test value
然而,这并非总是如此。如果 doSomething() 函数在严格模式下运行,它将记录undefined而不是全局窗口对象。这是因为,在严格模式下(由行 : 'use strict';表示), this 的默认值,对于任何函数对象都设置为未定义而不是全局对象。
例如 :
function doSomething() {
// enable the strict mode
'use strict' ;
// logs undefined
document.write( this + ' )
function innerFunction() {
// Also logs undefined, indicating that
// strict mode permeates to inner function scopes
document.write( this )
}
innerFunction();
}
// function invocation
doSomething();
|
输出:
undefined undefined
this与方法调用:
函数,当定义为对象的字段或属性时,称为方法。
JavaScript
let person = {
name : "John" ,
age : 31,
logInfo : function () {
document.write( this .name + " is " + this .age + " years old " );
}
}
// logs John is 31 years old
person.logInfo()
|
输出:
John is 31 years old
在上面的代码示例中,logInfo()是person 对象的一个方法,我们使用对象调用模式来调用它。
也就是说,我们使用属性访问器来访问作为对象一部分的方法。
这样的调用需要使用一个表达式来计算我们的方法所属的对象,以及一个属性访问器(例如:person.logInfo()),后跟一组左括号和右括号。
了解函数调用和方法调用的不同之处至关重要。
这反过来将帮助我们理解this上下文在任何给定函数中可能是什么,因为在每个调用中,this是不同的。
在使用属性访问器调用的此类方法中,this将具有调用对象的值,即this将指向与属性访问器一起使用以进行调用的对象。
例如 :
let add = {
num : 0,
calc : function () {
// logs the add object
document.write( this + ' ' )
this .num
+= 1;
return this .num;
}
};
// logs 1
document.write(add.calc() + ' );
// logs 2
document.write(add.calc());
|
输出:
[object Object] 1 [object Object] 2
在上面的例子中, calc() 是 add 对象的一个方法,因此使用第 9 行和第 10 行的方法调用规则来调用。
我们知道,当使用方法调用模式时,this的值被设置为调用目的。
在这个 calc() 方法中,this 的值被设置为调用对象,在我们的例子中是 add。因此我们可以成功访问 add 的 num 属性。
然而,让我们看看一个主要的混淆点:在嵌套在对象方法中的函数中
会发生什么?
let add = {
num : 0,
calc : function () {
// logs the add object
document.write( this + ' ' )
function innerfunc() {
this .num += 1;
// logs the window object
document.write( this + ' ' );
return this .num
} return innerfunc();
}
};
// logs NaN
document.write(add.calc() + ' );
// logs NaN
document.write(add.calc());
|
输出:
[object Object] [object Window] NaN [object Object] [object Window] NaN
让我们试着理解刚刚发生的事情。
当我们在第 14 行和第 15 行调用 calc() 时,我们正在使用方法调用,它将this设置为添加到 calc() 中。这可以使用第 4 行中的 log 语句来验证。
但是,使用简单的函数调用(第 11 行)从 calc() 方法中调用 innerfunc()。这意味着,在 innerfunc() 内部,this 被设置为没有 num 属性的全局对象,因此获得了 NaN 输出。
我们如何解决这个问题?我们如何从嵌套函数内部的外部方法中保留 this 的值?
一种解决方案是将外部函数的this值分配给要在嵌套函数中使用的变量,如下所示:
let add = {
num : 0,
calc : function () {
// logs the add object
document.write( this + ' ' )
// using thisreference variable to
// store the value of this
thisreference = this ;
function innerfunc()
{
// using the variable to access the
// context of the outer function
thisreference.num += 1;
// logs the add object
document.write(thisreference + ' ' );
return thisreference.num;
}
return innerfunc();
}
};
// logs 1
document.write(add.calc() + ' );
// logs 2
document.write(add.calc());
|
输出:
[object Object] [object Object] 1 [object Object] [object Object] 2
此问题的其他解决方案包括使用 bind()、call() 或 apply(),我们将很快研究这些方法。
当 new 关键字后跟一个函数名和一组左括号和右括号(带或不带参数)时,将执行构造函数调用。
例如: let person1= new People('John', 21);
这里,person1 是新创建的对象,People 是用于创建该对象的构造函数。
构造函数调用是在 JavaScript 中创建对象的几种方法之一。
当我们将 new 关键字与函数名结合使用时,究竟会发生什么?
通过这种方法创建对象基本上涉及五个步骤。让我们用下面的例子来研究它们:
let people = function (name, age) {
this .name = name;
this .age = age;
this .displayInfo = function () {
document.write( this .name + " is " + this .age + " years old" );
}
}
let person1
= new people( 'John' , 21);
// logs John is 21 years old
person1.displayInfo();
|
输出:
John is 21 years old首先,创建一个空对象,它是与 new 一起使用的函数名称的实例(即:people(name, age))。换句话说,它将对象的构造函数属性设置为调用中使用的函数(people(name, age))。
然后,它将构造函数(people)的原型链接到新创建的对象,从而保证这个对象可以继承构造函数的所有属性和方法
然后,在这个新创建的对象上调用构造函数。如果我们回想方法调用,我们会看到类似。因此,在构造函数内部,this获取调用中使用的新创建对象的值。
最后,将创建的对象及其所有属性和方法集返回给 person1
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)