Как & ldquo; правильно & rdquo; создать пользовательский объект в JavaScript?

Я нашел другой способ, который может быть проще ...

Объявить метод InvokeMethod

[WebMethod]
    public object InvokeMethod(string methodName, Dictionary<string, object> methodArguments)
    {
        try
        {
            string lowerMethodName = '_' + methodName.ToLowerInvariant();
            List<object> tempParams = new List<object>();
            foreach (MethodInfo methodInfo in serviceMethods.Where(methodInfo => methodInfo.Name.ToLowerInvariant() == lowerMethodName))
            {
                ParameterInfo[] parameters = methodInfo.GetParameters();
                if (parameters.Length != methodArguments.Count()) continue;
                else foreach (ParameterInfo parameter in parameters)
                    {
                        object argument = null;
                        if (methodArguments.TryGetValue(parameter.Name, out argument))
                        {
                            if (parameter.ParameterType.IsValueType)
                            {
                                System.ComponentModel.TypeConverter tc = System.ComponentModel.TypeDescriptor.GetConverter(parameter.ParameterType);
                                argument = tc.ConvertFrom(argument);

                            }
                            tempParams.Insert(parameter.Position, argument);

                        }
                        else goto ContinueLoop;
                    }

                foreach (object attribute in methodInfo.GetCustomAttributes(true))
                {
                    if (attribute is YourAttributeClass)
                    {
                        RequiresPermissionAttribute attrib = attribute as YourAttributeClass;
                        YourAttributeClass.YourMethod();//Mine throws an ex
                    }
                }

                return methodInfo.Invoke(this, tempParams.ToArray());
            ContinueLoop:
                continue;
            }
            return null;
        }
        catch
        {
            throw;
        }
    }

Затем я определяю свои методы следующим образом

[WebMethod]
    public void BroadcastMessage(string Message)
    {
        //MessageBus.GetInstance().SendAll("<span class='system'>Web Service Broadcast: <b>" + Message + "</b></span>");
        //return;
        InvokeMethod("BroadcastMessage", new Dictionary<string, object>() { {"Message", Message} });
    }

    [RequiresPermission("editUser")]
    void _BroadcastMessage(string Message)
    {
        MessageBus.GetInstance().SendAll("<span class='system'>Web Service Broadcast: <b>" + Message + "</b></span>");
        return;
    }

Теперь у меня есть проверка во время выполнения без инъекции зависимостей ...

Нет ошибок на сайте:)

Надеюсь, вы согласитесь, что это меньше, чем тогда AOP Framework или из MarshalByRefObject или с использованием удаленных или прокси-классов.

462
задан dakab 29 December 2016 в 10:59
поделиться

5 ответов

Есть две модели для реализации классов и экземпляров в JavaScript: способ прототипирования и способ закрытия. У обоих есть преимущества и недостатки, и существует множество расширенных вариантов. Многие программисты и библиотеки используют разные подходы и служебные функции для работы с классами, чтобы скрыть некоторые из уродливых частей языка.

В результате в смешанной компании вы получите мешанину из метаклассов, которые ведут себя немного по-разному. Что еще хуже, большинство учебных материалов по JavaScript ужасны и служат своего рода промежуточным компромиссом, чтобы охватить все основы, оставляя вас в замешательстве. (Вероятно, автор тоже сбит с толку. Объектная модель JavaScript сильно отличается от большинства языков программирования и во многих местах изначально плохо спроектирована.)

Пусть ' Начнем с способа-прототипа . Это наиболее родной для JavaScript, который вы можете получить: здесь минимум накладных расходов, и instanceof будет работать с экземплярами этого типа объекта.

function Shape(x, y) {
    this.x= x;
    this.y= y;
}

Мы можем добавить методы к экземпляру, созданному new Shape записав их в поиск прототипа этой функции-конструктора:

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

Теперь создадим подкласс, насколько вы можете вызывать то, что JavaScript выполняет подклассы. Мы делаем это, полностью заменяя это странное волшебное свойство prototype :

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

перед добавлением к нему методов:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

Этот пример будет работать, и вы увидите подобный код во многих руководствах. Но, черт возьми, этот new Shape () уродлив: мы создаем экземпляр базового класса, даже несмотря на то, что на самом деле Shape не создается. В этом простом случае это работает, потому что JavaScript очень небрежный: он позволяет передавать нулевые аргументы, и в этом случае x и y становятся undefined и присваиваются прототипу this.x и this.y . Если бы функция-конструктор выполняла что-то более сложное, она бы упала ниц.

Итак, что нам нужно сделать, это найти способ создать объект-прототип, который содержит методы и другие члены, которые мы хотим на уровне класса, без вызова функции конструктора базового класса. Для этого нам нужно начать писать вспомогательный код. Это самый простой из известных мне подходов:

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

Он передает члены базового класса в его прототипе новой функции-конструктору, которая ничего не делает, а затем использует этот конструктор. Теперь мы можем просто написать:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

вместо неправильности new Shape () . Теперь у нас есть приемлемый набор примитивов для построенных классов.

Есть несколько улучшений и расширений, которые мы можем рассмотреть в рамках этой модели. Например, вот версия с синтаксическим сахаром:

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

Обе версии имеют недостаток, заключающийся в том, что функция-конструктор не может быть унаследована, как это есть во многих языках. Поэтому, даже если ваш подкласс ничего не добавляет к процессу построения, он должен помнить о вызове базового конструктора с любыми аргументами, которые хочет база. Это можно немного автоматизировать с помощью apply , но все же вы должны написать:

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

Таким образом, обычное расширение - это разбить материал инициализации на свою собственную функцию, а не на сам конструктор. Затем эта функция может наследовать от базовой:

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

Теперь у нас есть один и тот же шаблон функции конструктора для каждого класса. Возможно, мы можем переместить это в свою собственную вспомогательную функцию, чтобы нам не приходилось вводить ее постоянно, например, вместо Function.prototype.subclass , развернув его и позволив функции базового класса выдавать подклассы :

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

... который начинает больше походить на другие языки, хотя и с немного более неуклюжим синтаксисом. Вы можете добавить несколько дополнительных функций, если хотите. Возможно, вы хотите, чтобы makeSubclass взял и запомнил имя класса и предоставил по умолчанию toString , используя его. Может быть, вы хотите, чтобы конструктор обнаруживал, когда он был случайно вызван без оператора new (что в противном случае часто приводило бы к очень неприятной отладке):

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

Может быть, вы хотите передать все новые члены и пусть makeSubclass добавляет их в прототип, чтобы избавить вас от необходимости писать Class.prototype ... довольно много. Многие системы классов делают это, например:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

Есть много потенциальных возможностей, которые вы могли бы счесть желательными в объектной системе, и никто не согласен с одной конкретной формулой.


Способ закрытия ], тогда. Это позволяет избежать проблем, связанных с наследованием на основе прототипов в JavaScript, поскольку наследование вообще не используется. Вместо этого:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

Теперь каждый экземпляр Shape будет иметь свою собственную копию метода toString (и любых других методов или других членов класса, которые мы добавляем).

Плохо Дело в том, что каждый экземпляр имеет свою собственную копию каждого члена класса, что он менее эффективен. Если вы имеете дело с большим количеством экземпляров подклассов, прототипное наследование может вам помочь.

[Кроме того, поскольку здесь нет наследования, оператор instanceof не будет работать; вам придется предоставить свой собственный механизм для сниффинга классов, если он вам нужен. Хотя вы могли возиться с объектами-прототипами так же, как и с наследованием прототипов, это немного сложно и не стоит того, чтобы просто заставить instanceof работать.]

Хорошая вещь. в отношении каждого экземпляра, имеющего свой собственный метод, заключается в том, что метод затем может быть привязан к конкретному экземпляру, которому он принадлежит. Это полезно из-за странного способа привязки JavaScript this в вызовах методов, что приводит к тому, что если вы отделите метод от его владельца:

var ts= mycircle.toString;
alert(ts());

, то this внутри метода выиграет не будет экземпляром Circle, как ожидалось (на самом деле это будет глобальный объект window , вызывая широко распространенные проблемы отладки). На самом деле это обычно происходит, когда метод берется и назначается setTimeout , onclick или EventListener в целом.

В способе прототипа у вас есть чтобы включить закрытие для каждого такого присвоения:

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

или в будущем (или сейчас, если вы взломаете Function.prototype) вы также можете сделать это с помощью function.bind () :

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

, если ваш экземпляры выполняются способом замыкания, привязка выполняется бесплатно путем замыкания переменной экземпляра (обычно называемой , что или self , хотя лично я бы посоветовал не использовать последнее как ] self уже имеет другое значение в JavaScript). Вы не получите аргументы 1, 1 в приведенном выше фрагменте бесплатно, так что вам все равно понадобится другое закрытие или bind () , если вам нужно это сделать.

Также существует множество вариантов метода закрытия. Вы можете полностью опустить this , создав новый that и вернув его вместо использования оператора new :

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

Какой путь «правильный»? Обе. Что лучше? Это зависит от вашей ситуации. FWIW Я склоняюсь к созданию прототипов для реального наследования JavaScript, когда я сильно занимаюсь объектно-ориентированным дизайном, и закрытием для простых эффектов одноразовой страницы.

Но оба способа довольно противоречивы для большинства программистов. У обоих есть много потенциально беспорядочных вариантов. Вы встретите и то, и другое (а также множество промежуточных и вообще неработающих схем), если будете использовать чужой код / ​​библиотеки. Нет единого общепринятого ответа.

[Это часть 94 книги «Почему JavaScript не мой любимый язык программирования».]

880
ответ дан 22 November 2019 в 22:55
поделиться

Наиболее известные библиотеки javascript, доступные сегодня, имеют функцию перетаскивания.

Вы можете попробовать:

-121-4245268-

Дуглас Крокфорд подробно обсуждает эту тему в Хорошие моменты . Он рекомендует избегать использования оператора new для создания новых объектов. Вместо этого он предлагает создавать индивидуальные конструкторы. Например:

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

В Javascript функция является объектом, и ее можно использовать для создания объектов вместе с оператором new . По соглашению функции, предназначенные для использования в качестве конструкторов, начинаются с заглавной буквы. Вы часто видите такие вещи, как:

25
ответ дан 22 November 2019 в 22:55
поделиться

You can also do it this way, using structures :

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

Then :

var counter1 = createCounter();
counter1.increaseBy(4);
6
ответ дан 22 November 2019 в 22:55
поделиться

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

Вот обычный объект JavaScript:

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

Вы можете многое извлечь из того, что Дуглас Крокфорд говорит о JavaScript. Джон Ресиг тоже великолепен. Удачи!

4
ответ дан 22 November 2019 в 22:55
поделиться

Я использую этот шаблон довольно часто - я обнаружил, что он дает мне довольно большую гибкость, когда мне это нужно. По использованию он очень похож на классы в стиле Java.

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

В нем используется анонимная функция, которая вызывается при создании и возвращает новую функцию-конструктор. Поскольку анонимная функция вызывается только один раз, вы можете создавать в ней частные статические переменные (они находятся внутри замыкания и видны другим членам класса). Функция-конструктор в основном представляет собой стандартный объект Javascript - внутри него вы определяете частные атрибуты, и общедоступные атрибуты прикреплены к переменной this .

По сути, этот подход объединяет подход Крокфорда со стандартными объектами Javascript для создания более мощного класса.

Вы можете использовать его так же, как и любой другой другой объект Javascript:

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method
90
ответ дан 22 November 2019 в 22:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: