«привилегированный» метод, вызывающий проблему метода члена - PullRequest
1 голос
/ 11 июня 2011

Рассмотрим следующий код:

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();

В этом коде есть ошибка с «привилегированным» методом canWalk(), предполагающим, что указатель this указывает на экземпляр Dog. Похоже, он указывает на глобальный объект, который меня смущает! Я читал, что из-за замыканий я могу получить ссылку на this в области конструктора, а затем использовать эту ссылку в моем «привилегированном» методе, но это похоже на взлом.

Я едва понимаю поведение указателя this в различных контекстах. Я понимаю, что метод, который «присоединен» к объекту, получит this, указывающий на присоединенный объект. Например, foo.doStuff() будет иметь this, указывающий на экземпляр foo.

Что беспокоит, так это то, что, хотя я думаю, что я умен и создаю «привилегированные» методы для своего ОБЪЕКТА, похоже, что Я ДЕЙСТВИТЕЛЬНО сбрасываю функции в глобальный контекст! Возможно, существовал глобальный метод под названием init() (вполне возможно) из другой библиотеки или файла, не моего собственного создания. Если бы я затем определил симпатичный маленький инкапсулированный объект с помощью «привилегированного» метода с именем init(), я бы заменил другой глобальный метод init().

Мой вопрос: каков наилучший способ создания "привилегированных" методов (и полей в этом отношении), которые ограничены объектом, которому они должны принадлежать? Какой другой подход к моему образцу объекта предоставил бы приватные или привилегированные методы, в то же время будучи должным образом ограниченным. Я не ищу неясные решения, которые отвечают требованиям, а скорее лучшую практику.

Ответы [ 3 ]

4 голосов
/ 11 июня 2011

Не используйте this.Поместите var self = this; в верхней части функции вашей собаки и используйте self.Таким образом, у вас всегда будет «безопасная» ссылка на Собаку.

В вашем примере 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();

Теперь вам не нужны this или new.Кроме того, вы можете сделать это:

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 голос
/ 11 июня 2011

Как вы уже поняли, вызов canWalk прямо вверх означает, что ключевое слово 'this' установлено для глобального объекта для вызова этой функции.

Если вы хотите выполнить вызов в контексте текущей собаки, вы можете сделать это явно так:

canWalk.call(this)

1 голос
/ 11 июня 2011

Во-первых, вам нужно var перед вашим canWalk. Таким образом, функция canWalk ограничена областью действия dog(), а не является неявной глобальной.

this указывает на «владельца» функции. При использовании оператора new функция создает собственную область видимости. Так, в теле dog(), this относится к объекту экземпляра собаки. Внутри вашей функции walk, this также относится к экземпляру собаки, поскольку вы устанавливаете для функции 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
    };
}

Надежда, которая проясняет ситуацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...