成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

用原生 JS 實(shí)現(xiàn) MVVM 框架1——觀察者模式和數(shù)據(jù)監(jiān)控

TANKING / 1190人閱讀

摘要:在前端頁面中,把用純對象表示,負(fù)責(zé)顯示,兩者做到了最大化的分離把和關(guān)聯(lián)起來的就是。了解了思想后,自己用原生實(shí)現(xiàn)一個(gè)框架。注意數(shù)據(jù)描述符和存儲描述符不能同時(shí)存在,否則會報(bào)錯(cuò)報(bào)錯(cuò)數(shù)據(jù)攔截使用來實(shí)現(xiàn)數(shù)據(jù)攔截,從而實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽。

在前端頁面中,把 Model 用純 JS 對象表示,View 負(fù)責(zé)顯示,兩者做到了最大化的分離

把 Model 和 View 關(guān)聯(lián)起來的就是 ViewModel。ViewModel 負(fù)責(zé)把 Model 的數(shù)據(jù)同步到 View 中顯示出來,還負(fù)責(zé)把 View 的修改同步回 Model。

MVVM 的設(shè)計(jì)思想:關(guān)注 Model 的變化,讓 MVVM 框架去自動更新 DOM 的狀態(tài),從而把開發(fā)者從操作 DOM 的繁瑣步驟中解脫出來。

了解了 MVVM 思想后,自己用原生 JS 實(shí)現(xiàn)一個(gè) MVVM 框架。

實(shí)現(xiàn) MVVM 框架前先來看幾個(gè)基本用法:

Object.defineProperty

普通聲明對象,定義和修改屬性

let obj = {}
obj.name = "zhangsan"
obj.age = 20

ObjectdefineProperty聲明對象
語法:

Object.defineProperty(obj,prop,descriptor)

obj:要處理的目標(biāo)對象

prop:要定義或修改的屬性的名稱

descriptor:將被定義或修改的屬性描述符

let obj = {}
Object.defineProperty(obj,"age",{
    value = 14,
})

咋一看有點(diǎn)畫蛇添足,這不很雞肋嘛

別急,往下看

描述符

descriptor有兩種形式:數(shù)據(jù)描述符和存儲描述符,他們兩個(gè)共有屬性:

configurable,是否可刪除,默認(rèn)為false,定義后無法修改

enumerable,是否可遍歷,默認(rèn)為false,定以后無法修改

共有屬性

configurable設(shè)置為false時(shí),其內(nèi)部屬性無法用delete刪除;如要?jiǎng)h除,需要把configurable設(shè)置為true。

let obj = {}
Object.defineProperty(obj,"age",{
    configurable:false,
    value:20,
})
delete obj.age         //false

enumerable設(shè)置為false時(shí),其內(nèi)部屬性無法遍歷;如需遍歷,要把enumerable設(shè)置為true

let obj = {name:"zhangsan"}
Object.defineProperty(obj,"age",{
    enumerable:false,
    value:20,
})
for(let key in obj){
    console.log(key)    //name
}
數(shù)據(jù)描述符

value:該屬性對應(yīng)的值,默認(rèn)為undefined。
writable:當(dāng)且緊當(dāng)為true時(shí),value才能被賦值運(yùn)算符改變。默認(rèn)為false。

let obj = {}
Object.defineProperty(obj,"age",{
    value:10,
    writable:false
})
obj.age = 11
obj.age        //10

writableconfigurable的區(qū)別是前者是value能否被修改,后者是value能否被刪除。

存儲描述符

get():一個(gè)給屬性提供getter的方法,默認(rèn)為undefined。
set():一個(gè)給屬性提供setter的方法,默認(rèn)為undefined。

let obj = {}
let age
Object.defineProperty(obj,"age",{
    get:function(){
        return age
    },
    set:function(newVal){
        age = newVal
    }
})
obj.age = 20
obj.age        //20

當(dāng)我調(diào)用obj.age時(shí),其實(shí)是在向obj對象要age這個(gè)屬性,它會干嘛呢?它會調(diào)用obj.get()方法,它會找到全局變量age,得到undefined。

當(dāng)我設(shè)置obj.age = 20時(shí),它會調(diào)用obj.set()方法,將全局變量age設(shè)置為20。

此時(shí)在調(diào)用obj.age,得到20。

注意:數(shù)據(jù)描述符和存儲描述符不能同時(shí)存在,否則會報(bào)錯(cuò)

let obj = {}
let age
Object.defineProperty(obj,"age",{
    value:10,        //報(bào)錯(cuò)
    get:function(){
        return age
    },
    set:function(newVal){
        age = newVal
    }
})
數(shù)據(jù)攔截

使用Object.defineProperty來實(shí)現(xiàn)數(shù)據(jù)攔截,從而實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽。

首先有一個(gè)對象

let data = {
    name:"zhangsan",
    friends:[1,2,3,4]
}

下面寫一個(gè)函數(shù),實(shí)現(xiàn)對data對象的監(jiān)聽,就可以在內(nèi)部做一些事情

observe(data)

換句話說,就是data內(nèi)部的屬性都被我們監(jiān)控的,當(dāng)調(diào)用屬性時(shí),就可以在上面做些手腳,使得返回的值變掉;當(dāng)設(shè)置屬性時(shí),不給他設(shè)置。

當(dāng)然這樣做很無聊,只是想說明,我們可以在內(nèi)部做手腳,實(shí)現(xiàn)我們想要的結(jié)果。

observe這個(gè)函數(shù)應(yīng)該怎么寫呢?

function observe(data){
    if(!data || typeof data !== "object")return //如果 data 不是對象,什么也不做,直接跳出,也就是說只對 對象 操作
    for(let key in data){    //遍歷這個(gè)對象
        let val = data[key]    //得到這個(gè)對象的每一個(gè)`value`
        if(typeof val === "object"){    //如果這個(gè) value 依然是對象,用遞歸的方式繼續(xù)調(diào)用,直到得到基本值的`value`
            observe(val)
        }
        Object.defineProperty(data,key,{    //定義對象
            configurable:true,    //可刪除,原本的對象就能刪除
            enumerable:true,    //可遍歷,原本的對象就能遍歷
            get:function(){
                console.log("這是假的")    //調(diào)用屬性時(shí),會調(diào)用 get 方法,所以調(diào)用屬性可以在 get 內(nèi)部做手腳
                //return val    //這里注釋掉了,實(shí)際調(diào)用屬性就是把值 return 出去
            },
            set:function(newVal){
                console.log("我不給你設(shè)置。。。")    //設(shè)置屬性時(shí),會調(diào)用 set 方法,所以設(shè)置屬性可以在 set 內(nèi)部做手腳
                //val = newVal    //這里注釋掉了,實(shí)際設(shè)置屬性就是這樣寫的。
            }
        })
    }
}

注意兩點(diǎn):

我們在聲明let val = data[key]時(shí),不能用var,因?yàn)檫@里需要對每個(gè)屬性進(jìn)行監(jiān)控,用let每次遍歷都會創(chuàng)建一個(gè)新的val,在進(jìn)行賦值;如果用var,只有第一次才是聲明,后面都是對一次聲明val進(jìn)行賦值,遍歷結(jié)束后,得到的是最后一個(gè)屬性,顯然這不是我們需要的。

get方法里,return就是前面聲明的val,這里不能用data[key],會報(bào)錯(cuò)。因?yàn)檎{(diào)用data.name,就是調(diào)用get方法時(shí),得到的結(jié)果是data.name,又繼續(xù)調(diào)用get方法,就隨變成死循環(huán),所以這里需要用一個(gè)變量來存儲data[key],并將這個(gè)變量返回出去。

觀察者模式

一個(gè)典型的觀察者模式應(yīng)用場景——微信公眾號

不同的用戶(我們把它叫做觀察者:Observer)都可以訂閱同一個(gè)公眾號(我們把它叫做主體:Subject)

當(dāng)訂閱的公眾號更新時(shí)(主體),用戶都能收到通知(觀察者)

用代碼怎么實(shí)現(xiàn)呢?先看邏輯:

Subject 是構(gòu)造函數(shù),new Subject()創(chuàng)建一個(gè)主題對象,它維護(hù)訂閱該主題的一個(gè)觀察者數(shù)組數(shù)組(舉例來說:Subject 是騰訊推出的公眾號,new Subject() 是一個(gè)某個(gè)機(jī)構(gòu)的公眾號——新世相,它要維護(hù)訂閱這個(gè)公眾號的用戶群體)

主題上有一些方法,如添加觀察者addObserver、刪除觀察者removeObserver、通知觀察者更新notify(舉例來說:新世相將用戶分為兩組,一組是忠粉就是 addObserver,一組是黑名單就是:removeObserver,它在忠粉組可以添加用戶,可以在黑名單里拉黑一些杠精,如果有福利發(fā)放,它就會統(tǒng)治忠粉里的用戶:notify)

Observer 是構(gòu)造函數(shù),new Observer() 創(chuàng)建一個(gè)觀察者對象,該對象有一個(gè)update方法(舉例來說:Observer 是忠粉用戶群體,new Observer() 是某個(gè)具體的用戶——小王,他必須要打開流量才能收到新世相的福利推送:updata)

當(dāng)調(diào)用notify時(shí)實(shí)際上調(diào)用全部觀察者observer自身的update方法(舉例來說:當(dāng)新世相推送福利時(shí),它會自動幫忠粉組的用戶打開流量,這比較極端,只是用來舉例)

ES5 寫法:
function Subject(){
    this.observers = []
}
Subject.prototype.addObserver = function(observer){
    this.observers.push(observer)
}
Subject.prototype.removeObserver = function(observer){
    let index = this.observers.indexOf(observer)
    if(index > -1){
        this.observers.splice(index,1)
    }
}
Subject.prototype.notify = function(){
    this.observers.forEach(observer=>{
        observer.update()
    })
}
function Observer(name){
    this.name = name
    this.update = function(){
        console.log(name + " update...")
    }
}

let subject = new Subject()    //創(chuàng)建主題
let observer1 = new Observer("xiaowang")    //創(chuàng)建觀察者1
subject.addObserver(observer1)    //主題添加觀察者1
let observer2 = new Observer("xiaozhang")    //創(chuàng)建觀察者2
subject.addObserver(observer2)    //主題添加觀察者2
subject.notify()    //主題通知觀察者

/**** 輸出 *****/
xiaowang update...
xiaozhang update...
ES6 寫法:
class Subject{
    constructor(){
        this.observers = []
    }
    addObserver(observer){
        this.observers.push(observer)
    }
    removeObserver(observer){
        let index = this.observers.indexOf(observer)
        if(index > -1){
            this.observers.splice(index,1)
        }
    }
    notify(){
        this.observers.forEach(observer=>{
            observer.update()
        })
    }
}
class Observer{
    constructor(name){
        this.name = name
        this.update = function(){
            console.log(name + " update...")
        }
    }
}
let subject = new Subject()    //創(chuàng)建主題
let observer1 = new Observer("xiaowang")    //創(chuàng)建觀察者1
subject.addObserver(observer1)    //主題添加觀察者1
let observer2 = new Observer("xiaozhang")    //創(chuàng)建觀察者2
subject.addObserver(observer2)    //主題添加觀察者2
subject.notify()    //主題通知觀察者

/**** 輸出 *****/
xiaowang update...
xiaozhang update...

ES5 和 ES6 寫法效果一樣,ES5 的寫法更好理解,ES6 只是個(gè)語法糖

主題添加觀察者的方法subject.addObserver(observer)很繁瑣,直接給觀察者下方權(quán)限,給他們增加添加進(jìn)忠粉組的權(quán)限

class Observer{
  constructor() {
    this.update = function() {
        console.log(name + " update...")
    }
  }
  subscribeTo(subject) {    //只要用戶訂閱了主題就會自動添加進(jìn)忠粉組
    subject.addObserver(this)    //這里的 this 是 Observer 的實(shí)例
  }
}

let subject = new Subject()
let observer = new Observer("lisi")
observer.subscribeTo(subject)  //觀察者自己訂閱忠粉分組
subject.notify()

/****** 輸出 *******/
lisi update...

MVVM 框架的內(nèi)部基本原理就是上面這些,下一篇用代碼寫一遍完整的 MVVM 框架。

用原生 JS 實(shí)現(xiàn) MVVM 框架MVVM 框架系列:
用原生 JS 實(shí)現(xiàn) MVVM 框架2——單向綁定

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97271.html

相關(guān)文章

  • 原生 JS 實(shí)現(xiàn) MVVM 框架2——單向綁定

    摘要:上一篇寫了實(shí)現(xiàn)框架的一些基本概念本篇用代碼來實(shí)現(xiàn)一個(gè)完整的框架思考假設(shè)有如下代碼,里面的會和試圖中的一一映射,修改的值,會直接引起試圖中對應(yīng)數(shù)據(jù)的變化如何實(shí)現(xiàn)上述呢回想下這篇講的觀察者模式和數(shù)據(jù)監(jiān)聽主題是什么觀察者是什么觀察者何時(shí)訂閱主題主 上一篇寫了實(shí)現(xiàn) MVVM 框架的一些基本概念 本篇用代碼來實(shí)現(xiàn)一個(gè)完整的 MVVM 框架 思考 假設(shè)有如下代碼,data里面的name會和試圖中的...

    Zoom 評論0 收藏0
  • MVC MVP MVVM

    摘要:,的事件回調(diào)函數(shù)中調(diào)用的操作方法。以為例調(diào)用關(guān)系模式實(shí)際就是將中的改名為,調(diào)用過程基本一致,最大的改良是間的雙向綁定。和間,有一個(gè)對象,可以操作修改,使用。 參考:MVC,MVP 和 MVVM 的圖示 - 阮一峰http://www.ruanyifeng.com/blo...Web開發(fā)的MVVM模式http://www.cnblogs.com/dxy198...界面之下:還原真實(shí)的MV...

    wushuiyong 評論0 收藏0
  • MVC MVP MVVM

    摘要:,的事件回調(diào)函數(shù)中調(diào)用的操作方法。以為例調(diào)用關(guān)系模式實(shí)際就是將中的改名為,調(diào)用過程基本一致,最大的改良是間的雙向綁定。和間,有一個(gè)對象,可以操作修改,使用。 參考:MVC,MVP 和 MVVM 的圖示 - 阮一峰http://www.ruanyifeng.com/blo...Web開發(fā)的MVVM模式http://www.cnblogs.com/dxy198...界面之下:還原真實(shí)的MV...

    Tangpj 評論0 收藏0
  • JS中的雙向數(shù)據(jù)綁定及Object.defineProperty方法

    摘要:,而且每種框架雙向數(shù)據(jù)綁定的實(shí)現(xiàn)方式都不太一致,比如內(nèi)部使用的是臟檢查,而內(nèi)部實(shí)現(xiàn)方式的本質(zhì)是設(shè)置屬性訪問器。在中也有類似的概念,不過不叫魔術(shù)方法,而是叫做訪問器。 緣起前幾天在看一些流行的迷你mvvm框架(比如avalon.js、 vue.js 這種較輕的框架,而非Angularjs、Emberjs這種較重的框架)的實(shí)現(xiàn)?,F(xiàn)代流行的mvvm框架一般都會將數(shù)據(jù)雙向綁定(two-ways...

    szysky 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<