【问题标题】:"privileged" method calling member method issue“特权”方法调用成员方法问题
【发布时间】:2011-06-10 23:08:24
【问题描述】:

考虑以下代码:

function Dog()
{
    this.foo = function() {};

    this.walk = function()
    {
        if(canWalk())
        {
            alert('walking');
            return;
        }

        alert('I have no legs!');
    }

    canWalk = function()
    {
        this.foo();
        return false;
    }
}

var instance = new Dog();
instance.walk();

假设this 指针指向Dog 的实例,该代码中的“特权”canWalk() 方法存在错误。它实际上似乎指向了让我感到困惑的全局对象!我读过,由于闭包,我可以在构造函数的范围内获取对 this 的引用,然后在我的“特权”方法中使用该引用,但这似乎是一种 hack。

我只是勉强掌握this 指针在各种上下文中的行为。我了解“附加”到对象的方法将收到指向附加对象的this。例如,foo.doStuff() 将有一个 this 指向 foo 实例。

令人不安的是,虽然我认为我很聪明并在我的 OBJECT 上创建了“特权”方法,但似乎我实际上是在将函数转储到全局范围内!也许有一个名为 init() 的全局方法(很可能)来自另一个库或文件,而不是我自己创建的。如果我随后用名为init() 的“特权”方法定义了一个漂亮的小封装对象,我将替换另一个全局init() 方法。

我的问题是,创建“特权”方法(以及与此相关的字段)的最佳实践是什么?我的示例对象还有哪些其他方法可以提供私有或特权方法,同时也具有适当的范围。我不是在寻找满足要求的晦涩解决方案,而是在寻找最佳实践。

【问题讨论】:

  • 我不知道如何编辑我的原始帖子,但我意识到我犯了一个不幸的错误并且没有粘贴正确的示例代码。 canWalk() 方法调用了“foo”方法,所以基本上 canWalk() 调用了 this.foo()。很抱歉造成混乱
  • 啊,我明白了。在那种情况下,this 可能指向了canWalk 函数。在我的示例中,如果您使用 self.foo 就可以了。而且某处可能有一个编辑按钮:)
  • @Steve 您可以通过单击文本左下角蓝色标签下方的“编辑”来编辑您自己的帖子。我尝试用您描述的内容更新代码,但有点不清楚。
  • 我认为这就是他所说的:jsfiddle.net/3aMwb 我相信这说明了问题。
  • @Erik 感谢您的尝试,我很感激。我单击编辑,它会弹出两个版本的比较 - 我实际上无法编辑文本。我已尝试注销并重新登录,结果相同。

标签: javascript javascript-framework


【解决方案1】:

不要使用this。将 var self = this; 放在 Dog 函数的顶部并使用 self。这样,您将始终对 Dog 有一个“安全”的引用。

在您的示例中,this 应该指向 Dog,除非我也遗漏了什么。

至于您的特权方法,您忘记定义变量。添加var canWalk;

还有另一种方式:闭包。我举个例子:

function create_dog() {
    function canWalk() {
        return false;
    }
    function walk() {
        if (canWalk()) {
            alert("walking");
            return;
        }
        alert("I have no hands!");
    }
    return {
        "walk": walk // points to the function walk()
    }; // this the API of dog, like public functions (or properties)
}
var dog = create_dog();
dog.walk();

现在您不需要thisnew。此外,您可以这样做:

function create_dog() {
    function canWalk() {
        return false;
    }
    function walk() {
        if (canWalk()) {
            alert("walking");
            return;
        }
        alert("I have no hands!");
    }
    var dog = {
        "walk": walk
    };
    return dog;
}
var dog = create_dog();
dog.walk();

因此您可以在您的权限函数中引用dog如果您不打算使用原型设计,我会建议关闭方法。

顺便说一句。为避免混淆:

function my_func() {}

var my_func = function () {};

是等价的。如果您在两个函数之间有循环引用并且想要强制使用前定义,则后者会很有用。

【讨论】:

  • +1 很好的答案,展示了如何以两种完全不同的方式做同样的事情。
  • 你为什么要离开new Dog而只创建一个对象?
  • 如果您不使用原型设计,则使用new 没有任何优势。完全封闭的方法是可用的,只是不要尝试做任何继承。
  • 很好的例子!我同意这是一个很好的方法,但是你最后的评论(“......如果你不打算使用原型设计......”)而且我确实打算使用原型设计。你可以想象 Dog 会继承一个“Animal”。
  • 如果你真的需要继承和多态,那么你不应该使用这种闭包方法,而是使用原型方法。我不是它的忠实粉丝,但它有它的用途。使用this时要小心;)
【解决方案2】:

首先,您需要在 canWalk 前面添加一个 var。这样一来,canWalk 函数就被限制在dog() 的范围内,而不是隐式全局。

this 指向函数的“所有者”。使用 new 运算符时,该函数会创建自己的作用域。所以,在dog() 的主体中,this 指的是狗实例对象。在您的 walk 函数中,this 还指代 dog 实例,因为您将 walk 函数设置为 dog (this.walk = function () { ... }) 的实例。但是在canWalk中,没有所有者,所以this指向全局对象window

我意识到我的解释可能令人困惑,所以这里是我的解释的代码注释版本:

var obj = {
    'f': function () {
        // this === obj, because obj "owns" this function
        // In other words, this function is callable by obj.f()
    },
    'o': {
         'f': function () {
             // this === obj.o, because obj.o "owns" this function
             // In other words, this function is callable by obj.o.f()
         }
    }
};

// The new operator essentially sets "this" within the 
// function to an empty object and returns that object
var instance = new Dog();

function Dog() {
    // this === instance, because of the "new" operator

    // For demonstration
    var self = this;

    this.walk = function () {
        // this === instance === self, because self/instance "owns" this function
        // In other words, this function is callable by self.walk()
    };

    var canWalk = function () {
        // What's the owner of this object? Nothing,
        // so it defaults to the global object, window
        // this === window

        // Instead, we can access the Dog instance by
        // using the "self" variable we set earlier.
        // self === instance
    };
}

希望能解决问题。

【讨论】:

  • 这是对这两种情况的一个很好的解释。我只涵盖了canWalk 的隐式全局范围,所以我删除了我的答案。 +1
  • 对事物的另一个很好的解释和介绍。我有点尴尬,我错过了前面没有使用'var'的事实。
【解决方案3】:

如您所见,像这样直接调用 canWalk 意味着“this”关键字被设置为该函数调用的全局对象。

如果你想在当前狗的上下文中运行调用,你可以明确地这样做:

canWalk.call(this)

【讨论】:

  • 我也找到了 call() 解决方案,但决定不使用它,因为它感觉不正确,它有一种味道,让我觉得我应该寻找设计更改。不过最好记住 call() 选项。
猜你喜欢
  • 1970-01-01
  • 2011-07-24
  • 2017-08-24
  • 1970-01-01
  • 1970-01-01
  • 2012-03-31
  • 1970-01-01
  • 2014-04-03
相关资源
最近更新 更多