Объект, который возвращает экземпляр себя - PullRequest
11 голосов
/ 22 мая 2011

Справочная информация: Мой последний проект не может использовать большую библиотеку, что меня огорчает.Есть несколько вещей, которые я хотел бы получить из любой библиотеки, такие как отсутствующие функции addClass, hasClass, removeClass, совместимые addEventListener и т. Д. Поэтому я создал маленький объект о котором я хотел бы высказать некоторые мнения в другое время, но у меня возникли небольшие проблемы с настройкой, как я хотел бы.

Для удобства использования, Я хочу, чтобы объектвозвращать новый экземпляр самого себя при создании.

Учитывая:

 $ = function() {
    this.name = "levi";

    return this;
};

console.log($());

Мы получаем DOMWindow вместо $ из-за странной природы this в JavaScript. Что еще более странно для меня, это то, что console.log(new $().name) правильно возвращает "леви".Если this привязано к окну, почему объект правильно получил значение? .Мы могли бы просто добавить новый console.log(new $()), и это работает.Однако я не хочу писать новое каждый раз.Поэтому я попытался:

$ = function() {
    var obj = function() {
        this.name = "levi";
    };

    return new obj();
};

console.log($());

Что дает мне то, что я хочу, но кажется немного ненужным заключать объект в функцию, которая его создает.Более того, возвращаемый объект - obj, а не $.Сравнительные тесты не пройдут.

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


Кто-нибудь видит что-то не так со следующим решением?

$a = function() {};

$ = function() {
    if (!(this instanceof $)) {
        return new $();
    }

    this.name = "levi";

    return this;
};

//helper function
var log = function(message) {
    document.write((message ? message : '') + "<br/>");
};

log("$().name == window.name: " + ($().name == window.name)); //false
log("$().name: " + $().name); //levi
log("window.name: " + window.name); //result

log();

log("$a instanceof $: " + ($a instanceof $)); //false
log("typeof $a: " + (typeof $a)); //function
log("typeof $: " + (typeof $)); //function

Itкажется, работает во всех моих тестах.

Ответы [ 4 ]

6 голосов
/ 22 мая 2011

Самый простой способ сделать то, что вы хотите, - это (я думаю):

$ = function(){
    if (!(this instanceof $)){
     return new $;
    }
    this.name = 'levi'; 
    return this;
}

Тот факт, что просто возвращение this не создает экземпляр $, происходит из-за способа this создается, выполняя $ как обычную функцию: в этом случае значение this указывает на глобальный объект (в браузере: window, фактический вызов выполнения $() совпадает с window.$()),Это факт жизни javascript, так сказать.Тот факт, что console.log(new $().name) показывает правильное значение, объясняется тем, что вы вызываете функцию как конструктор, который возвращает экземпляр этого конструктора (т. Е. Новый экземпляр $).Но console.log($().name) также напечатает 'levi', потому что он возвращает глобальный объект со свойством name, то есть window.name.попробуйте $(); console.log(name), и вы увидите, что name теперь является глобальной переменной.Поэтому, если вы не хотите использовать ключевое слово new каждый раз, проверьте, вызывается ли ваша функция как обычная функция или как конструктор для экземпляра (=== instanceof $) внутри функции конструктора.При использовании вышеуказанного метода конструктор экземпляров, независимо от того, был ли он создан с new или без него, всегда будет $

Возможно, вам следует перефразировать заголовок вашего вопроса в: «Объект [конструктор], который возвращает экземпляр самого себя '

Может быть эта запись в блоге может пролить дополнительный свет.

5 голосов
/ 22 мая 2011

Способ, которым jQuery делает это, сначала проверяет, является ли this значением window (вызывается как функция), и, если это так, возвращает новый экземпляр самого себя.Например:

var $ = function() {
    if(this === window) {
        return new $();
    }
    this.name = "levi";
    return this;
};

console.log($());

Это работает, потому что при обычном вызове функции (func()) this будет таким же, как this вызывающего.(Связано, но не имеет значения: obj.method() будет иметь this be obj) Поскольку область по умолчанию window, this внутри $ будет window, когда вы вызовете его как $().

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

Это решение работает, потому что оно сначала тестируетесли this равно window, и, следовательно, было названо как $().Если он был вызван как $(), он вернет new $().В противном случае он был вызван с new $() и будет работать как положено.

4 голосов
/ 22 мая 2011
>  $ = function() {
>     this.name = "levi";
> 
>     return this; };
> 
> console.log($());

Мы получаем DOMWindow вместо $

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

из-за причудливой природы этого в JavaScript

Javascript это ключевое слово работает так, как указано для работы. Он отличается от других языков, но именно так он и работает.

Что для меня более странно, console.log ($ (). name) правильно возвращает "Levi".

Когда вы вызываете $ как функцию, ее ключевое слово this является глобальным объектом, поэтому:

this.name = 'levi';

создает свойство глобального объекта с именем name со значением 'levi'. Не странно, когда знаешь, что происходит. : -)

Мы могли бы просто добавить новый console.log (новый $ ()) и все работает.

Именно так конструкторы должны вызываться в javascript. Когда функция вызывается с new , ее ключевое слово this устанавливается на вновь созданный объект, поэтому this.name создаст новое свойство этого объекта. Кстати, return this является избыточным, конструкторы возвращают это по умолчанию.

> $ = function() {
>     var obj = function() {
>         this.name = "levi";
>     };
> 
>     return new obj(); };

console.log ($ ()); Что дает мне что Я хочу, но это кажется немного ненужным обернуть объект внутри функция, которая создает его. В дальнейшем более того, он имеет тип obj, а не типа $

Предположительно, вы используете typeof , который может возвращать только одно из значений, указанных в ECMA-262. Этот короткий список (который включает в себя объект, число, строку и т. Д.) Не включает $ .

Какими еще способами это может быть сделано?

Вы можете использовать подход, который вы уже обнаружили, клон Лассе Рейхштейна Нильсена (также популяризированный Дубласом Крокфордом как «порождать») и шаблон модуля Ричарда Корнфорда. Используйте Google, есть много, много сообщений обо всем вышеперечисленном.

1 голос
/ 22 мая 2011

Попробуйте что-то вроде этого:

function $(name){
    if( !(this instanceof arguments.callee) ){
        return new arguments.callee(name);
    }
    this.name = name;
    return this;
}

Обновление :

@ Леви Моррисон

Редактировать: Кто-нибудь видит что-то не так?со следующим решением:

Поскольку вы спросили, Я не совсем влюблен в document.write .
Попробуйте вместо этого:

var log = function(message){
    if(message){
        document.body.appendChild(document.createTextNode(message));
    }
    document.body.appendChild(document.createElement('br'));
}
...