目次
Object.defineProperty
描述符
共有属性
数据描述符
存储描述符
数据拦截
記述子
データ記述子
ストレージ記述子
ES6 写法:
ホームページ ウェブフロントエンド jsチュートリアル MVVM フレームワークをネイティブ JS で実装する基本原理の詳細な説明

MVVM フレームワークをネイティブ JS で実装する基本原理の詳細な説明

Sep 01, 2018 pm 05:35 PM
javascript

この記事では、ネイティブ JS で MVVM フレームワークを実装するための基本原則について詳しく説明します。必要な方は参考にしていただければ幸いです。

フロントエンドページでは、モデルは純粋な JS オブジェクトで表され、ビューはその 2 つが分離されて最大化されます。

モデルとビューを関連付けるものは ViewModel です。 ViewModel は、表示のために Model データを View に同期する役割を果たします。また、View の変更を Model に同期して戻す役割も担います。

MVVM の設計思想: モデルの変更に注意を払い、MVVM フレームワークに DOM の状態を自動的に更新させることで、開発者は DOM を操作する面倒な手順から解放されます。

MVVMの考え方を理解した後、ネイティブJSを使用してMVVMフレームワークを実装しました。

MVVM フレームワークを実装する前に、いくつかの基本的な使用法を見てみましょう:

Object.defineProperty

一般的にオブジェクトを宣言し、プロパティを定義および変更します

let obj = {}
obj.name = 'zhangsan'
obj.age = 20
ログイン後にコピー

ObjectdefineProperty を使用してオブジェクトを宣言します
構文: ObjectdefineProperty声明对象
语法:

Object.defineProperty(obj,prop,descriptor)

obj:要处理的目标对象

prop:要定义或修改的属性的名称

descriptor:将被定义或修改的属性描述符

let obj = {}
Object.defineProperty(obj,'age',{
    value = 14,
})
ログイン後にコピー

咋一看有点画蛇添足,这不很鸡肋嘛

别急,往下看

描述符

descriptor有两种形式:数据描述符和存储描述符,他们两个共有属性:

configurable,是否可删除,默认为false,定义后无法修改

enumerable,是否可遍历,默认为false,定以后无法修改

共有属性

configurable设置为false时,其内部属性无法用delete删除;如要删除,需要把configurable设置为true

let obj = {}
Object.defineProperty(obj,'age',{
    configurable:false,
    value:20,
})
delete obj.age         //false
ログイン後にコピー

enumerable设置为false时,其内部属性无法遍历;如需遍历,要把enumerable设置为true

let obj = {name:'zhangsan'}
Object.defineProperty(obj,'age',{
    enumerable:false,
    value:20,
})
for(let key in obj){
    console.log(key)    //name
}
ログイン後にコピー

数据描述符

value:该属性对应的值,默认为undefined
writable:当且紧当为true时,value才能被赋值运算符改变。默认为false

let obj = {}
Object.defineProperty(obj,'age',{
    value:10,
    writable:false
})
obj.age = 11
obj.age        //10
ログイン後にコピー

writableconfigurable的区别是前者是value能否被修改,后者是value能否被删除。

存储描述符

get():一个给属性提供getter的方法,默认为undefined
set():一个给属性提供setter的方法,默认为undefined

let obj = {}
let age
Object.defineProperty(obj,'age',{
    get:function(){
        return age
    },
    set:function(newVal){
        age = newVal
    }
})
obj.age = 20
obj.age        //20
ログイン後にコピー

当我调用obj.age时,其实是在向obj对象要age这个属性,它会干嘛呢?它会调用obj.get()方法,它会找到全局变量age,得到undefined

当我设置obj.age = 20时,它会调用obj.set()方法,将全局变量age设置为20

此时在调用obj.age,得到20

注意:数据描述符和存储描述符不能同时存在,否则会报错

let obj = {}
let age
Object.defineProperty(obj,'age',{
    value:10,        //报错
    get:function(){
        return age
    },
    set:function(newVal){
        age = newVal
    }
})
ログイン後にコピー

数据拦截

使用Object.defineProperty来实现数据拦截,从而实现数据监听。

首先有一个对象

let data = {
    name:'zhangsan',
    friends:[1,2,3,4]
}
ログイン後にコピー

下面写一个函数,实现对data对象的监听,就可以在内部做一些事情

observe(data)
ログイン後にコピー

换句话说,就是data内部的属性都被我们监控的,当调用属性时,就可以在上面做些手脚,使得返回的值变掉;当设置属性时,不给他设置。

当然这样做很无聊,只是想说明,我们可以在内部做手脚,实现我们想要的结果。

observe这个函数应该怎么写呢?

function observe(data){
    if(!data || typeof data !== 'object')return //如果 data 不是对象,什么也不做,直接跳出,也就是说只对 对象 操作
    for(let key in data){    //遍历这个对象
        let val = data[key]    //得到这个对象的每一个`value`
        if(typeof val === 'object'){    //如果这个 value 依然是对象,用递归的方式继续调用,直到得到基本值的`value`
            observe(val)
        }
        Object.defineProperty(data,key,{    //定义对象
            configurable:true,    //可删除,原本的对象就能删除
            enumerable:true,    //可遍历,原本的对象就能遍历
            get:function(){
                console.log('这是假的')    //调用属性时,会调用 get 方法,所以调用属性可以在 get 内部做手脚
                //return val    //这里注释掉了,实际调用属性就是把值 return 出去
            },
            set:function(newVal){
                console.log('我不给你设置。。。')    //设置属性时,会调用 set 方法,所以设置属性可以在 set 内部做手脚
                //val = newVal    //这里注释掉了,实际设置属性就是这样写的。
            }
        })
    }
}
ログイン後にコピー

注意两点:

  1. 我们在声明let val = data[key]时,不能用var,因为这里需要对每个属性进行监控,用let每次遍历都会创建一个新的val,在进行赋值;如果用var,只有第一次才是声明,后面都是对一次声明val进行赋值,遍历结束后,得到的是最后一个属性,显然这不是我们需要的。

  2. get方法里,return就是前面声明的val,这里不能用data[key],会报错。因为调用data.name,就是调用get方法时,得到的结果是data.name,又继续调用get方法,就随变成死循环,所以这里需要用一个变量来存储data[key]

    Object.defineProperty(obj,prop,descriptor)
obj: 処理対象のターゲット オブジェクト

prop:定義または変更された属性の名前

descriptor: 定義または変更される属性記述子🎜
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()    //创建主题
let observer1 = new Observer('xiaowang')    //创建观察者1
subject.addObserver(observer1)    //主题添加观察者1
let observer2 = new Observer('xiaozhang')    //创建观察者2
subject.addObserver(observer2)    //主题添加观察者2
subject.notify()    //主题通知观察者

/**** 输出 *****/
hunger update...
valley update...
ログイン後にコピー
ログイン後にコピー
🎜一見すると、少し余分ですが、役に立たないのではないでしょうか。続きを読む🎜

記述子

🎜記述子 にはデータ記述子とストレージ記述子の 2 つの形式があります。どちらも属性を共有します: 🎜🎜configurable (可能かどうか)。削除可能、デフォルトは false、定義後に変更できません🎜🎜enumerable、走査可能かどうか、デフォルトは false、後で変更することはできません🎜

共有属性 h4>🎜 configurablefalse に設定されている場合、その内部プロパティは delete を使用して削除できませんcode>; 削除したい場合は、<code>configurabletrue に設定する必要があります。 🎜
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()    //创建主题
let observer1 = new Observer('xiaowang')    //创建观察者1
subject.addObserver(observer1)    //主题添加观察者1
let observer2 = new Observer('xiaozhang')    //创建观察者2
subject.addObserver(observer2)    //主题添加观察者2
subject.notify()    //主题通知观察者

/**** 输出 *****/
hunger update...
valley update...
ログイン後にコピー
ログイン後にコピー
🎜 enumerablefalse に設定されている場合、その内部プロパティをトラバースする必要がある場合は、enumerable に設定します。 true🎜
class Observer{
  constructor() {
    this.update = function() {
        console.log(name + ' update...')
    }
  }
  subscribeTo(subject) {    //只要用户订阅了主题就会自动添加进忠粉组
    subject.addObserver(this)    //这里的 this 是 Observer 的实例
  }
}

let subject = new Subject()
let observer = new Observer('lisi')
observer.subscribeTo(subject)  //观察者自己订阅忠粉分组
subject.notify()

/****** 输出 *******/
lisi update...
ログイン後にコピー
ログイン後にコピー

データ記述子

🎜value: この属性に対応する値。デフォルトは unknown です。
writable: true の場合、value は代入演算子によって変更できます。デフォルトは false です。 🎜rrreee🎜 writableconfigurable の違いは、前者は value を変更できるかどうか、後者は value を変更できるかどうかです。 は変更・削除可能です。 🎜

ストレージ記述子

🎜get(): プロパティの getter を提供するメソッド。デフォルトは unknown です。 。
set(): プロパティの setter を提供するメソッド。デフォルトは unknown です。 🎜rrreee🎜 obj.age を呼び出すと、実際には obj オブジェクトに age 属性を問い合わせることになります。 obj.get() メソッドを呼び出し、グローバル変数 age を見つけて unknown を取得します。 🎜🎜obj.age = 20 を設定すると、obj.set() メソッドが呼び出され、グローバル変数 age が に設定されます。 >20。 🎜🎜この時点で、obj.age を呼び出すと、20 が返されます。 🎜🎜注: データ記述子とストレージ記述子は同時に存在できません。そうしないとエラーが報告されます 🎜rrreee🎜データインターセプト🎜🎜データを実装するには Object.defineProperty を使用してくださいデータ監視を実現するための傍受。 🎜🎜最初にオブジェクトがあります🎜rrreee🎜 data オブジェクトを監視するための関数を以下に記述します。その後、内部でいくつかの処理を行うことができます🎜rrreee🎜 つまり、それは dataコード > 内部属性はすべて私たちによって監視されます。属性を呼び出すときに、属性を設定するときに戻り値を変更するためのいくつかのトリックを実行できます。 🎜🎜もちろん、これを行うのは退屈ですが、内部的に操作して望む結果を達成できることを示したいだけです。 🎜🎜それでは、<code>observe 関数はどのように記述すべきでしょうか? 🎜rrreee🎜次の 2 つの点に注意してください: 🎜
  1. 🎜 let val = data[key] を宣言する場合、var では、ここで各属性を監視する必要があるため、<code>let を使用すると、各トラバーサルに対して新しい val が作成され、 を使用する場合は値が割り当てられます。 var は最初のみ宣言であり、その後の宣言 val はすべて値が割り当てられます。走査が完了すると、最後の属性が取得されますが、これは明らかに必要なものではありません。 。 🎜🎜
  2. 🎜 get メソッドでは、return は以前に宣言された val です。data[key] は使用できません。コード>を使用すると、エラーが報告されます。 <code>data.name を呼び出すことは、get メソッドを呼び出すことを意味するため、取得される結果は data.name であり、次に get です。 > メソッドが呼び出されます。 code> メソッドは無限ループになるため、data[key] を格納し、この変数を返す変数が必要です。 🎜🎜🎜🎜オブザーバーモード🎜🎜典型的なオブザーバーモードのアプリケーションシナリオ - WeChatパブリックアカウント🎜
    1. 不同的用户(我们把它叫做观察者:Observer)都可以订阅同一个公众号(我们把它叫做主体:Subject)

    2. 当订阅的公众号更新时(主体),用户都能收到通知(观察者)

    用代码怎么实现呢?先看逻辑:

    Subject 是构造函数,new Subject()创建一个主题对象,它维护订阅该主题的一个观察者数组数组(举例来说:Subject 是腾讯推出的公众号,new Subject() 是一个某个机构的公众号——新世相,它要维护订阅这个公众号的用户群体)

    主题上有一些方法,如添加观察者addObserver、删除观察者removeObserver、通知观察者更新notify(举例来说:新世相将用户分为两组,一组是忠粉就是 addObserver,一组是黑名单就是:removeObserver,它在忠粉组可以添加用户,可以在黑名单里拉黑一些杠精,如果有福利发放,它就会统治忠粉里的用户:notify)

    Observer 是构造函数,new Observer() 创建一个观察者对象,该对象有一个update方法(举例来说:Observer 是忠粉用户群体,new Observer() 是某个具体的用户——小王,他必须要打开流量才能收到新世相的福利推送:updata)

    当调用notify时实际上调用全部观察者observer自身的update方法(举例来说:当新世相推送福利时,它会自动帮忠粉组的用户打开流量,这比较极端,只是用来举例)

    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()    //创建主题
    let observer1 = new Observer('xiaowang')    //创建观察者1
    subject.addObserver(observer1)    //主题添加观察者1
    let observer2 = new Observer('xiaozhang')    //创建观察者2
    subject.addObserver(observer2)    //主题添加观察者2
    subject.notify()    //主题通知观察者
    
    /**** 输出 *****/
    hunger update...
    valley 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()    //创建主题
    let observer1 = new Observer('xiaowang')    //创建观察者1
    subject.addObserver(observer1)    //主题添加观察者1
    let observer2 = new Observer('xiaozhang')    //创建观察者2
    subject.addObserver(observer2)    //主题添加观察者2
    subject.notify()    //主题通知观察者
    
    /**** 输出 *****/
    hunger update...
    valley update...
    ログイン後にコピー
    ログイン後にコピー

    ES5 和 ES6 写法效果一样,ES5 的写法更好理解,ES6 只是个语法糖

    主题添加观察者的方法subject.addObserver(observer)很繁琐,直接给观察者下方权限,给他们增加添加进忠粉组的权限

    class Observer{
      constructor() {
        this.update = function() {
            console.log(name + ' update...')
        }
      }
      subscribeTo(subject) {    //只要用户订阅了主题就会自动添加进忠粉组
        subject.addObserver(this)    //这里的 this 是 Observer 的实例
      }
    }
    
    let subject = new Subject()
    let observer = new Observer('lisi')
    observer.subscribeTo(subject)  //观察者自己订阅忠粉分组
    subject.notify()
    
    /****** 输出 *******/
    lisi update...
    ログイン後にコピー
    ログイン後にコピー

    MVVM 框架的内部基本原理就是上面这些。

    相关推荐:

    js实现一个简单的MVVM框架示例分享

    PHP的MVC框架 深入解析_PHP教程

以上がMVVM フレームワークをネイティブ JS で実装する基本原理の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 Dec 17, 2023 pm 02:54 PM

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 はじめに: 技術の継続的な発展により、音声認識技術は人工知能の分野の重要な部分になりました。 WebSocket と JavaScript をベースとしたオンライン音声認識システムは、低遅延、リアルタイム、クロスプラットフォームという特徴があり、広く使用されるソリューションとなっています。この記事では、WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法を紹介します。

WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー Dec 17, 2023 pm 05:30 PM

WebSocketとJavaScript:リアルタイム監視システムを実現するためのキーテクノロジー はじめに: インターネット技術の急速な発展に伴い、リアルタイム監視システムは様々な分野で広く利用されています。リアルタイム監視を実現するための重要なテクノロジーの 1 つは、WebSocket と JavaScript の組み合わせです。この記事では、リアルタイム監視システムにおける WebSocket と JavaScript のアプリケーションを紹介し、コード例を示し、その実装原理を詳しく説明します。 1.WebSocketテクノロジー

JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 Dec 17, 2023 pm 12:09 PM

JavaScript と WebSocket を使用してリアルタイム オンライン注文システムを実装する方法の紹介: インターネットの普及とテクノロジーの進歩に伴い、ますます多くのレストランがオンライン注文サービスを提供し始めています。リアルタイムのオンライン注文システムを実装するには、JavaScript と WebSocket テクノロジを使用できます。 WebSocket は、TCP プロトコルをベースとした全二重通信プロトコルで、クライアントとサーバー間のリアルタイム双方向通信を実現します。リアルタイムオンラインオーダーシステムにおいて、ユーザーが料理を選択して注文するとき

WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 Dec 17, 2023 am 09:39 AM

WebSocket と JavaScript を使用してオンライン予約システムを実装する方法 今日のデジタル時代では、ますます多くの企業やサービスがオンライン予約機能を提供する必要があります。効率的かつリアルタイムのオンライン予約システムを実装することが重要です。この記事では、WebSocket と JavaScript を使用してオンライン予約システムを実装する方法と、具体的なコード例を紹介します。 1. WebSocket とは何ですか? WebSocket は、単一の TCP 接続における全二重方式です。

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 Dec 17, 2023 pm 05:13 PM

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 はじめに: 今日、天気予報の精度は日常生活と意思決定にとって非常に重要です。テクノロジーの発展に伴い、リアルタイムで気象データを取得することで、より正確で信頼性の高い天気予報を提供できるようになりました。この記事では、JavaScript と WebSocket テクノロジを使用して効率的なリアルタイム天気予報システムを構築する方法を学びます。この記事では、具体的なコード例を通じて実装プロセスを説明します。私たちは

簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 Jan 05, 2024 pm 06:08 PM

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

JavaScriptでinsertBeforeを使用する方法 JavaScriptでinsertBeforeを使用する方法 Nov 24, 2023 am 11:56 AM

使用法: JavaScript では、insertBefore() メソッドを使用して、DOM ツリーに新しいノードを挿入します。このメソッドには、挿入される新しいノードと参照ノード (つまり、新しいノードが挿入されるノード) の 2 つのパラメータが必要です。

JavaScript で HTTP ステータス コードを簡単に取得する方法 JavaScript で HTTP ステータス コードを簡単に取得する方法 Jan 05, 2024 pm 01:37 PM

JavaScript で HTTP ステータス コードを取得する方法の紹介: フロントエンド開発では、バックエンド インターフェイスとの対話を処理する必要があることが多く、HTTP ステータス コードはその非常に重要な部分です。 HTTP ステータス コードを理解して取得すると、インターフェイスから返されたデータをより適切に処理できるようになります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法と、具体的なコード例を紹介します。 1. HTTP ステータス コードとは何ですか? HTTP ステータス コードとは、ブラウザがサーバーへのリクエストを開始したときに、サービスが

See all articles