JavaScript---エージェントと反射

41202 ワード

引用:エージェントと反射はjavaScriptが他の言語と異なる独自の特性のように聞こえますが、いくつかの面から言えば、エージェントはC++のポインタに似ていますが、ここでは定義について説明しません.次にいくつかの例をあげると、分かりやすいです.
空のエージェントの作成
let target = {
     
    name: 'lihua',
    age: 18,
    array: [1, 2, 6]
}
let handler = {
     }

let proxy = new Proxy(target, handler)

//  proxy target            ,             
console.log(target.array === proxy.array) //true 
proxy.age = 45
console.log(target.age === proxy.age) //true

//             
console.log(proxy === target)  //false :  proxy target      
    

//Proxy.prototype undefined

エージェントはProxyコンストラクション関数を使用して作成され、このコンストラクション関数は2つのオブジェクトを受信し、いずれのパラメータも欠けているとTypeErrorが放出されます.
最も簡単なエージェントは、抽象的なターゲットオブジェクトとして以外に何もしないという上の空のエージェントのように、次のような行為です.
let a = 'name'
let b = [2, 4]
let target = {
     
    a,
    b
}
let proxy = {
     
    a,
    b
}
console.log(target.b === proxy.b)  //true
console.log(target === proxy)	//false

キャプチャの定義
例えば、targetのプロパティにアクセスしようとすると、実際に得られた値のソースは、get()handerと値を返すget()キャプチャを定義することができる.
let target = {
     
    name: 'lihua'
}
let handler = {
     
    get() {
     
        return 'handle over'
    }
}
let proxy = new Proxy(target, handler)
console.log(proxy.name) //handle over

上図の太字に注意してください.属性にアクセスしないとしたら?proxyのプロパティ値はhanderで処理されません.
let target = {
     
    name: 'lihua'
}
let handler = {
     
    get() {
     
        return 'handle over'
    }
}
let proxy = new Proxy(target, handler)

console.log(proxy) 	// { name: 'lihua' }
console.log(proxy.name) // handle over

これは通常のオブジェクトに比べて不思議な現象であり,次にhanderオブジェクトとget()キャプチャをさらに理解する.
キャプチャパラメータと反射API
すべてのキャプチャは応答のパラメータにアクセスできます.get()キャプチャは、ターゲットオブジェクト、クエリーするプロパティ、エージェントオブジェクトの3つのパラメータを受信するなど、キャプチャされたメソッドの元の動作を再構築できます.
let target = {
     
    name: 'lihua',
    age: 18,
    array: [1, 2, 6]
}
let handler = {
     
	//      
    get(trapTarget, property, reciver) {
     
        console.log(trapTarget === target)
        console.log(property)
        console.log(reciver === proxy)
        return undefined
    }
}
let proxy = new Proxy(target, handler)
//     ,       proxy   ,hander  get()        ,      

consoel.log(proxy.age)  //  get  ,  
//true
//age
//true
//undefined 	proxy.age   get      

これらのパラメータを使用すると、キャプチャされた元の動作を再構築できます.
let target = {
     
    name: 'lihua',
    age: 18,
    array: [1, 2, 6]
}
let handler = {
     
    get(trapTarget, property, reciver) {
     
        return trapTarget[property]
    }
}
let proxy = new Proxy(target, handler)
console.log(proxy.age) //18

すべてのキャプチャは、独自のパラメータに基づいて元の操作を再構築できますが、get()のようにすべてのキャプチャの動作が簡単ではないため、手書きコードで法のように砲撃する考えは現実的ではありません.
プロセッサ・オブジェクト内のすべてのキャプチャ可能なメソッドには、対応するリフレクションAPIメソッドがあり、このように空のエージェント・オブジェクトを定義することができます.
let target = {
     
    name: 'lihua',
    age: 18,
    array: [1, 2, 6]
}
let handler = {
     
    get(trapTarget, property, reciver) {
     
        return Reflect.get(trapTarget, property, reciver)
    }
}
let proxy = new Proxy(target, handler)
console.log(proxy.age) //18

さらに、handerは、より簡潔にすることができます.
let handler = {
     
    get: Reflect.get
}

反射処理値を簡単に利用
let target = {
     
    word: 'hello'
}
let handler = {
     
    get() {
     
        return Reflect.get(...arguments) + ' world'
    }
}
let proxy = new Proxy(target, handler)
console.log(proxy.word) //hello world

エージェントの条件-キャプチャ不変
キャプチャを使用すると、ほとんどの基本的な方法の動作を変更できますが、制限はありません.
キャプチャプロセッサの動作は、キャプチャ不変式に従う必要があります.トラップの不変形は方法によって異なります
たとえば、ターゲットオブジェクトに のデータ属性がある場合、TypeErrorが放出されます.
let target = {
     
    word: 'hello'
}
Object.defineProperty(target, 'age', {
     
    writable: false,  //    
    configurable: false,  //    
    value: 48
})
let handler = {
     
    get() {
     
        return Reflect.get(...arguments) + ' world'
    }
}
let proxy = new Proxy(target, handler)
console.log(proxy.age) 
//TypeError

取り消し可能エージェント
エージェントオブジェクトとターゲットの連絡を中断する必要がある場合があります.new Proxyコンストラクション関数で作成されたオブジェクトがエージェントを取り消すことができない場合は、
Proxyには、エージェントオブジェクトを作成し、revocableの取り消しメソッドを暴露するためのrevokeメソッドが暴露されています.revocableメソッドの戻り値は、{「proxy」:proxy,「revoke」:revoke}という構造のオブジェクトです.Proxy.revocableは、エージェントオブジェクトとターゲットとの関連付けを取り消すために使用され、エージェントを取り消す操作は不可逆的である.
let target = {
     
    word: 'hello'
}

let handler = {
     
    get() {
     
        return Reflect.get(...arguments) + ' world'
    }
}
let {
      proxy, revoke } = Proxy.revocable(target, handler)
console.log(proxy.word) //hello world
revoke()
console.log(proxy.word) //TypeError

反射APIとオブジェクトAPI
反射APIを使用する場合は、次のことを覚えておいてください.
  • 反射APIは、キャプチャ処理プログラム
  • に限定されない.
  • ほとんどの反射APIメソッドは、Objectタイプに対応するメソッド
  • を有する.
    通常、Object上の方法は汎用プログラムに用いられ、反射方法は細粒度のオブジェクト制御と操作に適している.
    1.ステータスタグ:
    多くの反射メソッドは、実行しようとする操作が成功したかどうかを示す「ステータスタグ」と呼ばれるブール値を返します.ステータスタグは、エラーを投げ出す可能性のある方法よりも役に立つ場合があります.
    let o = {
         }
    Object.freeze(o)
    try {
         
        Object.defineProperty(o, 'name', {
          value: 'lihua' })
    } catch (error) {
         
        console.log('error')
    }
    //error
    

    反射APIで再構築
    const o = {
         }
    Object.freeze(o)
    if (Reflect.defineProperty(o, 'name', {
          value: 'lihua' })) {
         
        console.log('success')
    } else {
         
        console.log('fail')
    }
    //fail
    

    次の反射方法では、ステータスタグが提供されます.
    Reflect.defineProperty()
    Reflect.preventExtensions()
    Reflect.setPrototypeOf()
    Reflect.set()
    Reflect.deleteProperty()
    

    2.オペレータの代わりに一等関数を使用
    次の反射方法では、オペレータのみが実行できる操作を提供します.
    Reflect.get():オブジェクト属性アクセス操作法Reflectに代わることができる.set():=の代入オペレータReflectを代替することができる.has():inオペレータまたはwith() Reflectに代わることができる.deleteProperty():deleteオペレータReflectの代わりに使用できます.construct():newオペレータの代わりに使用できます.
    例:
    let o = {
         
        name: 'ming'
    }
    // Reflect.get():              
    console.log(Reflect.get(o, 'name'))  //ming
    
    // Reflect.set():     =     
    Reflect.set(o, 'age', 18)  
    
    // Reflect.has():      in      with()
    console.log(Reflect.has(o, 'age'))  //true
    
    // Reflect.deleteProperty():     delete   
    console.log(Reflect.deleteProperty(o, 'name')) //true
    
    // Reflect.construct():     new   
    console.log(Reflect.construct(Array, [2, 5, 6])) //[ 2, 5, 6 ]
    

    エージェント別エージェント
    エージェントは、反射APIの操作をブロックできます.これは、ターゲットオブジェクト上に多層ブロックネットワークを構築するために、別のエージェントオブジェクトをエージェントするエージェントオブジェクトを完全に作成できることを意味します.
    let target = {
         
        name: 'lihua'
    }
    
    let firstProxy = new Proxy(target, {
         
        get() {
         
            console.log('first proxy')
            return Reflect.get(...arguments)
        }
    })
    
    let secondProxy = new Proxy(firstProxy, {
         
        get() {
         
            console.log('second proxy')
            return Reflect.get(...arguments)
        }
    })
    
    console.log(secondProxy.name)
    //second proxy
    //first proxy
    //lihua
    

    エージェントの問題と不足
    エージェントはECMAScriptの既存のベースに構築された新しいAPIであるため、実際には最善を尽くしています.しかし、場合によっては、エージェントも現在のECMAScriptメカニズムとうまく連携できない場合があります.
    1.エージェントのthis
    エージェントの現在の問題のソースはthis値です.
    let target = {
         
        thisVal() {
         
            console.log(this === proxy)
            console.log(this === target)
        }
    }
    let proxy = new Proxy(target, {
         })
    proxy.thisVal()
    // true
    // false
    target.thisVal()
    // false 
    // true
    

    多くの場合、これは予想に合致する行為です.しかし、ターゲットオブジェクトがオブジェクトIDに依存している場合、予想外の状況に遭遇する可能性があります.少し曲がった例を見てみましょう.
    const wm = new WeakMap()
    
    class User {
         
        constructor(userId) {
         
            wm.set(this, userId)
        }
        set id(userId) {
         
            wm.set(this, userId)
        }
        get id() {
         
            return wm.get(this)
        }
    }
    const user = new User(123)
    const proxy = new Proxy(user, {
         })	//     this    
    console.log(proxy.id) 
    //undefined
    

    これは,Userインスタンスが最初にターゲットオブジェクトをWeakMapのキーとして使用していたのに,エージェントオブジェクトが自身からこのインスタンスを取得しようとしたためである.
    この問題を解決するには、エージェントを再構成し、エージェントUserインスタンスをエージェントUserクラス自体に変更します.その後エージェントを作成するインスタンスは、そのエージェントインスタンスをWeakMapのキーとして使用します.
    const wm = new WeakMap()
    
    class User {
         
        constructor(userId) {
         
            wm.set(this, userId)
        }
        set id(userId) {
         
            wm.set(this, userId)
        }
        get id() {
         
            return wm.get(this)
        }
    }
    
    const UserClassProxy = new Proxy(User, {
         })
    const proxyUser = new UserClassProxy(123)
    console.log(proxyUser.id) //123
    

    もしこの文章があなたに役に立つならば、いいね+コレクション+関心を歓迎して、後でもっと良質な内容を出すことができます