前言
JS 是一种脚本语言,因此被很多人认为是简单易学的。然而情况可能与之相悖,JS 遵从函数式编程、闭包、基于原型的继承等高级功能。本文介绍一下JS中的 this 关键字,可以这样说,正确掌握了 JS 中的 this 关键字,才算迈入了 JS 这门语言的门槛。
注: 本文采用部分 es6 进行代码分析
Java工程师所熟识的this
在 Java 中定义类经常会使用 this 关键字,多数情况下是为了避免命名冲突,比如在下面例子的中,定义一个 Image 类,很自然的,大家会使用 width、height 为其属性或成员变量命名,在构造函数中,使用 width、height 为参数命名。无论哪种情况,this 的含义是一样的,均指当前类对象,当调用构造方法时全局变量width、height的值会被修改。
public class Image {
private int width = 0;
private int height = 0;
public Image(int width, int height) {
this.width = width;
this.height = height;
}
public void println() {
System.out.println("width = " + this.width);
System.out.println("height = " + this.height);
}
}
调用方式如下:
Image image = new Image(10, 10);
image.println();
输出结果:
width = 10
height = 10
Process finished with exit code 0
可以看得出来this.x 与 this.y 直接修改的全局变量的值,也证明了我们上面的观点~
JS语言中的 this
由于JS等脚本语言运行期进行解释的特性,JS 中的 this 含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。
JS 中函数的调用有以下几种方式:
1、作为对象方法调用
2、作为函数调用
3、作为构造函数调用
4、使用 apply 或 call 调用
接下来我们按照调用方式的不同,分别讨论 this 的含义。
作为对象方法调用
var image = {
width : 0,
height : 0,
update : function(width, height) {
this.width = this.width + width
this.height = this.height + height
}
}
// this 绑定到当前对象,即 image 对象
// 执行此方法后image的width与Height属性值变为1
image.update(1, 1)
作为函数调用
当调用函数时,此时 this 绑定到全局对象,在浏览器中,window 就是该全局对象,例如下面:setLocalValue 函数被调用时,this 被绑定到全局对象,接下来执行赋值语句,相当于隐式的声明了一个全局变量,这显然不是调用者希望的效果~
function setLocalValue(value) {
this.localValue = value
}
setLocalValue(5)
// localValue 已经声明成一个值为 5 的全局变量
console.log(localValue) // ==> 5
对于内部函数,即声明在一个函数体内的函数,这种绑定到全局对象的方式会衍生出一个新的问题,我们仍然以前面提到的 image 对象为例,这次我们希望在 update 方法内定义两个函数,分别将 width、height 属性进行修改。结果可能出乎大家意料,不仅 image 对象没有移动,反而多出两个全局变量 width、height。
var image = {
width : 0,
height : 0,
update : function(width, height) {
// 内部函数
var updateWidth = function(width) {
//this 绑定到了哪里?
this.width = width;
}
// 内部函数
var updateHeight = function(height) {
//this 绑定到了哪里?
this.height = height;
}
updateWidth(width)
updateHeight(height)
}
}
image.update(1, 1)
console.log(image.width) // ==> 0
console.log(image.height) // ==> 0
console.log(width) // ==> 1
console.log(height) // ==> 1
这属于 JS 语言的设计缺陷,正确的设计方式是内部函数的 this 应该绑定到其外层函数对应的对象上,为了规避这一设计缺陷,我们一般采用变量替换的方法,该变量草民习惯命名为成 self~
var image = {
width : 0,
height : 0,
update : function(width, height) {
var self = this
// 内部函数
var updateWidth = function(width) {
self.width = width;
}
// 内部函数
var updateHeight = function(height) {
self.height = height;
}
updateWidth(width)
updateHeight(height)
}
}
image.update(1, 1)
console.log(image.width) // ==> 1
console.log(image.height) // ==> 1
这样就达到了我们想要的效果~
作为构造函数调用
JS 支持面向对象编程,但与主流的面向对象式编程语言不同。
:es6之前JS 并没有类(class)的概念,而是使用基于原型(prototype)的继承方式,JS 中的构造函数也很特殊,如果不使用 new 调用,则和普通函数一样,作为又一项约定俗成的准则,构造函数以大写字母开头,提醒调用者使用正确的方式调用,如下es5示例。
:es6之后支持了class的概念,重写constructor方法,this 绑定到新创建的对象上。
es5示例 :
function Image(width, height){
this.width = width
this.height = height
}
var img1 = new Image(10, 10)
es6 示例 :
class Image {
constructor(width, height) {
this.width = width
this.height = height
}
}
let img1 = new Image(10, 10)
使用 apply 或 call 调用
此次着重强调一下,在 JS 语言中函数也是对象,对象则有行为(方法),apply 和 call 就是函数对象的方法。这两个方法功能超级强大,它们允许切换函数执行的上下文环境(context),即 this 指向的对象,在很多JS库中也得到了广泛应用~
class Image {
constructor(width, height) {
this.width = width
this.height = height
}
update(width, height) {
this.width = width
this.height = height
}
}
let img1 = new Image(0, 0)
let img2 = {width: 0, height: 0}
img1.update(10, 10)
// img2 width、height ==> 20
img1.update.apply(img2, [20, 20])
// img2 width、height ==> 20
img1.update.call(img2, 20, 20)
在上面的例子中,我们使用构造函数生成了一个对象 img1,该对象同时具有 update 方法,同时定义了另一个对象 img2,使用 apply 可以将 img1 的方法应用到 img2 上,这时候 this 也被绑定到对象 img2 上。
结语:
JS之this关键字就介绍到这里,谢谢大家的观看,希望对大家有所帮助~