Câu chuyện về hàm $ trong jQuery

Khi sử dụng JavaScript tương tác với các thành phần của DOM, một trong những thao tác phổ biến nhất đó là tìm kiếm một phần tử của DOM thỏa mãn một điều kiện nào đó.

Ví dụ cách đơn giản nhất để tìm kiếm một element có thuộc tính id:

var el=document.getElementById(“foo”);

Tuy nhiên nếu câu truy vấn của tôi phức tạp hơn: tìm một element có thuộc tính class mang giá trị “blink” v.v…
Hay khó hơn nữa: tìm kiếm một element tuân theo một biểu thức quy tắc nào đó.
Khi đó câu lệnh tìm kiếm không còn đơn giản như trên nữa.

Thông tin nằm trong tài liệu HTML rất phong phú, để khai thác được chúng thì việc tất yếu đối với lập trình viên là phải thiết lập một hàm tìm kiếm thống nhất, đơn giản để đáp ứng mọi nhu cầu tìm kiếm trong chương trình.

Và một trong số những hàm nổi tiếng nhất trong các hàm javascript đã ra đời: hàm $() hay còn gọi là hàm nguyên mẫu (lần đầu tiên xuất hiện trong Prototype.js). Sau đây là phiên bản đơn giản của hàm $:

function $() {
    var elements = new Array();
    for (var i = 0; i < arguments.length; i++) {
        var element = arguments[i];
        if (typeof element == 'string')
            element = document.getElementById(element);
        if (arguments.length == 1)
            return element;
        elements.push(element);
    }
    return elements;
}

// Ví dụ:
var obj1 = document.getElementById('element1');
var obj2 = document.getElementById('element2');
function alertElements() {
    var i;
    var elements = $('a','b','c',obj1,obj2,'d','e');
    for(i=0;i<elements.length;i++) {
         alert(elements[i]);
    }
}

Chạy thử

Rất đẹp! Bạn có thể thấy hàm $ trên có thể nhận một hay nhiều tham số, hoặc truyền vào id của element hay thậm chí là object. Và kết quả luôn trả về các element thỏa mãn.


Một phiên bản đầy đủ hơn của $ function có thể tìm thấy trong JQuery framework:

var jQuery=function( selector, context, rootjQuery ) {
    var match, elem, ret, doc;

    // Handle $(""), $(null), or $(undefined)
    if ( !selector ) {
        return this;
    }

    // Handle $(DOMElement)
    if ( selector.nodeType ) {
        this.context = this[0] = selector;
        this.length = 1;
        return this;
    }

    // The body element only exists once, optimize finding it
    if ( selector === "body" && !context && document.body ) {
        this.context = document;
        this[0] = document.body;
        this.selector = selector;
        this.length = 1;
        return this;
    }

    // Handle HTML strings
    if ( typeof selector === "string" ) {
    // Are we dealing with HTML string or an ID?
    if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
        // Assume that strings that start and end with <> are HTML and skip the regex check
        match = [ null, selector, null ];

    } else {
        match = quickExpr.exec( selector );
    }

    // Verify a match, and that no context was specified for #id
    if ( match && (match[1] || !context) ) {

         // HANDLE: $(html) -> $(array)
        if ( match[1] ) {
             context = context instanceof jQuery ? context[0] : context;
             doc = ( context ? context.ownerDocument || context : document );

             // If a single string is passed in and it's a single tag
             // just do a createElement and skip the rest
             ret = rsingleTag.exec( selector );

             if ( ret ) {
                 if ( jQuery.isPlainObject( context ) ) {
                     selector = [ document.createElement( ret[1] ) ];
                     jQuery.fn.attr.call( selector, context, true );

                 } else {
                     selector = [ doc.createElement( ret[1] ) ];
                 }

             } else {
                 ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
                 selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
             }

             return jQuery.merge( this, selector );

             // HANDLE: $("#id")
         } else {
             elem = document.getElementById( match[2] );

             // Check parentNode to catch when Blackberry 4.6 returns
             // nodes that are no longer in the document #6963
             if ( elem && elem.parentNode ) {
                 // Handle the case where IE and Opera return items
                 // by name instead of ID
                 if ( elem.id !== match[2] ) {
                      return rootjQuery.find( selector );
                 }

                 // Otherwise, we inject the element directly into the jQuery object
                 this.length = 1;
                 this[0] = elem;
             }

             this.context = document;
             this.selector = selector;
             return this;
         }

     // HANDLE: $(expr, $(...))
     } else if ( !context || context.jquery ) {
         return ( context || rootjQuery ).find( selector );

     // HANDLE: $(expr, context)
     // (which is just equivalent to: $(context).find(expr)
     } else {
          return this.constructor( context ).find( selector );
     }

     // HANDLE: $(function)
     // Shortcut for document ready
  } else if ( jQuery.isFunction( selector ) ) {
      return rootjQuery.ready( selector );
  }

  if ( selector.selector !== undefined ) {
       this.selector = selector.selector;
       this.context = selector.context;
  }

   return jQuery.makeArray( selector, this );
}
…
// Expose jQuery to the global object: $
window.jQuery = window.$ = jQuery;

Dòng cuối cùng trong đoạn mã trên thực chất là để “trả lại tên cho em” ($=jQuery). Nếu lướt qua đám comment trong hàm $ này ta có thể thấy các chức năng chính của nó gồm:
// Handle $(“”), $(null), or $(undefined)
// Handle $(DOMElement)
// Handle HTML strings
// HANDLE: $(“#id”)
// HANDLE: $(expr, $(…))
// HANDLE: $(expr, context)
// HANDLE: $(function)

Như vậy hàm $ trong jQuery thực sự là một hàm tìm kiếm đa năng, với tham số đầu vào từ Html string cho đến DOM element hay thậm chí là function. Hàm $ của jQuery còn cung cấp một khả năng truy vấn dựa trên biểu thức CSS selector . Có thể hình dung hàm $ trong jQuery giống như ô tìm kiếm trên trang chủ Google vậy! Chỉ cần search(“keyword”) là có ngay những gì mình cần 🙂

Đến đây, chắc hẳn các bạn lập trình viên sẽ cảm thấy thoải mái với hàm $, không chỉ bởi vì nó là $, mà còn bởi sử dụng nó sẽ tiết kiệm rất nhiều thời gian và đem lại vô số lợi ích thành tiền, thành $ thật sự 😀

Trong các bài viết tới chúng ta sẽ có dịp quay trở lại nói chuyện kỹ hơn về $, cách xài $ v.v… Nếu không muốn chờ đợi, các bạn có thể tham khảo về $ tại đây.

Tham khảo:

Mã nguồn jQuery 1.7.2 : http://code.jquery.com/jquery-1.7.2.js

2 thoughts on “Câu chuyện về hàm $ trong jQuery

  1. Mình đọc bài trên và có một thắc mắc là:
    Hàm $() kiểu đơn giản ấy, thực sự để sử dụng nó thì mình phải định nghĩa lại hàm đó (kiểu giống như của bạn – copy code bỏ vào) hay là sử dụng cái của jquery. Bởi mình có thử 2 cách và kết quả chúng trả về là khác nhau.
    Hi, mình mới tập tành js thôi, nên chưa hiểu. Bạn thông cảm ^^.

    Code của mình:
    Mình dùng source này, ko biết có ảnh hưởng ko ?
    src=”http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js”>

    function alertElements()
    {
    var obj1 = document.getElementById(‘element1’);
    var obj2 = document.getElementById(‘element2’);

    document.write(“”+ obj1 + obj2 +””);
    var elements = $(obj1,obj2);
    document.write(“”+elements.length+””);
    for(var i=0;i<elements.length;i++)
    {
    document.write("”+elements[i]+””);
    }
    }

    Nếu xài $ của jquerry thì nó trả về elements.length = 1;
    Nếu xài $ của tự định nghĩa thì nó trả về elements.length = 2; (@.@)

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *