国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

實(shí)例詳解jQuery的無new構(gòu)建

2019-11-20 09:19:46
字體:
供稿:網(wǎng)友

jQuery的無new構(gòu)建

jQuery框架的核心就是從HTML文檔中匹配元素并對(duì)其執(zhí)行操作、

回想一下使用 jQuery 的時(shí)候,實(shí)例化一個(gè) jQuery 對(duì)象的方法:

// 無 new 構(gòu)造$('#test').text('Test'); // 當(dāng)然也可以使用 newvar test = new $('#test');test.text('Test');

大部分人使用 jQuery 的時(shí)候都是使用第一種無 new 的構(gòu)造方式,直接 $('') 進(jìn)行構(gòu)造,這也是 jQuery 十分便捷的一個(gè)地方。

當(dāng)我們使用第一種無 new 構(gòu)造方式的時(shí)候,其本質(zhì)就是相當(dāng)于 new jQuery(),那么在 jQuery 內(nèi)部是如何實(shí)現(xiàn)的呢?看看:

(function(window, undefined) {  var  // ...  jQuery = function(selector, context) {    // The jQuery object is actually just the init constructor 'enhanced'    return new jQuery.fn.init(selector, context, rootjQuery);  },   jQuery.fn = jQuery.prototype = {    init: function(selector, context, rootjQuery) {      // ...    }  }  jQuery.fn.init.prototype = jQuery.fn;})(window);

 沒看懂?沒關(guān)系,我們一步一步分析。

函數(shù)表達(dá)式和函數(shù)聲明

在ECMAScript中,創(chuàng)建函數(shù)的最常用的兩個(gè)方法是函數(shù)表達(dá)式和函數(shù)聲明,兩者期間的區(qū)別是有點(diǎn)暈,因?yàn)镋CMA規(guī)范只明確了一點(diǎn):函數(shù)聲明必須帶有標(biāo)示符(Identifier)(就是大家常說的函數(shù)名稱),而函數(shù)表達(dá)式則可以省略這個(gè)標(biāo)示符: 

   //函數(shù)聲明:  function 函數(shù)名稱 (參數(shù):可選){ 函數(shù)體 }  //函數(shù)表達(dá)式:  function 函數(shù)名稱(可選)(參數(shù):可選){ 函數(shù)體 }

所以,可以看出,如果不聲明函數(shù)名稱,它肯定是表達(dá)式,可如果聲明了函數(shù)名稱的話,如何判斷是函數(shù)聲明還是函數(shù)表達(dá)式呢?

ECMAScript是通過上下文來區(qū)分的,如果function foo(){}是作為賦值表達(dá)式的一部分的話,那它就是一個(gè)函數(shù)表達(dá)式,

如果function foo(){}被包含在一個(gè)函數(shù)體內(nèi),或者位于程序的最頂部的話,那它就是一個(gè)函數(shù)聲明。

 function foo(){} // 聲明,因?yàn)樗浅绦虻囊徊糠?var bar = function foo(){}; // 表達(dá)式,因?yàn)樗琴x值表達(dá)式的一部分 new function bar(){}; // 表達(dá)式,因?yàn)樗莕ew表達(dá)式 (function(){  function bar(){} // 聲明,因?yàn)樗呛瘮?shù)體的一部分 })();

還有一種函數(shù)表達(dá)式不太常見,就是被括號(hào)括住的(function foo(){}),他是表達(dá)式的原因是因?yàn)槔ㄌ?hào) ()是一個(gè)分組操作符,它的內(nèi)部只能包含表達(dá)式

 再來看jQuery源碼:

(function(window, undefined) {  /...})(window)

可以將上面的代碼結(jié)構(gòu)分成兩部分:(function(){window, undefined}) (window) ,

第1個(gè)()是一個(gè)表達(dá)式,而這個(gè)表達(dá)式本身是一個(gè)匿名函數(shù),

所以在這個(gè)表達(dá)式后面加(window)就表示執(zhí)行這個(gè)匿名函數(shù)并傳入?yún)?shù)window。

原型 prototype

認(rèn)識(shí)一下什么是原型?

在JavaScript中,原型也是一個(gè)對(duì)象,通過原型可以實(shí)現(xiàn)對(duì)象的屬性繼承,JavaScript的對(duì)象中都包含了一個(gè)" [[Prototype]]"內(nèi)部屬性,這個(gè)屬性所對(duì)應(yīng)的就是該對(duì)象的原型。

對(duì)于"prototype"和"__proto__"這兩個(gè)屬性有的時(shí)候可能會(huì)弄混,"Person.prototype"和"Person.__proto__"是完全不同的。

在這里對(duì)"prototype"和"__proto__"進(jìn)行簡(jiǎn)單的介紹:

    1.對(duì)于所有的對(duì)象,都有__proto__屬性,這個(gè)屬性對(duì)應(yīng)該對(duì)象的原型

    2.對(duì)于函數(shù)對(duì)象,除了__proto__屬性之外,還有prototype屬性,當(dāng)一個(gè)函數(shù)被用作構(gòu)造函數(shù)來創(chuàng)建實(shí)例時(shí),該函數(shù)的prototype屬性值將被作為原型賦值給所有對(duì)象實(shí)例(也就是設(shè)置實(shí)例的__proto__屬性)

function Person(name, age){  this.name = name;  this.age = age;}Person.prototype.getInfo = function(){  console.log(this.name + " is " + this.age + " years old");};//調(diào)用var will = new Person("Will", 28);will.getInfo();//"Will is 28 years old"

閉包

閉包的定義:

當(dāng)一個(gè)內(nèi)部函數(shù)被其外部函數(shù)之外的變量引用時(shí),就形成了一個(gè)閉包。

閉包的作用:

在了解閉包的作用之前,我們先了解一下 javascript中的GC機(jī)制:

在javascript中,如果一個(gè)對(duì)象不再被引用,那么這個(gè)對(duì)象就會(huì)被GC回收,否則這個(gè)對(duì)象一直會(huì)保存在內(nèi)存中。

在上述例子中,B定義在A中,因此B依賴于A,而外部變量 c 又引用了B, 所以A間接的被 c 引用,

也就是說,A不會(huì)被GC回收,會(huì)一直保存在內(nèi)存中。為了證明我們的推理,看如下例子:

function A(){  var count = 0;  function B(){    count ++;    console.log(count);  }  return B;}var c = A();c();// 1c();// 2c();// 3

count是A中的一個(gè)變量,它的值在B中被改變,函數(shù)B每執(zhí)行一次,count的值就在原來的基礎(chǔ)上累加1。因此,A中的count一直保存在內(nèi)存中。

這就是閉包的作用,有時(shí)候我們需要一個(gè)模塊中定義這樣一個(gè)變量:希望這個(gè)變量一直保存在內(nèi)存中但又不會(huì)“污染”全局的變量,這個(gè)時(shí)候,我們就可以用閉包來定義這個(gè)模塊

在看jQuery源碼:

(function(window, undefined) {  var  // ...  jQuery = function(selector, context) {    // The jQuery object is actually just the init constructor 'enhanced'    return new jQuery.fn.init(selector, context, rootjQuery);  },  jQuery.fn = jQuery.prototype = {    init: function(selector, context, rootjQuery) {      // ...    }  }  jQuery.fn.init.prototype = jQuery.fn;})(window);
 

我們知道了 什么是閉包:當(dāng)一個(gè)內(nèi)部函數(shù)被其外部函數(shù)之外的變量引用時(shí),就形成了一個(gè)閉包。

jQuery.fn的init 函數(shù)被jQuery 的構(gòu)造函數(shù)調(diào)用了,這里形成了一個(gè)閉包。
構(gòu)造函數(shù)及調(diào)用代碼:

// ...  jQuery = function(selector, context) {    // The jQuery object is actually just the init constructor 'enhanced'    return new jQuery.fn.init(selector, context, rootjQuery);  },

問題關(guān)鍵來了。

如何實(shí)現(xiàn)無new構(gòu)建

JavaScript是函數(shù)式語言,函數(shù)可以實(shí)現(xiàn)類,類就是面向?qū)ο缶幊讨凶罨镜母拍?/p>

var aQuery = function(selector, context) {    //構(gòu)造函數(shù)}aQuery.prototype = {  //原型  name:function(){},  age:function(){}}var a = new aQuery();a.name();
 

這是常規(guī)的使用方法,顯而易見jQuery不是這樣玩的

要實(shí)現(xiàn)這樣,那么jQuery就要看成一個(gè)類,那么$()應(yīng)該是返回類的實(shí)例才對(duì)

按照jQuery的抒寫方式

$().ready() $().noConflict()

要實(shí)現(xiàn)這樣,那么jQuery就要看成一個(gè)類,那么$()應(yīng)該是返回類的實(shí)例才對(duì)

所以把代碼改一下:

var aQuery = function(selector, context) {    return new aQuery();}aQuery.prototype = {  name:function(){},  age:function(){}}

通過new aQuery(),雖然返回的是一個(gè)實(shí)例,但是也能看出很明顯的問題,死循環(huán)了

那么如何返回一個(gè)正確的實(shí)例?

在javascript中實(shí)例this只跟原型有關(guān)系

那么可以把jQuery類當(dāng)作一個(gè)工廠方法來創(chuàng)建實(shí)例,把這個(gè)方法放到aQuery.prototye原型中

var aQuery = function(selector, context) {    return aQuery.prototype.init(selector);}aQuery.prototype = {  init:function(selector){    return this;  }  name:function(){},  age:function(){}}

當(dāng)執(zhí)行aQuery() 返回的實(shí)例:

很明顯aQuery()返回的是aQuery類的實(shí)例,那么在init中的this其實(shí)也是指向的aQuery類的實(shí)例

問題來了init的this指向的是aQuery類,如果把init函數(shù)也當(dāng)作一個(gè)構(gòu)造器,那么內(nèi)部的this要如何處理?

var aQuery = function(selector, context) {    return aQuery.prototype.init(selector);}aQuery.prototype = {  init: function(selector) {    this.age = 18    return this;  },  name: function() {},  age: 20}aQuery().age //18

 因?yàn)閠his只是指向aQuery類的,所以aQueryage屬性是可以被修改的。

這樣看似沒有問題,其實(shí)問題很大的

為什么是new jQuery.fn.init?

看如下代碼:

var aQuery = function(selector, context) {    return aQuery.prototype.init(selector);}aQuery.prototype = {  init: function(selector) {    if(selector=="a")      this.age = 18    return this;  },  name: function() {},  age: 20}aQuery("a").age //18aQuery("b").age //18
 

當(dāng)我調(diào)用 傳入"a"的時(shí)候,修改age=18,及aQuery("a").age 的值為18

但是當(dāng)我  傳入"b"的時(shí)候 并沒又修改 age的值,我也希望得到默認(rèn)age的值20,但是aQuery("b").age 的值為18.

因?yàn)樵?調(diào)用aQuery("a").age 的時(shí)候age被修改了。

這樣的情況下就出錯(cuò)了,所以需要設(shè)計(jì)出獨(dú)立的作用域才行。

jQuery框架分隔作用域的處理

jQuery = function( selector, context ) {    // The jQuery object is actually just the init constructor 'enhanced'    return new jQuery.fn.init( selector, context, rootjQuery );  },

很明顯通過實(shí)例init函數(shù),每次都構(gòu)建新的init實(shí)例對(duì)象,來分隔this,避免交互混淆

我們修改一下代碼:

var aQuery = function(selector, context) {    return new aQuery.prototype.init(selector);}aQuery.prototype = {  init: function(selector) {    if(selector=="a")      this.age = 18    return this;  },  name: function() {},  age: 20}aQuery("a").age //18aQuery("b").age //undefinedaQuery("a").name() //Uncaught TypeError: Object [object Object] has no method 'name' 

又出現(xiàn)一個(gè)新的問題,

age  :undefined,

name() :拋出錯(cuò)誤,無法找到這個(gè)方法,所以很明顯new的init跟jquery類的this分離了

怎么訪問jQuery類原型上的屬性與方法?

     做到既能隔離作用域還能使用jQuery原型對(duì)象的作用域呢,還能在返回實(shí)例中訪問jQuery的原型對(duì)象?

實(shí)現(xiàn)的關(guān)鍵點(diǎn)

// Give the init function the jQuery prototype for later instantiationjQuery.fn.init.prototype = jQuery.fn;

我們?cè)俑囊幌拢?/p>

var aQuery = function(selector, context) {    return new aQuery.prototype.init(selector);}aQuery.prototype = {  init: function(selector) {    if(selector=="a")      this.age = 18    return this;  },  name: function() {     return age;  },  age: 20}aQuery.prototype.init.prototype = aQuery.prototype; aQuery("a").age //18aQuery("b").age //20aQuery("a").name()  //20

最后在看一下jQuery源碼:

(function(window, undefined) {  var  // ...  jQuery = function(selector, context) {    // The jQuery object is actually just the init constructor 'enhanced'    return new jQuery.fn.init(selector, context, rootjQuery);  },  jQuery.fn = jQuery.prototype = {    init: function(selector, context, rootjQuery) {      // ...    }  }  jQuery.fn.init.prototype = jQuery.fn;})(window);

是不是明白了?

哈哈哈~~~

在簡(jiǎn)單說兩句:

大部分人初看 jQuery.fn.init.prototype = jQuery.fn 這一句都會(huì)被卡主,很是不解。但是這句真的算是 jQuery 的絕妙之處。理解這幾句很重要,分點(diǎn)解析一下:

1)首先要明確,使用 $('xxx') 這種實(shí)例化方式,其內(nèi)部調(diào)用的是 return new jQuery.fn.init(selector, context, rootjQuery) 這一句話,也就是構(gòu)造實(shí)例是交給了 jQuery.fn.init() 方法取完成。

2)將 jQuery.fn.init 的 prototype 屬性設(shè)置為 jQuery.fn,那么使用 new jQuery.fn.init() 生成的對(duì)象的原型對(duì)象就是 jQuery.fn ,所以掛載到 jQuery.fn 上面的函數(shù)就相當(dāng)于掛載到 jQuery.fn.init() 生成的 jQuery 對(duì)象上,所有使用 new jQuery.fn.init() 生成的對(duì)象也能夠訪問到 jQuery.fn 上的所有原型方法。

3)也就是實(shí)例化方法存在這么一個(gè)關(guān)系鏈 

    1.jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;

    2.new jQuery.fn.init() 相當(dāng)于 new jQuery() ;

    3.jQuery() 返回的是 new jQuery.fn.init(),而 var obj = new jQuery(),所以這 2 者是相當(dāng)?shù)模晕覀兛梢詿o new 實(shí)例化 jQuery 對(duì)象。

總結(jié)

以上就是jQuery的無new構(gòu)建的全部?jī)?nèi)容,希望本文對(duì)大家學(xué)習(xí)jQuery有所幫助。也請(qǐng)大家繼續(xù)支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 伊春市| 大丰市| 淄博市| 航空| 泰和县| 海南省| 阿荣旗| 乌兰浩特市| 洛阳市| 滨州市| 酒泉市| 怀安县| 咸丰县| 阜城县| 西贡区| 宁都县| 普陀区| 阿瓦提县| 武安市| 阿图什市| 溆浦县| 承德市| 浦东新区| 沙田区| 宁蒗| 林口县| 龙海市| 临高县| 获嘉县| 衡东县| 溧水县| 台湾省| 荥经县| 余江县| 衡阳县| 商南县| 澜沧| 堆龙德庆县| 永城市| 邵阳县| 莲花县|