问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

我不理解js中this的详细用法,谁可以帮我详细回答一下。

发布网友 发布时间:2022-04-25 11:52

我来回答

2个回答

懂视网 时间:2022-04-07 14:00

我喜欢在JS中更改函数执行上下文的指向,也称为 this 指向。

例如,咱们可以在类数组对象上使用数组方法:

const reduce = Array.prototype.reduce;

function sumArgs() {
 return reduce.call(arguments, (sum, value) => {
 return sum += value;
 });
}

sumArgs(1, 2, 3); // => 6

另一方面,this 很难把握。

咱们经常会发现自己用的 this 指向不正确。下面的教你如何简单地将 this 绑定到所需的值。

【相关课程推荐:JavaScript视频教程】

在开始之前,我需要一个辅助函数execute(func),它仅执行作为参数提供的函数。

function execute(func) {
 return func();
}

execute(function() { return 10 }); // => 10

现在,继续理解围绕this错误的本质:方法分离。

1. 方法分离问题

假设有一个类Person包含字段firstName和lastName。此外,它还有一个方法getFullName(),该方法返回此人的全名。如下所示:

function Person(firstName, lastName) {
 this.firstName = firstName;
 this.lastName = lastName;

 this.getFullName = function() {
 this === agent; // => true
 return `${this.firstName} ${this.lastName}`;
 }
}

const agent = new Person('前端', '小智');
agent.getFullName(); // => '前端 小智'

可以看到Person函数作为构造函数被调用:new Person('前端', '小智')。 函数内部的 this 表示新创建的实例。

getfullname()返回此人的全名:'前端 小智'。正如预期的那样,getFullName()方法内的 this 等于agent。

如果辅助函数执行agent.getFullName方法会发生什么:

execute(agent.getFullName); // => 'undefined undefined'

执行结果不正确:'undefined undefined',这是 this 指向不正确导致的问题。

现在在getFullName() 方法中,this的值是全局对象(浏览器环境中的 window )。 this 等于 window,${window.firstName} ${window.lastName} 执行结果是 'undefined undefined'。

发生这种情况是因为在调用execute(agent.getFullName)时该方法与对象分离。 基本上发生的只是常规函数调用(不是方法调用):

execute(agent.getFullName); // => 'undefined undefined'

// 等价于:

const getFullNameSeparated = agent.getFullName;
execute(getFullNameSeparated); // => 'undefined undefined'

这个就是所谓的方法从它的对象中分离出来,当方法被分离,然后执行时,this 与原始对象没有连接。

1、为了确保方法内部的this指向正确的对象,必须这样做

2、以属性访问器的形式执行方法:agent.getFullName()或者静态地将this绑定到包含的对象(使用箭头函数、.bind()方法等)

方法分离问题,以及由此导致this指向不正确,一般会在下面的几种情况中出现:

回调

// `methodHandler()`中的`this`是全局对象
setTimeout(object.handlerMethod, 1000);

在设置事件处理程序时

// React: `methodHandler()`中的`this`是全局对象
<button onClick={object.handlerMethod}>
 Click me
</button>

接着介绍一些有用的方法,即如果方法与对象分离,如何使this指向所需的对象。

2. 关闭上下文

保持this指向类实例的最简单方法是使用一个额外的变量self:

function Person(firstName, lastName) {
 this.firstName = firstName;
 this.lastName = lastName;
 const self = this;
 this.getFullName = function() {
 self === agent; // => true
 return `${self.firstName} ${self.lastName}`;
 }
}
const agent = new Person('前端', '小智');
agent.getFullName(); // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'

getFullName()静态地关闭self变量,有效地对this进行手动绑定。

现在,当调用execute(agent.getFullName)时,一切工作正常,因为getFullName()方法内 this 总是指向正确的值。

3. 使用箭头函数

有没有办法在没有附加变量的情况下静态绑定this? 是的,这正是箭头函数的作用。

使用箭头函数重构Person:

function Person(firstName, lastName) {
 this.firstName = firstName;
 this.lastName = lastName;

 this.getFullName = () => `${this.firstName} ${this.lastName}`;
}

const agent = new Person('前端', '小智');

agent.getFullName(); // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'

箭头函数以词法方式绑定this。 简单来说,它使用来自其定义的外部函数this的值。

建议在需要使用外部函数上下文的所有情况下都使用箭头函数。

4. 绑定上下文

现在让咱们更进一步,使用ES6中的类重构Person。

class Person {
 constructor(firstName, lastName) {
 this.firstName = firstName;
 this.lastName = lastName;
 }

 getFullName() {
 return `${this.firstName} ${this.lastName}`;
 }
}

const agent = new Person('前端', '小智');

agent.getFullName(); // => '前端 小智'
execute(agent.getFullName); // => 'undefined undefined'

不幸的是,即使使用新的类语法,execute(agent.getFullName)仍然返回“undefined undefined”。

在类的情况下,使用附加的变量self或箭头函数来修复this的指向是行不通的。

但是有一个涉及bind()方法的技巧,它将方法的上下文绑定到构造函数中:

class Person {
 constructor(firstName, lastName) {
 this.firstName = firstName;
 this.lastName = lastName;

 this.getFullName = this.getFullName.bind(this);
 }

 getFullName() {
 return `${this.firstName} ${this.lastName}`;
 }
}

const agent = new Person('前端', '小智');

agent.getFullName(); // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'

构造函数中的this.getFullName = this.getFullName.bind(this)将方法getFullName()绑定到类实例。

execute(agent.getFullName) 按预期工作,返回'前端 小智'。

5. 胖箭头方法

bind 方式有点太过冗长,咱们可以使用胖箭头的方式:

class Person {
 constructor(firstName, lastName) {
 this.firstName = firstName;
 this.lastName = lastName;
 }

 getFullName = () => {
 return `${this.firstName} ${this.lastName}`;
 }
}

const agent = new Person('前端', '小智');

agent.getFullName(); // => '前端 小智'
execute(agent.getFullName); // => '前端 小智'

胖箭头方法getFullName =() =>{…}绑定到类实例,即使将方法与其对象分离。

这种方法是在类中绑定this的最有效和最简洁的方法。

6. 总结

与对象分离的方法会产生 this 指向不正确问题。静态地绑定this,可以手动使用一个附加变量self来保存正确的上下文对象。然而,更好的替代方法是使用箭头函数,其本质上是为了在词法上绑定this。

在类中,可以使用bind()方法手动绑定构造函数中的类方法。当然如果你不用使用 bind 这种冗长方式,也可以使用简洁方便的胖箭头表示方法。

原文:https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter5.md

本文来自 js教程 栏目,欢迎学习!

热心网友 时间:2022-04-07 11:08

在面向对象编程语言中,对于this关键字我们是非常熟悉的。比如C++、C#和Java等都提供了这个关键字,虽然在开始学习的时候觉得比较难,但只要理解了,用起来是非常方便和意义确定的。JavaScript也提供了这个this关键字,不过用起来就比经典OO语言中要"混乱"的多了。

下面就来看看,在JavaScript中各种this的使用方法有什么混乱之处?

1、在HTML元素事件属性中inline方式使用this关键字:
<div onclick="
// 可以在里面使用this

">division element</div>
<div onclick="
// 可以在里面使用this

">division element</div>
我们一般比较常用的方法是在此使用:javascirpt: EventHandler(this),这样的形式。不过这里其实可以写任何合法的JavaScript语句,要是高兴在此定义个类也可以(不过将会是个内部类)。这里的原理是脚本引擎生成了一个div实例对象的匿名成员方法,而onclick指向这个方法。

2、用DOM方式在事件处理函数中使用this关键字:
<div id="elmtDiv">division element</div>
<mce:script language="javascript"><!--
var div = document.getElementById('elmtDiv');
div.attachEvent('onclick', EventHandler);

function EventHandler()
{
// 在此使用this
}

// --></mce:script>
<div id="elmtDiv">division element</div>
<mce:script language="javascript"><!--
var div = document.getElementById('elmtDiv');
div.attachEvent('onclick', EventHandler);

function EventHandler()
{
// 在此使用this
}

// --></mce:script>
这时的EventHandler()方法中的this关键字,指示的对象是IE的window对象。这是因为EventHandler只是一个普通的函数,对于attachEvent后,脚本引擎对它的调用和div对象本身没有任何的关系。同时你可以再看看EventHandler的caller属性,它是等于null的。如果我们要在这个方法中获得div对象引用,应该使用:this.event.srcElement。

3、用DHTML方式在事件处理函数中使用this关键字:
<div id="elmtDiv">division element</div>
lt;mce:script language="javascript"><!--
var div = document.getElementById('elmtDiv');
div.onclick = function()
{
// 在此使用this
};

/ --></mce:script>
<div id="elmtDiv">division element</div>
<mce:script language="javascript"><!--
var div = document.getElementById('elmtDiv');
div.onclick = function()
{
// 在此使用this
};

// --></mce:script>
这里的this关键字指示的内容是div元素对象实例,在脚本中使用DHTML方式直接为div.onclick赋值一个EventHandler的方法,等于为div对象实例添加一个成员方法。这种方式和第一种方法的区别是,第一种方法是使用HTML方式,而这里是DHTML方式,后者脚本解析引擎不会再生成匿名方法。

4、类定义中使用this关键字:
function JSClass()
{
var myName = 'jsclass';
this.m_Name = 'JSClass';
}

JSClass.prototype.ToString = function()
{
alert(myName + ', ' + this.m_Name);
};

var jc = new JSClass();
jc.ToString();
function JSClass()
{
var myName = 'jsclass';
this.m_Name = 'JSClass';
}

JSClass.prototype.ToString = function()
{
alert(myName + ', ' + this.m_Name);
};

var jc = new JSClass();
jc.ToString();
这是JavaScript模拟类定义中对this的使用,这个和其它的OO语言中的情况非常的相识。但是这里要求成员属性和方法必须使用this关键字来引用,运行上面的程序会被告知myName未定义。

5、为脚本引擎内部对象添加原形方法中的this关键字:
function.prototype.GetName = function()
{
var fnName = this.toString();
fnName = fnName.substr(0, fnName.indexOf('('));
fnName = fnName.replace(/^function/, '');
return fnName.replace(/(^\s+)|(\s+$)/g, '');
}
function foo(){}
alert(foo.GetName());
function.prototype.GetName = function()
{
var fnName = this.toString();
fnName = fnName.substr(0, fnName.indexOf('('));
fnName = fnName.replace(/^function/, '');
return fnName.replace(/(^\s+)|(\s+$)/g, '');
}
function foo(){}
alert(foo.GetName());
这里的this指代的是被添加原形的类的实例,和4中类定义有些相似,没有什么太特别的地方。

6、结合2&4,说一个比较迷惑的this关键字使用:
view plaincopy to clipboardprint?
function JSClass()
{
this.m_Text = 'division element';
this.m_Element = document.createElement('DIV');
this.m_Element.innerHTML = this.m_Text;

this.m_Element.attachEvent('onclick', this.ToString);
}

JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element);
}

JSClass.prototype.ToString = function()
{
alert(this.m_Text);
};

var jc = new JSClass();
jc.Render();
jc.ToString();
function JSClass()
{
this.m_Text = 'division element';
this.m_Element = document.createElement('DIV');
this.m_Element.innerHTML = this.m_Text;

this.m_Element.attachEvent('onclick', this.ToString);
}

JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element);
}

JSClass.prototype.ToString = function()
{
alert(this.m_Text);
};

var jc = new JSClass();
jc.Render();
jc.ToString();
我就说说结果,页面运行后会显示:"division element",确定后点击文字"division element",将会显示:"undefined"。

7、CSS的expression表达式中使用this关键字:
<table width="100" height="100">
<tr>
<td>
<div style="width: expression(this.parentElement.width);
height: expression(this.parentElement.height);">
division element</div>
</td>
</tr>
</table>
<table width="100" height="100">
<tr>
<td>
<div style="width: expression(this.parentElement.width);
height: expression(this.parentElement.height);">
division element</div>
</td>
</tr>
</table>

这里的this看作和1中的一样就可以了,它也是指代div元素对象实例本身。

8、函数中的内部函数中使用this关键字:
view plaincopy to clipboardprint?
function OuterFoo()
{
this.Name = 'Outer Name';

function InnerFoo()
{
var Name = 'Inner Name';
alert(Name + ', ' + this.Name);
}
return InnerFoo;
}
OuterFoo()();
function OuterFoo()
{
this.Name = 'Outer Name';

function InnerFoo()
{
var Name = 'Inner Name';
alert(Name + ', ' + this.Name);
}
return InnerFoo;
}
OuterFoo()();
运行结果显示是:"Inner Name, Outer Name"。按我们在2中的讲解,这里的结果如果是"Inner Name, undefined"似乎更合理些吧?但是正确的结果确实是前者,这是由于JavaScript变量作用域的问题决定的,详细了解推荐参看"原来JScript中的关键字'var'还是有文章的"一文及回复。

归纳起来,JavaScript中的this用法有以下3种(详细用法参原文):
1.在HTML元素事件属性 或 CSS的expression表达式 中inline方式使用this关键字——对应原文的1、7
2.在事件处理函数中使用this关键字——对应原文的2、3
其中可分为两种方式
(1)DOM方式——此种方式的结果是this指向窗口(window)对象
(2)DHTML方式——此种方式的结果是this指向div元素对象实例
3.在类定义中使用this关键字并在其 内部函数 或 成员函数(主要是prototype产生)中使用——对应原文的4、5、8
需要说明的是,在函数也是个对象,因此需要区分 变量定义 和 成员变量定义,如下:
view plaincopy to clipboardprint?
var variableName; //变量定义
//作用域:函数定义范围内
//使用方法:直接使用variableName
this.varName; //成员变量定义
//作用域:函数对象定义范围内及其成员函数中
//使用方法:this.varName
var variableName; //变量定义
//作用域:函数定义范围内
//使用方法:直接使用variableName
this.varName; //成员变量定义
//作用域:函数对象定义范围内及其成员函数中
//使用方法:this.varName

以上归纳出的三类this的使用方法中,第一种比较容易理解,这里对原文中第6点提到的程序进行了测试和改进如下,以说明上述后两种使用方法:

view plaincopy to clipboardprint?
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Test "this"</title>
<mce:script type="text/javascript"><!--
function JSClass()
{
var varText = "func variable!"; //函数中的普通变量
this.m_Text = 'func member!'; //函数类的成员变量
this.m_Element = document.createElement('DIV'); //成员变量,创建一个div对象
this.m_Element.innerHTML = varText; //使用函数的普通变量
this.m_Element.attachEvent('onclick', this.ToString); //给这个对象的事件连上处理函数
this.newElement = document.createElement('DIV');
this.newElement.innerHTML = "new element";
this.newElement.m_Text = "new element text!"; //给创建的对象建个成员
this.newElement.onclick = function()
{
alert(this.m_Text); //指向div对象的成员
};
}

JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element); //把div对象挂在窗口上
document.body.appendChild(this.newElement);
}

JSClass.prototype.ToString = function()
{
alert(this.m_Text); //指向窗口(window)对象
};

function initialize(){
var jc = new JSClass();
jc.Render();
jc.ToString(); //里面的this指向JSClass类的实例,里面有m_Text成员
}

// --></mce:script>
</head>
<body>
<mce:script type="text/javascript"><!--
initialize();

// --></mce:script>
</body>
</html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Test "this"</title>
<mce:script type="text/javascript"><!--
function JSClass()
{
var varText = "func variable!"; //函数中的普通变量
this.m_Text = 'func member!'; //函数类的成员变量
this.m_Element = document.createElement('DIV'); //成员变量,创建一个div对象
this.m_Element.innerHTML = varText; //使用函数的普通变量
this.m_Element.attachEvent('onclick', this.ToString); //给这个对象的事件连上处理函数
this.newElement = document.createElement('DIV');
this.newElement.innerHTML = "new element";
this.newElement.m_Text = "new element text!"; //给创建的对象建个成员
this.newElement.onclick = function()
{
alert(this.m_Text); //指向div对象的成员
};
}

JSClass.prototype.Render = function()
{
document.body.appendChild(this.m_Element); //把div对象挂在窗口上
document.body.appendChild(this.newElement);
}

JSClass.prototype.ToString = function()
{
alert(this.m_Text); //指向窗口(window)对象
};

function initialize(){
var jc = new JSClass();
jc.Render();
jc.ToString(); //里面的this指向JSClass类的实例,里面有m_Text成员
}

// --></mce:script>
</head>
<body>
<mce:script type="text/javascript"><!--
initialize();

// --></mce:script>
</body>
</html>

上面的代码执行结果是:
页面加载时,弹出对话框,输出func member!
页面上显示
func variable!
new element
单击func variable时,弹出对话框,显示undefined
——因为这时toString函数里的this指针指向window
单击new element时,弹出对话框显示new element text!
——因为这时toString函数里的this指针指向div元素,而该元素已经定义了m_Text成员(this.newElement.m_Text = "new element text!")追问说的太麻木了。

追答好吧,我承认这段讲解是呆板了些,你也可以不看。
但你必须明白,要想成为高手,就必须看好多枯燥的东西。
生动的东西只适合入门
其次,你是被这段代码的长度吓住了,它只是列了许多代码,显得长了,但讲解的条理和内容,还算清析全面。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
2024年四川570分能考上什么大学? 2024年四川文科560分能考上什么大学? 2024年广东561分能考上什么大学? 2024年江苏历史类561分能考上什么大学? 在形ABC中,角A.B.C所对的边分别为a.b.c,且acosB+bcosA=2c cos 求... ...B、C的对边分别为a、b、c,已知acosB+bcosA=2(bcosC+ccosB).(1... ...b、c的对边分别为a、b、c,且acosb+bcosa=2cosc 在△ ABC中,角A、B、C所对的边分别为a、b、c,已知b=cosA,c=2cosB,试... 在△ABC中,角A,B,C所对的边分别为a,b,c,且满足bcosA+acosB=2ccosC,△... ...对应的边分别为a、b、c,且满足aCOSB+bcosA=2cCOSC... js中this关键字如何应用? 好听的女网名(学生类) javaScript里this的使用 好听的女生网名有那些? Javascript中this是怎么用的? js中this的用法 有什么好听的女生网名吗,介绍介绍的,, 帮我起个好听的女生网名吧? javascript中的this指的对象是什么? javascript中this用法 js中的this怎么用? js中的this如何使用 javascript中的this到底指什么? 好听女网名 Javascript中“this” 的作用? excel中给出一个百分比,如何根据这个比例做出比例图,左右两边颜色可以调... excel 07 百分比条形图,怎么显示大于100%的条形? 如何用excel做出投票网站里一样的百分比条形图 用EXCEL如何得出下图的条形百分比图 excel制作百分比进度条,不用公式的那种 JavaScript的this用法,真心被弄晕了 电影学 英语 怎么说 如何选择合适的电影学英语 请推荐几部可供学英语的电影,谢谢 推荐几部适合学英语的电影~ 看电影学英语 怎样看电影学英语? 好看又可以学英语的美国电影大片 看电影学英语的软件? 我的小米8内屏坏啦售后服务说得里外屏都得换得650元,刚用了7,8个月的手机 WH是什么意思? wh什么意思? Wh是wh是什么意思? 电力系统中wh是什么意思 13.14wh是什么意思? 电池上的WH表示什么意思 充电宝的Wh是什么意思 WH 是什么意思?谢谢 手机电池wh代表什么意思? 电气系统图里面的WH是什么意思