Проблема загрязнения пространства имен Javascript

Я только начинаю работать с Javascript, поэтому моя первая попытка использовать пространства имен выглядела так:

var myNameSpace = {};
var myNameSpaceProto = myNameSpace.__proto__;

myNameSpaceProto.SomeFunc = function()
{
    alert("SomeFunc()");
};

myNameSpaceProto.SomeObject = function()
{
    alert("SomeObject constructor");
};

var instance = new myNameSpace.SomeObject();

Я полагаю, что могу смело пропустить шаг прототипа и просто иметь myNameSpace.SomeFunc = function..., потому что существует только один экземпляр объекта myNameSpace, поэтому прототип ничего не сохраняет.

Вопрос 1. Это правильно? Я хочу добавить в пространство имен из нескольких отдельных файлов .js, поэтому этот способ кажется удобным.

Вопрос 2. В приведенном выше фрагменте кода я обнаружил странный побочный эффект загрязнения пространства имен, который показан следующим телом SomeObject:

myNameSpaceProto.SomeObject = function()
{
    // As expected NonexistantFunc is not a member of this and returns "undefined"
    alert("typeof this.NonexistantFunc = " + typeof this.NonexistantFunc);

    // Returns 'function'. How has SomeFunc made it to this.SomeFunc?  It's supposed to be under myNameSpace.SomeFunc
    alert("typeof this.SomeFunc = " + typeof this.SomeFunc);

    // Turns out it's in the prototype's prototype.  Why?
    alert("this.__proto__.__proto__.SomeFunc = " + this.__proto__.__proto__.SomeFunc);
};

Это было проверено на Chrome 8, и я не могу понять, как SomeObject стал участником SomeFunc. Кажется, это пробел в моих ограниченных знаниях о прототипах. Может кто-нибудь объяснить?


person AshleysBrain    schedule 28.01.2011    source источник


Ответы (2)


давайте начнем с основ.

Не трогайте __proto__. Это ящик Пандоры. Вы не хотите возиться с этим. Он не только не поддерживается кросс-браузерно, но вы можете написать какой-нибудь отвратительный код, и его не нужно использовать.

var Constructor = new Function;
Constructor.fn = Constructor.prototype;

Constructor.fn.someFunc = function() { 
    alert("someFunc");
}

var obj = new Constructor;

var namespace = {};
namespace.someStaticFunc = function() {
   alert("someStaticFunc");
}    

Вам нужно различать пространство имен и конструктор. Есть ли реальная причина, по которой методы пространства имен должны быть записаны в прототип, а не как свойства объекта?

Итак, для первого ответа да, вы можете пропустить прототип.

Что касается второго вопроса, поскольку вы изначально пишете прототип, то, что вы на самом деле делаете, - это непосредственное редактирование методов объекта.

Считайте .prototype определением Class. Если вы редактируете obj.__proto__, вы редактируете Class из Object во время выполнения. Вы портите все остальные объекты, полученные из этого Class. Динамические классы в порядке. Но редактирование классов внутри объектов — действительно хороший способ создавать непонятные ошибки.

Вопрос 2: странно.

Вот что происходит:

var o = {}; // Ok o is an object
var o.__proto__.property = 5; // Ok I changed o's class and it now has a property = 5.

var o.__proto__.construct = function() { }; // Ok I changed the class again it now has a constructor

var p = new o.construct(); // we create an object from my constructor.

(p.__proto__ === o.construct.prototype) // true! the proto object is o.c.prototype. Because 
// p is created from o.c so the prototype is that of o.c

(o.construct.__proto__ === Object.prototype) // true! Oh-uh. Now look what we've been doing! 
// When you created `var o = {}` and edited o.__proto__ you've been editing Object.prototype

Ты уже видишь тревожные звоночки? Вы редактировали класс Object на лету. Весь ваш код вокруг вас разваливается.

Функция - это объект, верно?

Function.property === 5 // oh dear!

Мы нашли всю причину. Мы писали эти методы в Object.prototype. Таким образом, этот метод определен для КАЖДОГО объекта. Включая .__proto__, потому что это тоже объект.

Видел ли я, что иметь дело с .__proto__ было плохой идеей? Я думаю, я должен сказать это снова.

Если вам интересно, this было instance, this.__proto__ было Object.prototype.SomeObject.prototype, а this.__proto__.__proto__ было Object.prototype

Вот ссылка на garden Прочтите.

person Raynos    schedule 28.01.2011
comment
Спасибо, хороший ответ :) Я работаю на C++, и люди говорят, что этот язык имеет недостатки... - person AshleysBrain; 28.01.2011
comment
@AshleysBrain это не так уж и плохо. Спецификация говорит, что вы не можете редактировать класс через объект во время выполнения. Это специфика браузера. Никогда не используйте .__proto__ - person Raynos; 28.01.2011

Ответ 2: SomeObject имеет доступ к SomeFunc таким образом, потому что this относится к myNameSpaceProto, а не SomeObject.

Quirksmode дает достойное объяснение, почему это так.

Вот гораздо лучшая статья, объясняющая это.

Итак, как вы могли догадаться, Ответ 1: Да.

person sdleihssirhc    schedule 28.01.2011
comment
Это не правильно. SomeObject имеет доступ к SomeFunc, потому что он находится в Object.prototype. this относится к instance - person Raynos; 28.01.2011