搜索
首页web前端js教程Javascript this 的一些学习总结_javascript技巧

1.1.1 摘要
相信有C++、C#或Java等编程经验的各位,对于this关键字再熟悉不过了。由于Javascript是一种面向对象的编程语言,它和C++、C#或Java一样都包含this关键字,接下来我们将向大家介绍Javascript中的this关键字。
本文目录
全局代码中的this
函数中的this
引用类型
函数调用以及非引用类型
引用类型以及this的null值
函数作为构造器被调用时this的值
手动设置函数调用时this的值

1.1.2 正文

由于许多面向对象的编程语言都包含this关键字,我们会很自然地把this和面向对象的编程方式联系在一起,this通常指向利用构造器新创建出来的对象。而在ECMAScript中,this不仅仅只用来表示创建出来的对象,也是执行上下文的一个属性:
复制代码 代码如下:

activeExecutionContext = {
// Variable object.
VO: {...},
this: thisValue
};

全局代码中的this
复制代码 代码如下:

// Global scope
// The implicit property of
// the global object
foo1 = "abc";
alert(foo1); // abc
// The explicit property of
// the global object
this.foo2 = "def";
alert(foo2); // def
// The implicit property of
// the global object
var foo3 = "ijk";
alert(foo3); // ijk

前面我们通过显式和隐式定义了全局属性foo1、foo2和foo3,由于this在全局上下文中,所以它的值是全局对象本身(在浏览器中是window object);接下来我们将介绍函数中的this。
函数中的this
当this在函数代码中,情况就复杂多了,并且会引发很多的问题。
函数代码中this值的第一个特性(同时也是最主要的特性)就是:它并非静态的绑定在函数上。
正如此前提到的,this的值是在进入执行上下文(Excution context)的阶段确定的,并且在函数代码中的话,其值每次都不尽相同。
然而,一旦进入执行代码阶段,其值就不能改变了。如果要想给this赋一个新的值是不可能的,因为在那时this根本就不是变量了。
接下来,我们通过具体的例子说明函数中的this。
首先我们定义两个对象foo和person,foo包含一个属性name,而person包含属性name和方法say(),具体的定义如下:
复制代码 代码如下:

// Defines foo object.
var foo = {
name: "Foo"
};
// Defines person object.
var person = {
name: "JK_Rush",
say: function() {
alert(this === person);
alert("My name is " + this.name);
}
};
person.say(); // My name is JK_Rush
// foo and person object refer to
// the same function say
foo.say = person.say;
foo.say(); // My name is Foo.

通过上面的代码,我们发现调用person的say()方法时,this指向person对象,当通过赋值方式使得foo的say()方法指向peson中的say()方法时。我们调用foo的say()方法,发现this不是指向person对象,而不是指向foo对象,这究竟是什么原因呢?
首先,我们必须知道this的值在函数中是非静态的,它的值确定在函数调用时,具体代码执行前,this的值是由激活上下文代码的调用者决定的,比如说,调用函数的外层上下文;更重要的是,this的值是由调用表达式的形式决定的,所以说this并非静态的绑定在函数上。
由于this并非静态地绑定在函数上,那么我们是否可以在函数中动态地修改this的值呢?
复制代码 代码如下:

// Defines foo object.
var foo = {
name: "Foo"
};
// Defines person object.
var person = {
name: "JK_Rush",
say: function() {
alert(this === person);
this = foo; // ReferenceError
alert("My name is " + this.name);
}
};
person.say(); // My name is JK_Rush

现在我们在方法say()中,动态地修改this的值,当我们重新执行以上代码,发现this的值引用错误。这是由于一旦进入执行代码阶段(函数调用时,具体代码执行前),this的值就确定了,所以不能改变了。
引用类型
前面我们提到this的值是由激活上下文代码的调用者决定的,更重要的是,this的值是由调用表达式的形式决定的;那么表达式的形式是如何影响this的值呢?
首先,让我们介绍一个内部类型——引用类型,它的值可以用伪代码表示为一个拥有两个属性的对象分别是:base属性(属性所属的对象)以及该base对象中的propertyName属性:
复制代码 代码如下:

// Reference type.
var valueOfReferenceType = {
base: mybase,
propertyName : 'mybasepropertyName'
};

引用类型的值只有可能是以下两种情况:
当处理一个标识符的时候
或者进行属性访问的时候
标识符其实就是变量名、函数名、函数参数名以及全局对象的未受限的属性。
复制代码 代码如下:

// Declares varible.
var foo = 23;
// Declares a function
function say() {
// Your code.
}

中间过程中,对应的引用类型如下:
复制代码 代码如下:

// Reference type.
var fooReference = {
base: global,
propertyName: 'foo'
};
var sayReference = {
base: global,
propertyName: 'say'
};

我们知道Javascript中属性访问有两种方式:点符号和中括号符号:
复制代码 代码如下:

// Invokes the say method.
foo.say();
foo['say']();

由于say()方法是标识符,所以它对应于foo对象引用类型如下:
复制代码 代码如下:

// Reference type.
var fooSayReference = {
base: foo,
propertyName: 'say'
};

我们发现say()方法的base属性值为foo对象,那么它对应的this属性也将指向foo对象。
假设,我们直接调用say()方法,它对应的引用类型如下:
复制代码 代码如下:

// Reference type.
var sayReference = {
base: global,
propertyName: 'say'
};

由于say()方法的base属性值为global(通常来说是window object),那么它对应的this属性也将指向global。
函数上下文中this的值是函数调用者提供并且由当前调用表达式的形式而定的。如果在调用括号()的左边有引用类型的值,那么this的值就会设置为该引用类型值的base对象。 所有其他情况下(非引用类型),this的值总是null。然而,由于null对于this来说没有任何意义,因此会隐式转换为全局对象。
函数调用以及非引用类型
前面我们提到,当调用括号左侧为非引用类型的时,this的值会设置为null,并最终隐式转换为全局对象。
现在我们定义了一个匿名自执行函数,具体实现如下:
复制代码 代码如下:

// Declares anonymous function
(function () {
alert(this); // null => global
})();

由于括号()左边的匿名函数是非引用类型对象(它既不是标识符也不属于属性访问),因此,this的值设置为全局对象。
复制代码 代码如下:

// Declares object.
var foo = {
bar: function () {
alert(this);
}
};
(foo.bar)(); // foo.
(foo.bar = foo.bar)(); // global?
(false || foo.bar)(); // global?
(foo.bar, foo.bar)(); // global

这里注意到四个表达式中,只有第一个表达式this是指向foo对象的,而其他三个表达式则执行global。
现在我们又有疑问了:为什么属性访问,但是最终this的值不是引用类型对象而是全局对象呢?
我们注意到表达式二是赋值(assignment operator),与表达式一组操作符不同的是,它会触发调用GetValue方法(参见11.13.1中的第三步)。 最后返回的时候就是一个函数对象了(而不是引用类型的值了),这就意味着this的值会设置为null,最终会变成全局对象。
第三和第四种情况也是类似的——逗号操作符和OR逻辑表达式都会触发调用GetValue方法,于是相应地就会丢失原先的引用类型值,变成了函数类型,this的值就变成了全局对象了。
引用类型以及this的null值
对于前面提及的情形,还有例外的情况,当调用表达式左侧是引用类型的值,但是this的值却是null,最终变为全局对象(global object)。 发生这种情况的条件是当引用类型值的base对象恰好为活跃对象(activation object)。
当内部子函数在父函数中被调用的时候就会发生这种情况,通过下面的示意代码介绍活跃对象:
复制代码 代码如下:

// Declares foo function.
function foo() {
function bar() {
alert(this); // global
}
// The same as AO.bar().
bar();
}

由于活跃对象(activation object)总是会返回this值为——null(用伪代码来表示AO.bar()就相当于null.bar()),然后,this的值最终会由null转变为全局对象。
当函数调用包含在with语句的代码块中,并且with对象包含一个函数属性的时候,就会出现例外的情况。with语句会将该对象添加到作用域链的最前面,在活跃对象的之前。 相应地,在引用类型的值(标识符或者属性访问)的情况下,base对象就不再是活跃对象了,而是with语句的对象。另外,值得一提的是,它不仅仅只针对内部函数,全局函数也是如此, 原因就是with对象掩盖了作用域链中更高层的对象(全局对象或者活跃对象):
函数作为构造器被调用时this的值
函数作为构造函数时,我们通过new操作符创建实例对象是,它会调用Foo()函数的内部[[Construct]]方法;在对象创建之后,会调用内部的[[Call]]方法,然后所有Foo()函数中this的值会设置为新创建的对象。
复制代码 代码如下:

// Declares constructor
function Foo() {
// The new object.
alert(this);
this.x = 10;
}
var foo = new Foo();
foo.x = 23;
alert(foo.x); // 23手动设置函数调用时this的值

Function.prototype原型上定义了两个方法,允许手动指定函数调用时this的值。这两个方法分别是:.apply()和.call()。这两个方法都接受第一个参数作为调用上下文中this的值,而这两个方法的区别是传递的参数,对于.apply()方法来说,第二个参数接受数组类型(或者是类数组的对象,比如arguments), 而.call()方法接受任意多的参数(通过逗号分隔);这两个方法只有第一个参数是必要的——this的值。
通过示例代码介绍call()方法和apply()方法的使用:
复制代码 代码如下:

var myObject = {};
var myFunction = function(param1, param2) {
//setviacall()'this'points to my Object when function is invoked
this.foo = param1;
this.bar = param2;
//logs Object{foo = 'foo', bar = 'bar'}
console.log(this);
};
// invokes function, set this value to myObject
myFunction.call(myObject, 'foo', 'bar');
// logs Object {foo = 'foo', bar = 'bar'}
console.log(myObject);

call()方法第一个参数是必要的this值,接着我们可以传递任意多个参数,接着介绍apply()方法的使用。
复制代码 代码如下:

var myObject = {};
var myFunction = function(param1, param2) {
//set via apply(), this points to my Object when function is invoked
this.foo=param1;
this.bar=param2;
// logs Object{foo='foo', bar='bar'}
console.log(this);
};
// invoke function, set this value
myFunction.apply(myObject, ['foo', 'bar']);
// logs Object {foo = 'foo', bar = 'bar'}
console.log(myObject);

通过与call()方法对比,我们发现apply()方法和call()方法没有太大的区别,只是方法签名不一样。

1.1.3 总结

本文介绍Javascript中this的使用,更重要的是帮助我们能更好地理解this值在全局、函数、构造函数以及一些特例的情况中值的变化。

对于在函数上下文中this的值是函数调用者提供并且由当前调用表达式的形式而定的。如果在调用括号()的左边有引用类型的值,那么this的值就会设置为该引用类型值的base对象。 所有其他情况下(非引用类型),this的值总是null。然而,由于null对于this来说没有任何意义,因此会隐式转换为全局对象。
对于特例情况,我们要记住赋值符、逗号操作符以及||逻辑表达式,会使this丢失原先的引用类型值,变成了函数类型,this的值就变成了全局对象了

参考
[1] http://dmitrysoshnikov.com/ecmascript/chapter-3-this/ 英文版
[2] http://blog.goddyzhao.me/post/11218727474/this 译文
[3] https://net.tutsplus.com/tutorials/javascript-ajax/fully-understanding-the-this-keyword/

[作者]: JK_Rush
声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python vs. JavaScript:开发人员的比较分析Python vs. JavaScript:开发人员的比较分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

Python vs. JavaScript:选择合适的工具Python vs. JavaScript:选择合适的工具May 08, 2025 am 12:10 AM

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript:了解每个的优势Python和JavaScript:了解每个的优势May 06, 2025 am 12:15 AM

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

JavaScript的核心:它是在C还是C上构建的?JavaScript的核心:它是在C还是C上构建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript应用程序:从前端到后端JavaScript应用程序:从前端到后端May 04, 2025 am 12:12 AM

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架:为现代网络开发提供动力JavaScript框架:为现代网络开发提供动力May 02, 2025 am 12:04 AM

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具