jQuery-原始碼閱讀,init()方法
檢視jQuery原始碼可以發現,jQuery中沒有使用new操作符來建立新物件,而是採用呼叫jQuery原型中init()函式的方式返回一個新物件。
1.首先簡單回憶下JavaScript中的原型。
我們建立的每個函式都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。如果按照字面意思來理解,那麼prototype就是通過呼叫建構函式而建立的那個物件例項的原型物件。使用原型物件的好處是可以讓所有物件例項共享它所包含的屬性和方法。換句話說,不必在建構函式中定義物件例項的資訊,而是可以將這些資訊直接新增到原型物件中。
無論什麼時候,只要建立了一個新函式,就會根據一組特定的規則為該函式建立一個prototype屬性,這個屬性指向函式的原型物件。在預設情況下,所有原型物件都會自動獲得一個constructor(建構函式)屬性,這個屬性包含一個指向prototype屬性所在函式的指標。而通過這個建構函式,我們還可繼續為原型物件新增其他屬性和方法。
function A(){ } A.prototype.name="hello"; A.prototype.sayname=function(){ window.alert(this.name); }; var a=new A(); a.sayname(); //hello var b=new A(); b.sayname(); //hello
2.JavaScript中建立物件的幾種方式。
1)工廠模式
function createBook(name,edition,author){
var o={};
o.name=name;
o.edition=edition;
o.author=author;
o.sayName=function (){
console.log(this.name);
};
return o;
}
var book_js=createBook("JavaScript","3","Nicholas");
book_js.sayName();
缺點:工廠模式雖然解決了建立多個相似物件的程式碼複用問題,但是卻沒有解決物件的識別問題。
2)建構函式模式
function Book(name,edition,author){
this.name=name;
this.edition=edition;
this.author=author;
this.sayName=function (){
console.log(this.name);
};
}
var book_css=new Book("CSS","3","Charles");
book_css.sayName();
console.log(book_css instanceof Book); //true
缺點:每個方法都要在買個例項上建立一遍。
3)原型模式
//一種形式
function Book(){}
Book.prototype.name="Python";
Book.prototype.edition="2";
Book.prototype.author="Magnus";
Book.prototype.sayName=function(){
console.log(this.name);
};
var book_python=new Book();
book_python.sayName();
//另一種形式
function Book(){}
Book.prototype={
constructor:Book, //會導致constructor變為可列舉屬性,解決方案見下方註釋
name:"Python",
edition:"2",
author:"Magnus",
sayName:function(){
console.log(this.name);
}
};
//ECMAScript5 : Object.defineProperty(Book.prototype,"constructor",{enumerable:false, value:Book});
var book_python=new Book();
book_python.sayName();
缺點:所有例項在預設情況下都將取得相同的值,沒有引數傳遞環節;某個例項對於建構函式原型中屬性的修改會影響其他所有例項。
function Book(){}
Book.prototype={
constructor:Book, //會導致constructor變為可列舉屬性,解決方案見下方註釋
name:"Python",
edition:"2",
author:["Magnus"],
sayName:function(){
console.log(this.name);
}
};
//ECMAScript5 : Object.defineProperty(Book.prototype,"constructor",{enumerable:false, value:Book});
var book_python1=new Book();
var book_python2=new Book();
book_python1.author.push("lie");
console.log(book_python2.author);//["Magnus", "lie"]
4)組合使用建構函式模式和原型模式
function Book(name,edition,author){
this.name=name;
this.edition=edition;
this.author=[author];
}
Book.prototype={
constructor:Book,
sayName:function(){
console.log(this.name);
},
sayAuthor:function(){
console.log(this.author);
}
};
var book_ajax=new Book("Ajax","1","Riordan");
var book_php=new Book("PHP","4","Luke");
book_ajax.sayAuthor();//["Riordan"]
book_ajax.author.push("蘇金國");
book_ajax.sayAuthor();//["Riordan", "蘇金國"]
book_php.sayAuthor();//["Luke"]
缺點:只要使用原型,就會使得在呼叫原型上方法時,在作用域鏈上多搜尋一層。
基於以上分析,jQuery物件的構建如果在效能上考慮,所以就必須採用原型式的結構:
jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context );
}
jQuery.fn = jQuery.prototype = {
init:function(){
return this
},
jquery: version,
constructor: jQuery,
………………
}
var a = $() ;
或者可以寫出一個更接近jQuery原始碼的實現:
( function(window){
var ajQuery = function(selector) {
//把原型上的init作為構造器
return new ajQuery.fn.init( selector );
};
ajQuery.fn = ajQuery.prototype = {
name: 'aaron',
init: function() {
console.log(this);
},
constructor: ajQuery
};
window.ajQuery=ajQuery;
}(window)
);
console.log(typeof ajQuery);//function
var aobj=ajQuery();//a…y.fn.a…y.init {}
console.log(typeof aobj);//object
注意,原始碼中還傳入了undefined,作用是為了防止undefined在全域性中被修改和縮短作用域鏈的查詢。