'this' in function inside prototype function

47

我基本上有一个对象,通过其原型扩展了一个函数。在该函数内部,存在另一个函数,但是当在此嵌套函数中使用this时,它似乎不是指向对象,而是指向函数。

例如,

var sampleObject = function() {
 this.foo = 123;
}

sampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 }
 return nested();
}

var test = new sampleObject();

window.alert(test.getFoo()); // undefined

this.foo并不指向123值,而是未定义的,因为它指向嵌套函数,在其中没有foo存在。如何从嵌套函数中访问123值?

8个回答

41
sampleObject.prototype.getFoo = function() {
 var me = this;
 var nested = function() {
  return me.foo;
 }
 return nested;
}

通过将this的值保存在本地变量中,您使其显式成为该函数及所有嵌套函数作用域的词法上下文的一部分。 因此,在调用“nested”时,该内部函数将具有其自己的作用域(它自己的this值),但仍然可以引用封闭范围中的变量“me”。


如果我这样做会发生什么: var foo = new sampleObject(); $(window).on('scroll', foo.getFoo ); - vsync
@vsync 这应该没问题 - 如果你在这方面遇到了问题,我建议你开一个全新的问题,并提供导致困难的代码示例。 - Pointy
1
但这并不好,因为使用函数引用会丢失原型中非常重要的this,所以在你的例子中var me = this;将指向window对象。 - vsync
1
@vsync 啊,好的,抱歉我误读了你的问题。是的,在这种情况下,您需要将调用包装在匿名函数中,或者使用.bind()或其他方法来为您完成它。 - Pointy
1
只是一个注释,在上面的例子中,嵌套函数是一个闭包,而且 me 将绑定到函数,即使在返回后也是如此。me 将在嵌套函数的闭包范围内。而 nested 将正常工作,因为它有 me 的一部分,并且引用了 sampleObject 实例。 - Mohamed Allal

14

在你的例子中,“this”指的是window对象,因为当你调用嵌套函数时没有指定另一个上下文,而且由于window.foo未定义,所以你得到了undefined。

你可以通过以下3种方法来解决这个问题。

1 - 使用变量存储外部的this - 最常用的方法

sampleObject.prototype.getFoo = function() {
 var _this = this;
 var nested = function() {
  return _this.foo;
 }
 return nested();
}

2 - 使用bind方法将外部的“this”绑定到内部

sampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 }.bind(this);
 return nested();
}

3 - 使用call方法,该方法可以将上下文传递给函数

SampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 };
 return nested.call(this);
}

8
常见的解决方法是使用闭包。
sampleObject.prototype.getFoo = function() {
  var _this = this; 
  var nested = function() {
    return _this.foo;
   }
   return nested();
}

有些库添加了方法来自动化这个过程


1
我更喜欢非自动化的方式,因为它不那么复杂,谢谢。 - pimvdb
1
在这种情况下你可能更喜欢手动方式,但对于事件处理程序来说自动化的方式确实很好。 - Hemlock

7

简述

使用箭头函数。它们自ECMAScript 6以来就可用:

var sampleObject = function() {
  this.foo = 123;
}

sampleObject.prototype.getFoo = function() {
  var nested = () => { // Changed this line.
    return this.foo;
  }
  return nested();
}

var test = new sampleObject();

window.alert(test.getFoo());

说明

箭头函数的主要优势之一是解决了您所描述的问题,该问题在不绑定this一节中有所阐述。参考文献指出:

在箭头函数之前,每个新函数都定义了自己的this值[...] 箭头函数不会创建自己的this上下文,因此this具有来自封闭上下文的原始含义。


4
除了声明 var _this = this,我还看到代码使用 var that = thisvar self = this
知道变量的作用域很重要,因为它可能会导致意外的结果。

如果你正在进行一个大项目,请制定一个规范。例如,始终使用“var that = this;”、“self”或其他类似的方式。 - Zachary K
对我来说,“that”通常是原型函数中的变量, 比如 foo.prototype.add = function(that) {return this + that},所以我更喜欢使用“self” 或者 “me”。 - pimvdb

1

这是一个老问题,但为了完整起见,我提供另一种解决方案。另一种方法涉及函数绑定

sampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 }
 return nested.bind(this)();
}

嘿,这很酷。那么,你能详细说明带参数的函数吗?我有一种感觉它看起来相似,但是..不同。 - bkwdesign

1
使用ES6的方法是使用箭头函数。基本上,当您使用箭头函数时,它不会创建自己的“this”上下文。因此,使用“this”将引用父函数的上下文。代码如下:
sampleObject.prototype.getFoo = function() {
    const nested = () => {
        return this.foo; //"this" refers to parent function's context
    }
    return nested;
}

0

这是 JavaScript 上已知的一个问题。通常的模式是在外部函数中将 this 分配给另一个变量(通常是 self),然后从内部函数中访问 self。这样可以解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接