用Object.defineProperty手寫(xiě)一個(gè)簡(jiǎn)單的雙向綁定

前言 上次寫(xiě)了一個(gè)Object.defineProperty() 不詳解,文末說(shuō)要寫(xiě)用它來(lái)寫(xiě)個(gè)雙向綁定。說(shuō)話算話,說(shuō)來(lái)就來(lái)

前文鏈接 Object.defineProperty() 不詳解
先看最后效果

model演示.gif

什么是雙向綁定?

  1. 當(dāng)一個(gè)對(duì)象(或變量)的屬性改變,那么調(diào)用這個(gè)屬性的地方顯示也應(yīng)該改變,模型到視圖(model => view)

2.當(dāng)調(diào)用屬性的這個(gè)地方改變了這個(gè)屬性(通常是一個(gè)表單元素),那么這個(gè)對(duì)象(或變量)的屬性也會(huì)改為最新的值 ,即視圖到模型(view => model)

我們?cè)趺粗缹?duì)象的屬性變了?

上文說(shuō)到,Object.defineProperty 設(shè)置對(duì)象屬性的描述字段里面有兩個(gè)屬性 set (設(shè)置屬性時(shí)被調(diào)用)和get(獲取屬性時(shí)被調(diào)用),只說(shuō)不練,你再講什么?眼見(jiàn)為實(shí)好嗎?OK ,上代碼

var user = {};
var defaultName = "狂奔的蝸牛";
Object.defineProperty(user,"name",{
    get:function(){
        console.log("你是不是來(lái)獲取值啦");
        return defaultName;
    },
    set:function(value){
        console.log("你是不是來(lái)設(shè)置值啦");
        defaultName = value;
    }
})

console.log(user.name);
user.name = "狂奔的蘿卜";
console.log(user.name);
get和set存取時(shí)被調(diào)用

如上圖所示 每當(dāng)我獲取user.name屬性時(shí),get方法被調(diào)用,get 方法對(duì)應(yīng)的函數(shù)被執(zhí)行,輸出 你是不是來(lái)獲取值啦;每當(dāng)我設(shè)置user.name屬性時(shí),set方法對(duì)應(yīng)的函數(shù)被執(zhí)行,輸出 你是不是來(lái)設(shè)置值啦 ; 是的,我們監(jiān)控到了代碼對(duì)user.name屬性的存取。

說(shuō)明 假設(shè)id="model" 的元素的 valueuser.name的值,既然我們可以在改變屬性的執(zhí)行日志輸出(console.log("你是不是來(lái)設(shè)置值啦");),那么,我們?cè)谠O(shè)置值的時(shí)候給id="model" 的元素設(shè)置下新值,不就實(shí)現(xiàn)了從模型到視圖?!!,說(shuō)干就干

模型到視圖(model => view)的同步

說(shuō)明 假設(shè)id="model" 的元素的 valueuser.name的值,既然我們可以在改變屬性的執(zhí)行日志輸出(console.log("你是不是來(lái)設(shè)置值啦");),那么,我們?cè)谠O(shè)置值的時(shí)候給id="model" 的元素設(shè)置下新值,不就實(shí)現(xiàn)了從模型到視圖?!!,說(shuō)干就干

<body>
    手寫(xiě)一個(gè)簡(jiǎn)單雙向綁定<br/>
    <input type="text" id="model"><br/>
    <div id="modelText"></div>
</body>
<script>
var user = {};
var defaultName = "狂奔的蝸牛";

document.querySelector("#model").value = defaultName;
document.querySelector("#modelText").textContent = defaultName;

//定義屬性 監(jiān)控改變
Object.defineProperty(user,"name",{
    get:function(){
        console.log("你是不是來(lái)獲取值啦");
        return defaultName;
    },
    set:function(newValue){
        console.log("設(shè)置新值");
        defaultName = newValue;
        console.log("實(shí)現(xiàn) 模型 => 視圖");
        document.querySelector("#model").value = newValue;
        document.querySelector("#modelText").textContent = newValue;
    }
})

console.log("2s 后改變值");

setTimeout(() => {
    //改變值
    user.name = "狂奔的蘿卜";
}, 2000);
</script>
模型到視圖(model => view)的同步

視圖到模型(view => model)的同步

問(wèn): 我們能捕捉到view對(duì)值更改嗎?
答:可以!! id="model" 的input元素的 valueuser.name的值,填充在這個(gè)文本框里面,文本框有個(gè)“ keyup” 事件,當(dāng)我們?cè)谖谋究蛑休斎胛淖值臅r(shí)候,文本框的值會(huì)跟著改變,并且會(huì)連續(xù)觸發(fā)keyup事件,那么我們只需要監(jiān)聽(tīng)這個(gè)事件,是不是就可以捕捉到view對(duì)值的更改了??既然文本框的值會(huì)跟著改變,我們獲取最新的值再把新值更新到user.name屬性,不就實(shí)現(xiàn)了視圖到模型(view => model)的同步?沒(méi)代碼說(shuō)個(gè)啥

<body>
    手寫(xiě)一個(gè)簡(jiǎn)單雙向綁定<br/>
    <input type="text" id="model"><br/>
    <div id="modelText"></div>
</body>
<script>
    var user = {};
    var defaultName = "狂奔的蝸牛";
    var model = document.querySelector("#model");
    var modelText = document.querySelector("#modelText");

    model.value = defaultName;
    modelText.textContent = defaultName;

    //定義屬性 監(jiān)控改變
    Object.defineProperty(user,"name",{
        get:function(){
            console.log("你是不是來(lái)獲取值啦");
            return defaultName;
        },
        set:function(newValue){
            console.log("設(shè)置新值");
            defaultName = newValue;
            model.value = newValue;
            modelText.textContent = newValue;
        }
    })

    model.addEventListener("keyup", function () {
        user.name = this.value;
        console.log("實(shí)現(xiàn) 視圖 => 模型");
    }, false)
</script>
view2model.gif

【最終源碼】

在上述代碼的基礎(chǔ)上,加入了 用戶(hù)輸入中文的判斷(用戶(hù)輸入中文時(shí),頻繁觸發(fā) keyup事件,但實(shí)際上輸入并沒(méi)有結(jié)束。)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>雙向綁定</title>
</head>
<body>
    手寫(xiě)一個(gè)簡(jiǎn)單雙向綁定<br/>
    <input type="text" id="model"><br/>
    <div id="modelText"></div>
</body>
<script>
    var model = document.querySelector("#model");
    var modelText = document.querySelector("#modelText");
    var defaultName = "defaultName";
    var userInfo = {}
    model.value = defaultName;
    Object.defineProperty(userInfo, "name", {
        get: function () {
            return defaultName;
        },
        set: function (value) {
            defaultName = value;
            model.value = value;
            console.log("-----value");
            console.log(value);
            modelText.textContent = value;
        }
    })

    userInfo.name = "new value";
    var isEnd = true;

    model.addEventListener("keyup", function () {
        if (isEnd) {
            userInfo.name = this.value;
        }
    }, false)
    //加入監(jiān)聽(tīng)中文輸入事件
    model.addEventListener("compositionstart", function () {
        console.log("開(kāi)始輸入中文");
        isEnd = false;
    })
    model.addEventListener("compositionend", function () {
        isEnd = true;
        console.log("結(jié)束輸入中文");
    })
</script>
</html>

【完結(jié)】
Object.defineProperty 可以做很多好玩兒的,自己慢慢探索哈~

個(gè)人水平有限,不喜勿噴哈,歡迎提意見(jiàn)~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容