摘要:一些額外的全局函數(shù)命名空間對(duì)象接口和構(gòu)造函數(shù)與沒(méi)有典型的關(guān)聯(lián),但卻是有效的。最后有幾點(diǎn)需要說(shuō)明的是每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針。
寫這篇文章的目的很簡(jiǎn)單,就是想把之前一些不太清晰的概念梳理一下,網(wǎng)上這類教程很多,但是本文盡可能還原問(wèn)題本質(zhì),注意知識(shí)點(diǎn)之間的聯(lián)系。相信看過(guò)我前面的博客的朋友一定知道我寫文章的風(fēng)格了,盡可能詳盡,而且不是只是為了解決某一個(gè)小問(wèn)題而寫,方便大家知識(shí)點(diǎn)更體系,一篇內(nèi)容其實(shí)相當(dāng)于一章節(jié)的內(nèi)容,容量有點(diǎn)大,我也不是一天完成的,一般是一周時(shí)間左右,所以大家閱讀的話可能也需要一些時(shí)間才能有所收獲。作為進(jìn)階教程,本文將簡(jiǎn)要講述JavaScript面向?qū)ο缶幊痰膬?nèi)容,但是不會(huì)介紹什么是接口,什么是對(duì)象,什么是對(duì)象屬性,什么是對(duì)象方法,但是相信你看完了后自然理所當(dāng)然的理解了這些基本概念。
在開始學(xué)習(xí)之前我們首先用一個(gè)工具,就是瀏覽器自帶的開發(fā)者工具控制臺(tái)。我這里用hbuider直接打開這個(gè)工具,在【web瀏覽器】預(yù)覽工具欄右鍵單擊會(huì)彈出一個(gè)框框,在這個(gè)框框中選擇【Console】,然后在命令行輸入js代碼我們就可以看到執(zhí)行結(jié)果,這里我們先輸入一個(gè)window ,然后會(huì)發(fā)現(xiàn)有json 結(jié)構(gòu)的內(nèi)容。
這么說(shuō)相信新手應(yīng)該沒(méi)啥感覺(jué),最好還是舉個(gè)例子說(shuō)說(shuō),比如我們?nèi)コ燥堃c(diǎn)菜,Window說(shuō)白了一個(gè)菜單,window是端上桌子的那道菜,至于這道菜色香味以及制作方法和Window無(wú)關(guān),只和window有關(guān)。
window.document指向document對(duì)象的引用,document對(duì)象是Document 接口接口的具體實(shí)現(xiàn)。Document 接口代表在瀏覽器及服務(wù)器中加載任意 web 頁(yè)面,也作為 web 頁(yè)面內(nèi)容(DOM tree, 包含如
雖然這段話看起來(lái),但是實(shí)際上意思很簡(jiǎn)單: 如果我們要想獲取一個(gè)document的內(nèi)容,我們可以使用document對(duì)象下的方法屬性和方法去獲取,比如獲取標(biāo)題:
hello world
我們這里的document為啥不加window也可以彈出結(jié)果呢,因?yàn)閣indow為頂層對(duì)象,這里可以忽略不寫,比如alert() 方法其實(shí)是window.alert() 下的方法,我們這里不寫window,一樣可以得到結(jié)果。另外,我們這里只是獲取了title,至于其他的內(nèi)容,那就要學(xué)習(xí)document的屬性和方法。
__proto__ 屬性(原型指針) 和 prototype屬性(原型對(duì)象)
說(shuō)到這兩個(gè)屬性,我們真的很糾結(jié),這兩者到底有什么聯(lián)系和區(qū)別呢?我們先看下面的例子:
> window.prototype === window.__proto__ // false
> Window.prototype === window.__proto__ // true
> window.constructor === Window // true
> Window.__proto__.__proto__.__proto__.__proto__ // null
臥槽,這是什么鬼?prototype 和__proto__ 到底分別各自指什么,Window鏈?zhǔn)秸{(diào)用__proto__ 怎么最后會(huì)變成null? 似乎說(shuō)到這里謎團(tuán)越來(lái)越多了,我們這里就要跳出window對(duì)象舉個(gè)簡(jiǎn)單例子說(shuō)說(shuō),不然大家真的是暈的。
function person(name) {
this.name = name;
this.getName = function() {
alert(this.name)
}
}
var zhangsan = new person("zhangsan");
var lisi = new person("lisi");
console.log(zhangsan.name)
console.log(lisi.name)
zhangsan.getName();
lisi.getName();
結(jié)果:
"zhangsan"
"lisi"
注:可以使用關(guān)鍵字 this調(diào)用類中的屬性, this是對(duì)當(dāng)前對(duì)象的引用。
這樣一個(gè)例子我們似乎看到了面向?qū)ο笾欣^承的特性,在其他面向?qū)ο笳Z(yǔ)言中,這里的person函數(shù)被設(shè)計(jì)為“類”,但是在JavaScript中這里設(shè)計(jì)得有點(diǎn)畸形的感覺(jué),為啥這么說(shuō)呢,因?yàn)檫@里的person是一個(gè)構(gòu)造函數(shù)(constructor),用new實(shí)例化的也不是其他面向?qū)ο笳Z(yǔ)言中的類,而是構(gòu)造函數(shù) ,這種設(shè)計(jì)導(dǎo)致一個(gè)問(wèn)題是啥呢?無(wú)法共享屬性和方法,每一個(gè)實(shí)例對(duì)象,都有自己的屬性和方法的副本?。?!
比如:每一個(gè)實(shí)例對(duì)象都有g(shù)etName(),都是從父親構(gòu)造器中繼承得到,這樣就產(chǎn)生多個(gè)副本,但是我們希望這個(gè)方法是公用的,避免多個(gè)副本的資源浪費(fèi),我們希望能夠把公用的屬性方法提取出來(lái),然后實(shí)例化的對(duì)象也可以引用,但是不會(huì)直接拷貝一份作為副本。這個(gè)時(shí)候構(gòu)造函數(shù)(constructor)顯得有點(diǎn)力不從心了,JavaScript的設(shè)計(jì)者引入了一個(gè)重要的屬性prototype,這個(gè)屬性包含一個(gè)對(duì)象(通常稱為“prototype對(duì)象")。我們把這個(gè)例子改成用prototype寫試試:
function person(name) {
this.name = name;
}
person.prototype.getName = function() {
alert(this.name)
}
var zhangsan = new person("zhangsan");
var lisi = new person("lisi");
這樣我們多個(gè)實(shí)例化對(duì)象可以公用同一個(gè)方法,換句話說(shuō)所有的實(shí)例對(duì)象共享同一個(gè)prototype對(duì)象,通常稱為原型。一層層的繼承實(shí)現(xiàn)了鏈條式的"原型鏈"(prototype chain),JavaScript因此通過(guò)這個(gè)原型鏈實(shí)現(xiàn)繼承。至于為啥最開始怎么設(shè)計(jì),都是為了開發(fā)者簡(jiǎn)單,但是也因此給大家的感覺(jué)是特別,而且特別難理解,但是事實(shí)上其實(shí)并沒(méi)有那么神奇?。?!
prototype屬性很特殊,它還有一個(gè)隱式的constructor,指向了構(gòu)造函數(shù)本身。
> person.prototype.constructor === person // true
> zhangsan.constructor === person // true
> zhangsan.constructor === person.prototype.constructor // true
說(shuō)了這個(gè)多,我們一直沒(méi)有解釋__proto__ 屬性,我們上面講了可以通過(guò)構(gòu)造函數(shù)的prototype 屬性實(shí)現(xiàn)繼承共用公用的屬性方法,但是我們沒(méi)有說(shuō)明實(shí)例化對(duì)象如何訪問(wèn)到它所繼承的對(duì)象的原型對(duì)象,這里的__proto__ 屬性就是這個(gè)作用。我們?cè)倩剡^(guò)頭去看之前的問(wèn)題: 因?yàn)?b>window是通過(guò)實(shí)例化Window 得到,自然我們?cè)L問(wèn)Window 原型對(duì)象有兩種方法:1.直接通過(guò)Window的prototype屬性;2.通過(guò)實(shí)例化子對(duì)象的__proto__ 訪問(wèn)父對(duì)象的原型對(duì)象。這兩種方法實(shí)現(xiàn)的結(jié)果一模一樣。
Window.prototype === window.__proto__ // true
另外在JavaScript中有一個(gè)很特別的地方:萬(wàn)物皆對(duì)象,萬(wàn)物皆為空。 怎么理解呢,在JavaScript中的一切都源于對(duì)象,而且最頂層的對(duì)象是null對(duì)象,這會(huì)讓人很費(fèi)解的。所以當(dāng)我們通過(guò)__proto__ 不斷的尋找最頂層的原型對(duì)象時(shí)會(huì)發(fā)現(xiàn)為null。
基于原型的編程不是面向?qū)ο缶幊讨畜w現(xiàn)的風(fēng)格,且行為重用(在基于類的語(yǔ)言中也稱為繼承)是通過(guò)裝飾它作為原型的現(xiàn)有對(duì)象的過(guò)程實(shí)現(xiàn)的。這種模式也被稱為弱類化,原型化,或基于實(shí)例的編程。
最后有幾點(diǎn)需要說(shuō)明的是:
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象(prototype),原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針(constructor),而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針(__proto__ )。
除了使用__proto__ 方式訪問(wèn)對(duì)象的原型,還可以通過(guò)Object.getPrototypeOf 方法來(lái)獲取對(duì)象的原型,以及通過(guò)Object.setPrototypeOf 方法來(lái)重寫對(duì)象的原型。__proto__ 屬性只有瀏覽器才需要部署,其他環(huán)境可以沒(méi)有這個(gè)屬性,而且前后的兩根下劃線,表示它本質(zhì)是一個(gè)內(nèi)部屬性,不應(yīng)該對(duì)使用者暴露。
instanceof和Object.isPrototypeOf()可以判斷兩個(gè)對(duì)象是否是繼承關(guān)系。如上面那個(gè)例子:
// instanceof 運(yùn)算符返回一個(gè)布爾值,表示一個(gè)對(duì)象是否由某個(gè)構(gòu)造函數(shù)創(chuàng)建。
> zhangsan instanceof person
=> true
// Object.isPrototypeOf()只要某個(gè)對(duì)象處在原型鏈上,都返回true。
> person.prototype.isPrototypeOf(zhangsan)
=> true
這里推薦大家看看下面幾篇文章:
JavaScript面向?qū)ο蠛?jiǎn)介
JavaScript 原型中的哲學(xué)思想
Javascript繼承機(jī)制的設(shè)計(jì)思想
如何打造一個(gè)自己的類jQuery的js工具庫(kù)?
文章寫到本來(lái)是準(zhǔn)備重新開篇的,剛剛上面在window下將原型鏈繼承不知道會(huì)不會(huì)有點(diǎn)誤導(dǎo)一些朋友,因?yàn)樽铋_始是準(zhǔn)備以window對(duì)象入手將面向?qū)ο蟮膬?nèi)容整理一下,發(fā)現(xiàn)寫著寫著有點(diǎn)零散了,因?yàn)閣indow對(duì)象有很多其他內(nèi)容值得將,但是篇幅和本文主題影響,只能先停下后面再開篇補(bǔ)充,講了原型鏈繼承的理論知識(shí),我們自然要實(shí)際動(dòng)手做點(diǎn)項(xiàng)目才能說(shuō)明問(wèn)題。
基本概念講解
如果我們?nèi)ゲ榭匆恍﹋s庫(kù)的寫法,我們會(huì)發(fā)現(xiàn)經(jīng)常有這樣一種結(jié)構(gòu):
(function(w,undefined) {
//...
})(window);
在理解為什么要這樣寫之前我們首先要明白什么JavaScript的作用域,什么是匿名函數(shù),什么是閉包?
作用域
在es6之前,JavaScript是遵循函數(shù)作用域,不支持塊級(jí)作用域。
var i=0;
if(i<2){
var i = 2;
}
alert(i); // 2
在es6中支持使用let聲明了一個(gè)塊級(jí)域的本地變量,并且可以同時(shí)初始化該變量。
var i=0;
if(i<2){
let i = 2;
}
alert(i); // 0
函數(shù)內(nèi)部可以直接讀取函數(shù)全局變量。函數(shù)內(nèi)的變量如果是使用var 申明,則是局部變量,作用域范圍為函數(shù)體內(nèi)部,不可讀??;但是需要注意的是未經(jīng)過(guò)var申明,就變成了全局變量,在函數(shù)外部也可以調(diào)用。
// 局部變量類型:
var i=0;
var fn = function () {
if(i<2){
var i = 2;
}
}
fn();
alert(i); // 0
// 全局變量類型
var i=0;
var fn = function () {
if(i<2){
i = 2;
}
}
fn();
alert(i); // 2
變量提升:一個(gè)變量或函數(shù)可以在它被引用之后聲明。
【變量】
foo = 2
var foo;
// 被隱式地解釋為:
var foo;
foo = 2;
【函數(shù)】
hoisted(); // logs "foo"
function hoisted() {
console.log("foo");
}
匿名函數(shù):沒(méi)有函數(shù)名稱的函數(shù)
匿名函數(shù)是這樣的:
function(arg1,arg2){
// code
}
但是通常我們會(huì)把匿名函數(shù)寫成自執(zhí)行的匿名函數(shù):
(function(arg1,arg2){
// code
})(a1,a2);
等價(jià)于:
var fn = function(arg1,arg2){
// code
}
fn(a1,a2);
其實(shí)這里就是實(shí)參與形參的關(guān)系,arg1,arg2在函數(shù)體內(nèi)作為形參被引用,a1,a2作為實(shí)參在調(diào)用的時(shí)候傳入到函數(shù)體中被調(diào)用,至于變量?jī)?nèi)部存儲(chǔ)原理這里不做深入探究,畢竟學(xué)過(guò)編程的人應(yīng)該都清楚。
我們現(xiàn)在回過(guò)頭來(lái)看看本小節(jié)開頭說(shuō)的那個(gè)例子,為啥要那樣寫呢?
(function(w,undefined) {
//...
})(window);
為什么要傳入 window? 通過(guò)傳入 window變量,使得 window由全局變量變?yōu)榫植孔兞?,?dāng)在我們封裝的代碼塊中訪問(wèn) window時(shí),不需要將作用域鏈回退到頂層作用域,這樣可以更快的訪問(wèn) window;同時(shí)將 window作為參數(shù)傳入,可以在壓縮代碼時(shí)進(jìn)行優(yōu)化。
為什么要傳入 undefined? 在只執(zhí)行匿名函數(shù)的作用域內(nèi),確保 undefined 是真的未定義。因?yàn)?undefined 能夠被重寫,賦予新的值。
閉包
我們前面說(shuō)了在函數(shù)外可以調(diào)用函數(shù)內(nèi)未經(jīng)過(guò)var聲明的全局變量,但是如何從外部讀取函數(shù)局部變量呢?我們可以在函數(shù)內(nèi)部再定義一個(gè)函數(shù)。
var fn = function(){
var name = "local";
var f = function(){
alert(name);
}
return f
}
// 調(diào)用
var resurlt = fn();
resurlt();
// or
fn()();
閉包主要有兩個(gè)作用: 一是可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中。
讀取函數(shù)內(nèi)部變量我們很好理解,但是至于內(nèi)部變量的值保存在存儲(chǔ)中這個(gè)就有點(diǎn)難理解,我們看個(gè)例子:
var fn = function(){
var i = 0;
add = function(){
i++;
}
var f = function(){
alert(i);
}
return f
}
var result = fn();
result(); // 0
add();
result(); // 1
add未加var 聲明是全局變量,如果變量i不在內(nèi)存中存儲(chǔ),那么我們第一次和第二次調(diào)用result() 值都應(yīng)該為0。原因在于我們將fn()的返回值f()數(shù)賦值給一個(gè)全局變量,由于這個(gè)全局變量一直處于內(nèi)存中,f函數(shù)同樣也在內(nèi)存中,f()函數(shù)依賴于fn()函數(shù),因此fn()中的局部變量i一直處于內(nèi)存之中。 如果上面的例子在調(diào)用的時(shí)候使用fn()() 則不會(huì)出現(xiàn)這種情況。
1)由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁(yè)的性能問(wèn)題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
2)閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值?!?學(xué)習(xí)Javascript閉包(Closure)
這里簡(jiǎn)要講解了一下閉包的一些作用,主要是為了幫助我們理解為啥一些js庫(kù)采用閉包。
jQuery中鏈?zhǔn)秸{(diào)用的實(shí)現(xiàn)原理
首先我們?cè)趺磳?shí)現(xiàn)私有命名空間? 通過(guò)定義一個(gè)匿名函數(shù),創(chuàng)建了一個(gè)"私有"的命名空間,該命名空間的變量和方法,不會(huì)破壞全局的命名空間,我們只暴漏出一個(gè)頂層的對(duì)象供外部調(diào)用即可。
前面我們講到window對(duì)象的時(shí)候有個(gè)知識(shí)點(diǎn)沒(méi)有說(shuō)的是,我們?cè)陧?yè)面定義一個(gè)全局變量的時(shí)候,這個(gè)全局變量最終是會(huì)在window對(duì)象下,對(duì)于調(diào)用window對(duì)象下的屬性和方法我們一般無(wú)需通過(guò)window. 的形式就可以調(diào)用。同理當(dāng)我們引用jQuery這種庫(kù)的時(shí)候,jQuery對(duì)象會(huì)在引用頁(yè)面的window對(duì)象下,這是因?yàn)閖Query庫(kù)最后會(huì)將jQuery或者$對(duì)象掛在window對(duì)象下,這樣就實(shí)現(xiàn)了頂層對(duì)象的暴漏。
下面我們實(shí)現(xiàn)一個(gè)適用于現(xiàn)代瀏覽器的極小DOM操作庫(kù),主要解決移動(dòng)端,所以我們這里取名為mjs。
(function(w,undefined) {
// 構(gòu)造函數(shù)
var mjs = function(selector, context) {
return new mjs.fn.init(selector, context);
}
// 構(gòu)造函數(shù)mjs的原型對(duì)象
mjs.fn = mjs.prototype = {
constructor: mjs,
init: function (selector, context) {
//...
}
}
mjs.fn.init.prototype = mjs.fn;
// 為window全局變量添加mjs對(duì)象
w.mjs = w.m = mjs;
})(window);
這樣我們就可以無(wú)需new mjs(),直接使用 mjs.* 或者 m.* 鏈?zhǔn)秸{(diào)用相關(guān)方法。
selector(選擇器)
下面我們實(shí)現(xiàn)一個(gè)最簡(jiǎn)的選擇器,這里我不考慮兼容低級(jí)版本瀏覽器,使用querySelectorAll實(shí)現(xiàn)。我們接著上面的完善mjs.prototype.init方法。我們?nèi)绻豢紤]鏈?zhǔn)秸{(diào)用,我們最簡(jiǎn)單的選擇器甚至可以長(zhǎng)這樣:
var $ = function (selector) {
return document.querySelector(selector);
}
調(diào)用:
$(".content")
如果想給選擇器加一個(gè)上下文,我們進(jìn)一步可以這樣:
/**
* 選擇器
* @param {Object} selector
* @param {Object} context
*/
var $ = function (selector, context) {
var context = context || document;
var el = context.querySelectorAll(selector);
return Array.prototype.slice.call(el);
};
// 調(diào)用
var divObj = $(".div",$(".content")[0]);
console.log(divObj[0].innerHTML)
當(dāng)然我們這里補(bǔ)充完整就是這樣了:
// 構(gòu)造函數(shù)mjs的原型對(duì)象
mjs.prototype = {
constructor: mjs,
init: function (selector, context) {
if(!selector) {
return mjs;
}else if(typeof selector === "object"){
var selector = [selector];
for (var i = 0; i < selector.length; i++) {
this[i] = selector[i];
}
this.length = selector.length;
return mjs;
}else if(typeof selector === "string"){
var selector = selector.trim();
var context = context || document;
var el = context.querySelectorAll(selector);
var dom = Array.prototype.slice.call(el);
var length = dom.length;
for (var i = 0; i < length; i++) {
this[i] = dom[i];
}
this.context = context;
this.selector = selector;
this.length = length;
return this;
}
}
}
這里我們先只完成最簡(jiǎn)單的選擇器功能,還有當(dāng)selector是function類型的我們沒(méi)有進(jìn)行判斷,這里不貼出來(lái),大家具體可以看看源代碼。我們可以驗(yàn)證一下我們封裝的這個(gè)選擇器:
div1
span1
console.log(mjs(".divBox")[0].innerHTML)
// "div1span1 "
console.log(mjs(".divBox span")[0].innerHTML)
// "span1"
var divBox = mjs(".divBox")[0];
console.log(mjs("span",divBox)[0].innerHTML)
// "span1"
因?yàn)閕nnerHTML是原生DOM操作的方法,我們mjs對(duì)象沒(méi)有這個(gè)方法,所以我們這里是將mjs對(duì)象轉(zhuǎn)成了原生DOM對(duì)象,轉(zhuǎn)換方法:mjs(selector)[0]。
html()、text()、attr()
為了簡(jiǎn)單起見(jiàn)我們繼續(xù)封裝,先完成一個(gè)html()方法。
...
html: function (content) {
if (content === undefined && this[0].nodeType === 1) {
return this[0].innerHTML.trim();
}else{
var len = this.length;
for (var i = 0; i < len; i++) {
this[i].innerHTML = content;
}
return this;
}
},
text: function (val) {
if (!arguments.length) {
return this[0].textContent.trim();
} else {
for (var i = 0; i < this.length; i++) {
this[i].innerText = val;
}
return this;
}
}
...
上面的例子我們可以這樣調(diào)用:
// 直接獲取內(nèi)容,默認(rèn)獲取第一個(gè)匹配項(xiàng)
console.log(mjs(".divBox").html())
console.log(mjs(".divBox span").html())
console.log(mjs(".divBox span").text())
// 更新內(nèi)容,默認(rèn)更新所有匹配項(xiàng)
mjs(".divBox span").html("我是新的內(nèi)容")
mjs(".divBox span").text("我是新的內(nèi)容")
// 支持上下文查找方法
console.log(mjs("span",mjs(".divBox")[0]).html())
// 設(shè)置屬性
mjs(".divBox").attr("name","divBox");
// 獲取屬性
console.log(mjs(".divBox").attr("name"))
prepend()、append()、before()、after()、remove()
prepend: function(str) {
var len = this.length;
for (var i = 0; i < len; i++) {
this[i].insertAdjacentHTML("afterbegin", str);
}
return this;
},
append: function (str) {
var len = this.length;
for (var i = 0; i < len; i++) {
this[i].insertAdjacentHTML("beforeend", str);
}
return this;
},
before: function (str) {
var len = this.length;
for (var i = 0; i < len; i++) {
this[i].insertAdjacentHTML("beforebegin", str);
}
return this;
},
after: function (str) {
var len = this.length;
for (var i = 0; i < len; i++) {
this[i].insertAdjacentHTML("afterend", str);
}
return this;
},
remove: function () {
var len = this.length;
for (var i = 0; i < len; i++) {
this[i].parentNode.removeChild(this[i]);
}
return this;
}
調(diào)用:
// 添加元素
mjs(".divBox").prepend(" ")
mjs(".divBox").append("")
mjs(".divBox").before("")
mjs(".divBox").after("")
// 刪除元素
mjs(".divBox").remove();
insertAdjacentHTML() 將指定的文本解析為 HTML 或 XML,然后將結(jié)果節(jié)點(diǎn)插入到 DOM 樹中的指定位置處。該方法不會(huì)重新解析調(diào)用該方法的元素,因此不會(huì)影響到元素內(nèi)已存在的元素節(jié)點(diǎn)。從而可以避免額外的解析操作,比直接使用 innerHTML 方法要快?!狹DN insertAdjacentHTML
語(yǔ)法: element.insertAdjacentHTML(position, text); position 是相對(duì)于 element 元素的位置,并且只能是以下的字符串之一:
beforebegin: 在 element 元素的前面。
afterbegin:在 element 元素的第一個(gè)子節(jié)點(diǎn)前面。
beforeend:在 element 元素的最后一個(gè)子節(jié)點(diǎn)后面。
afterend:在 element 元素的后面。
hasClass()、addClass()、removeClass()、toggleClass()
...
hasClass: function (cls) {
return this[0].classList.contains(cls);
},
addClass: function (cls) {
var len = this.length;
for (var i = 0; i < len; i++) {
if(!this[i].classList.contains(cls)){
this[i].classList.add(cls);
}
}
return this;
},
removeClass: function (cls) {
var len = this.length;
for (var i = 0; i < len; i++) {
if(this[i].classList.contains(cls)){
this[i].classList.remove(cls);
}
}
return this;
},
toggleClass: function (cls) {
return this[0].classList.toggle(cls);
}
...
調(diào)用方法:
// hasClass(返回值為布爾值)
console.log(mjs(".divBox").hasClass("divBox"))
// addClass
mjs(".divBox").addClass("red")
// removeClass
mjs(".divBox").removeClass("red")
// toggleClass
mjs(".divBox").toggleClass("red")
css()
css: function (attr,val) {
var len = this.length;
for(var i = 0;i < len; i++) {
if(arguments.length === 1){
var obj = arguments[0];
if(typeof obj === "string"){
return getComputedStyle(this[i],null)[attr];
}else if(typeof obj === "object"){
for(var attr in obj){
this[i].style[attr] = obj[attr];
}
}
} else {
if(typeof val === "function"){
this[i].style[attr] = val();
}else{
this[i].style[attr] = val;
}
}
}
return this;
}
調(diào)用方法:
// 獲取樣式屬性值
console.log(mjs(".divBox").css("color"));
// 設(shè)置樣式屬性值
// 方法1
mjs(".divBox").css("color","red");
// 方法2
mjs(".divBox").css({
"width":"100px",
"color":"white",
"background-color":"#98bf21",
"font-family":"Arial",
"font-size":"20px",
"padding":"5px"
});
// 方法3
mjs(".divBox").css(
"background-color",function(){
return "#F00"
}
)
find()、first()、last()、eq(index)、parent()
find: function(selector){
return this.init(selector,this[0])
},
first: function(){
return this.init(this[0])
},
last: function(){
return this.init(this[this.length-1])
},
eq: function(index){
return this.init(this[index])
},
parent: function(){
return this.init(this[0].parentNode);
}
我們前面想通過(guò)上下文查找內(nèi)容:
console.log(mjs("span",mjs(".divBox")[0]).html())
我們可以通過(guò)find方法這樣寫:
console.log(mjs(".divBox").find("span").html()) console.log(mjs(".divBox span").first().html())
console.log(mjs(".divBox span").last().html())
console.log(mjs(".divBox span").eq(1).html())
console.log(mjs(".divBox span").eq(1).parent().html())
關(guān)鍵在于mjs對(duì)象和原生dom的區(qū)別和相互轉(zhuǎn)換。
至此我們封裝了一個(gè)簡(jiǎn)單的類jQuery的工具庫(kù),當(dāng)然對(duì)于一個(gè)完整的工具庫(kù),比如jQuery、zepto等,它們功能肯定是更為完善,封裝了更多的方法,在異常處理及性能、可拓展性方法做得更好,由于本文的重點(diǎn)不是為了完成一個(gè)完整的庫(kù),在此只是拋磚引玉,只是學(xué)習(xí)一下常用的思想,有興趣的朋友可以繼續(xù)完善這個(gè)庫(kù)。
mjs github地址:https://github.com/zhaomenghu...
參考文章
MDN javascript 可想造一個(gè)屬于你自己的jQuery庫(kù)?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/79868.html
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。
showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385);
馬上就要到七夕了,離年底老媽老爸...
李昌杰
2019-08-16 10:40
評(píng)論0
收藏0
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。
showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385);
馬上就要到七夕了,離年底老媽老爸...
Lyux
2019-06-28 13:32
評(píng)論0
收藏0
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。
showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385);
馬上就要到七夕了,離年底老媽老爸...
摘要:然而學(xué)習(xí)布局,你只要學(xué)習(xí)幾個(gè)手機(jī)端頁(yè)面自適應(yīng)解決方案布局進(jìn)階版附源碼示例前端掘金一年前筆者寫了一篇手機(jī)端頁(yè)面自適應(yīng)解決方案布局,意外受到很多朋友的關(guān)注和喜歡。
十分鐘學(xué)會(huì) Fiddler - 后端 - 掘金一.Fiddler介紹 Fiddler是一個(gè)http抓包改包工具,fiddle英文中有欺騙、偽造之意,與wireshark相比它更輕量級(jí),上手簡(jiǎn)單,因?yàn)橹荒茏ttp和https數(shù)據(jù)...
A Loity
2019-08-20 17:45
評(píng)論0
收藏0
摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠?lái)都是中的主導(dǎo)范式。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。
JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠?lái)都是JavaScript中的主導(dǎo)范式。JavaScript作為一門多范式編程語(yǔ)言,然而,近幾年,函數(shù)式編程越來(lái)越多得受到開發(fā)者的青睞。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。因此,...
cfanr
2019-08-20 16:33
評(píng)論0
收藏0
男| 高級(jí)講師
閱讀 3264· 2021-11-04 16:09
閱讀 3275· 2021-09-23 11:49
閱讀 3762· 2021-09-09 09:33
閱讀 3776· 2021-08-18 10:22
閱讀 2113· 2019-08-30 15:55
閱讀 3693· 2019-08-30 15:53
閱讀 2724· 2019-08-28 18:08
閱讀 956· 2019-08-26 18:18
<