Создание jQuery-подобного объекта "$" - PullRequest
11 голосов
/ 29 декабря 2010

Моя конечная цель - сделать что-то вроде этого:

MyVar(parameter).functionToPerform();

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

Это то, что я пробовал до сих пор, но не получается:

var MyClass = function(context) {
this.print = function(){
    console.log("Printing");
}

this.move = function(){
    console.log(context);
}
};

var test = new MyClass();
test.print(); // Works
console.log('Moving: ' + test('azerty').move() ); // Type property error

Ответы [ 8 ]

15 голосов
/ 30 декабря 2010

Когда я пишу это, ответ Squeegy имеет наибольшее количество голосов: 7. И все же это неправильно, потому что __proto__ не является стандартным и не поддерживается Internet Explorer (даже версии 8). Тем не менее, избавление от __proto__ не работает и в IE 6.

Это (несколько упрощенно) способ, которым jQuery фактически делает это ( даже пробует его в IE 6 ), и он также включает примеры статических методов и цепочки методов. Для того, чтобы узнать все подробности о том, как это делает jQuery, вам, конечно, придется проверить исходный код jQuery .

var MyClass = function(context) {
    // Call the constructor
    return new MyClass.init(context);
};

// Static methods
MyClass.init = function(context) {
    // Save the context
    this.context = context;
};
MyClass.messageBox = function(str) {
    alert(str);
};


// Instance methods
MyClass.init.prototype.print = function() {
    return "Printing";
};
MyClass.init.prototype.move = function() {
    return this.context;
};

// Method chaining example
MyClass.init.prototype.flash = function() {
    document.body.style.backgroundColor = '#ffc';
    setInterval(function() {
        document.body.style.backgroundColor = '';
    }, 5000);
    return this;
};


$('#output').append('<li>print(): '+ MyClass().print() +'</li>');
$('#output').append('<li>flash().move():'+ MyClass('azerty').flash().move() +'</li>');
$('#output').append('<li>context: '+ MyClass('azerty').context +'</li>');
MyClass.messageBox('Hello, world!');

Обратите внимание, что если вам нужны "частные" данные, вам придется поместить методы экземпляра в MyClass.init (с переменной, объявленной только внутри этой функции) как this.print = function() { ... }; вместо использования MyClass.init.prototype.

13 голосов
/ 29 декабря 2010

jQuery() - это и модуль с глобальными методами, и конструктор.Он автоматически вызывает конструктор, если это необходимо.Если нас не вызывают с ключевым словом new, то this не будет создан с MyClass.Мы можем обнаружить это и вызвать функцию в режиме конструктора.Как только мы это сделаем, this будет экземпляром MyClass, и мы сможем начать добавлять к нему материал.

1 голос
/ 29 декабря 2010

каждый раз, когда вы вызываете $ в jQuery, он возвращает новый jQuery.init объект. У объекта jQuery.init есть функции, которые затем вызываются.

function test(type)
{
  switch (type)
  {
    case 'azerty':
      return new type.a();
    case 'qwerty':
    default:
      return new type.b();
  }
}

test.a = function()
{
  //a object defined
};

test.a.prototype.move = function()
{
  //move function defined for the a object
};

etc...

Я только что набрал это на лету, так что может понадобиться немного подправить

Это позволит вам позвонить test('azerty').move();. Что еще более важно: я надеюсь, что вы видите общую используемую структуру.

Изменить, чтобы добавить:

Чтобы продолжить связывание функций, как в jQuery, убедитесь, что вы возвращаете объект this в конце каждого вызова функции:

test.a.prototype.move = function()
{
  //move function defined for the a object
  return this;
};
1 голос
/ 29 декабря 2010

когда вы делаете

var test = new MyClass()

, вы создаете объект, который имеет два атрибута move и print.Ваш объект test больше не является функцией из-за оператора new.Так что звонить test() неправильно.

0 голосов
/ 20 июня 2018

Представленные решения пока не отражают точную структуру jQuery (или jQuery изменился с течением времени).

Недавно я хотел создать объект (похожий на jQuery) и натолкнулся на этот вопрос. Итак, вот мой ответ:

// dummy text element 
var p = document.createElement("p");
p.innerText = "Lorem ipsum...";


// jQuery-like object
var CustomObject = function (element) {
    return new CustomObject.prototype.init(element);
};

// constructor
CustomObject.prototype.init = function (element) {
    this.el = element;
    this.text = element.innerText;
};

// instance methods
CustomObject.prototype.log = function () {
    console.log(this.text);
    // by returning "this" at the end of instance methods
    // we make these methods chainable
    return this;
};

CustomObject.prototype.add2body = function (delay) {
    document.body.appendChild(this.el);
    return this;
};

// all the instance methods are added to CustomObject, not to CustomObject.init (constructor)
// calling CustomObject() returns a CustomObject.init object, not CustomObject object
// so by default, instance methods are not accessible
// to fix this, you need to assign the prototype of CustomObject to CustomObject.prototype.init
CustomObject.prototype.init.prototype = CustomObject.prototype;

// testing
var obj = CustomObject(p).add2body().log();
0 голосов
/ 13 января 2018

Я недавно работал над упражнением, в котором предлагалось воссоздать JQuery-подобную библиотеку, используя только ES6.

Из приведенных выше пунктов мне удалось создать логику создания экземпляров, которая позволяет вызывать методы для селекторов, например, имена классов.

Когда экземпляр $ T вызывается с помощью селектора, новый экземпляр $ TLib создается на конкретном селекторе, который будет содержать все методы, которые можно использовать на селекторе с элементами в контексте.

В приведенном ниже примере я добавил несколько методов, которые можно объединить в цепочку, что позволяет добавить класс CSS к элементу и удалить один и тот же класс за один вызов, например:

$T('.class-selector').addClass('green').removeClass('green);

Для тех, кто хочет запустить нечто подобное:

const $T = (selector) => {
  // returns the HTML elements that match the selector
  const elements = document.querySelectorAll(selector);
  return new $TLib(elements, selector);
};

class $TLib {
  constructor (elements) {
    this._elements = elements;
  }

  addClass (className) {
    this._elements.forEach((element) => element.classList.add(className));
    return this;
  }

  removeClass (className) {
    this._elements.forEach((element) => element.classList.remove(className));
    return this;
  }

  toggleClass (className) {
    this._elements.forEach((element) => {
      const classList = element.classList;
      (classList.contains(className)) ? classList.remove(className) : classList.add(className);
    });
    return this;
  }

}
0 голосов
/ 16 декабря 2015

[Вероятно] Самое элегантное решение

Во-первых, jQuery использует шаблон, который ближе к Монаде , Factory или их комбинации.,Тем не менее, вот что я использовал в своих проектах, потому что сам шаблон так слабо связан с любым классом, который вы хотели бы использовать:

;(function (undefined) {
    if (undefined) return;
    var ENV = this;

    var Class = function Class() {
        var thus = this;

        function find(data) {
            console.log('@find #data', data);
            return this;
        }

        function show(data) {
            console.log('@show #data', data);
            return this;
        }

        // export precepts
        this.find = find;
        this.show = show;

        return this;
    };

    var Namespace = ENV['N'] = new (function Namespace(Class) {
        var thus = this;

        var Ns = Class.apply(function Ns(data) {

            if (this instanceof N) {
              return new Namespace(Class);
            }

            return Ns.find.apply(Ns, arguments);
        });


        return Ns;
    })(Class);

}).call(window || new function Scope() {});

var n = N('#id').show(450);
var m = new N();

m('#id')('.curried').show('slow');

console.log(n !== m);  // >> true

По сути, вы можете использовать его как функциюобъекта и используйте ключевое слово new для создания другого уникального объекта / функции.Вы можете использовать это для принудительного применения метода арбитр (по умолчанию, как метод find выше) или использовать другие методы в зависимости от того, какие параметры вводятся.Например, вы можете сделать что-то вроде:

var elementsList = N('#id1')('#id2')('#otherSpecialElement').each(fn);

- ИЛИ -

var general = N('.things');
var specific = general('.specific')('[data-more-specific]').show();

Выше, например, будет накапливаться список узлов из нескольких элементов (1-е выражение),или развернуть до одного конкретного элемента (2-й).

Надеюсь, это поможет

0 голосов
/ 29 декабря 2010

Вы можете передать значение 'azerty' как параметр конструктору MyClass и изменить тест ('azerty'). Move () на test.move ()

<script type="text/javascript">
    var MyClass = function(context) { 
        this.print = function(){     
            console.log("Printing"); 
        }  
        this.move = function(){     
        console.log(context); 
        return context;
        } 
    };  
    var test = new MyClass('azerty'); 
    test.print(); // Works 
    console.log('Moving: ' + test.move() ); // Type property error 
</script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...