Maison > interface Web > js tutoriel > le corps du texte

Introduction détaillée à DefineProperty et proxy dans ES6 (exemple de code)

不言
Libérer: 2018-11-15 16:59:31
avant
2388 Les gens l'ont consulté

Cet article vous apporte une introduction détaillée (exemple de code) sur DefineProperty et proxy dans ES6. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

Nous avons tous plus ou moins entendu le mot « liaison de données ». La clé de la « liaison de données » est de surveiller les changements dans les données, mais pour un tel objet : var obj = {value: 1}, comment savons-nous obj Quoi ? a changé ?

definePropety

ES5 fournit la méthode Object.defineProperty, qui peut définir une nouvelle propriété sur un objet, ou modifier une propriété existante d'un objet et renvoyer l'objet.

Syntaxe

Object.defineProperty(obj, prop, descriptor)
Copier après la connexion

Paramètres

obj: 要在其上定义属性的对象。
prop:  要定义或修改的属性的名称。
descriptor: 将被定义或修改的属性的描述符。
Copier après la connexion

Par exemple :

var obj = {};
Object.defineProperty(obj, "num", {
    value : 1,
    writable : true,
    enumerable : true,
    configurable : true
});
//  对象 obj 拥有属性 num,值为 1
Copier après la connexion

Bien que nous puissions ajouter des propriétés et des valeurs directement, en utilisant cette méthode, nous pouvons effectuer davantage de configuration. Le descripteur d'attribut représenté par le troisième descripteur de paramètre de la fonction

a deux formes : descripteur de données et descripteur d'accès .

Les deux ont les deux valeurs clés suivantes  :

configurable

si et seulement si l'attribut configurable est Quand vrai, le descripteur de propriété peut être modifié ou supprimé. La valeur par défaut est fausse.

enumerable

Si et seulement si l'énumérable de l'attribut est vrai, l'attribut peut apparaître dans l'attribut d'énumération de l'objet. La valeur par défaut est fausse.

Le descripteur de données possède également les valeurs clés facultatives suivantes  :

valeur

La valeur correspondant à cet attribut. Il peut s'agir de n'importe quelle valeur JavaScript valide (nombre, objet, fonction, etc.). La valeur par défaut n'est pas définie.

writable

Cette propriété peut être modifiée par l'opérateur d'affectation si et seulement si l'écriture de la propriété est vraie. La valeur par défaut est fausse.

Le descripteur d'accès a également les valeurs de clés facultatives suivantes  :

get

celle fournie pour la méthode Getter d'attribut, s'il n'y a pas de getter, il n'est pas défini. La valeur de retour de cette méthode est utilisée comme valeur d'attribut. La valeur par défaut n'est pas définie.

set

Une méthode qui fournit un setter pour une propriété. S'il n'y a pas de setter, elle n'est pas définie. . Cette méthode acceptera un paramètre unique et attribuera la nouvelle valeur du paramètre à la propriété. La valeur par défaut n'est pas définie.

Il convient de noter :

Les descripteurs d'attribut doivent être soit des descripteurs de données, soit des descripteurs d'accès, pas les deux en même temps. Cela signifie que vous pouvez :

Object.defineProperty({}, "num", {
    value: 1,
    writable: true,
    enumerable: true,
    configurable: true
});
Copier après la connexion

Également :

var value = 1;
Object.defineProperty({}, "num", {
    get : function(){
      return value;
    },
    set : function(newValue){
      value = newValue;
    },
    enumerable : true,
    configurable : true
});
Copier après la connexion

Mais pas :

// 报错
Object.defineProperty({}, "num", {
    value: 1,
    get: function() {
        return 1;
    }
});
Copier après la connexion

De plus, toutes les descriptions d'attributs Les descripteurs ne sont pas obligatoires , mais le champ descripteur est obligatoire. Si vous n'effectuez aucune configuration, vous pouvez faire ceci :

var obj = Object.defineProperty({}, "num", {});
console.log(obj.num); // undefined
Copier après la connexion

Setters et Getters

La raison pour laquelle nous parlons de DefineProperty est parce que nous en avons besoin. pour utiliser get et set dans le descripteur d'accès. Ces deux méthodes sont également appelées getters et setters. Les propriétés définies par les getters et les setters sont appelées « propriétés d'accesseur ».

Lorsqu'un programme interroge la valeur d'une propriété d'accesseur, JavaScript appelle la méthode getter. La valeur de retour de cette méthode est la valeur de l'expression d'accès à l'attribut. Lorsqu'un programme définit la valeur d'une propriété d'accesseur, JavaScript appelle la méthode setter, en transmettant la valeur située à droite de l'expression d'affectation en tant que paramètre au setter. Dans un sens, cette méthode est chargée de « définir » la valeur de la propriété. La valeur de retour de la méthode setter peut être ignorée.

Par exemple :

var obj = {}, value = null;
Object.defineProperty(obj, "num", {
    get: function(){
        console.log('执行了 get 操作')
        return value;
    },
    set: function(newValue) {
        console.log('执行了 set 操作')
        value = newValue;
    }
})

obj.value = 1 // 执行了 set 操作

console.log(obj.value); // 执行了 get 操作 // 1
Copier après la connexion

N'est-ce pas la méthode avec laquelle nous souhaitons surveiller les modifications des données ? Encapsulons-le à nouveau :

function Archiver() {
    var value = null;
    // archive n. 档案
    var archive = [];

    Object.defineProperty(this, 'num', {
        get: function() {
            console.log('执行了 get 操作')
            return value;
        },
        set: function(value) {
            console.log('执行了 set 操作')
            value = value;
            archive.push({ val: value });
        }
    });

    this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.num; // 执行了 get 操作
arc.num = 11; // 执行了 set 操作
arc.num = 13; // 执行了 set 操作
console.log(arc.getArchive()); // [{ val: 11 }, { val: 13 }]
Copier après la connexion

watch API

Étant donné que les modifications des données peuvent être surveillées, je peux imaginer que lorsque les données changent, le travail de rendu sera automatiquement effectué. Par exemple :

Il y a une balise span et une balise bouton en HTML

<span id="container">1</span>
<button id="button">点击加 1</button>
Copier après la connexion

Lorsque l'on clique sur le bouton, la valeur de la balise span est augmentée de 1.

L'approche traditionnelle est :

document.getElementById(&#39;button&#39;).addEventListener("click", function(){
    var container = document.getElementById("container");
    container.innerHTML = Number(container.innerHTML) + 1;
});
Copier après la connexion

Si DefineProperty est utilisé :

var obj = {
    value: 1
}

// 储存 obj.value 的值
var value = 1;

Object.defineProperty(obj, "value", {
    get: function() {
        return value;
    },
    set: function(newValue) {
        value = newValue;
        document.getElementById(&#39;container&#39;).innerHTML = newValue;
    }
});

document.getElementById(&#39;button&#39;).addEventListener("click", function() {
    obj.value += 1;
});
Copier après la connexion

Le code semble augmenter, mais lorsque nous devons changer la valeur dans le span tagQuand, modifiez simplement la valeur de obj.value directement.

Cependant, avec la façon actuelle d'écrire, nous devons toujours déclarer une variable distincte pour stocker la valeur de obj.value, car si vous obj.value = newValue directement dans set, vous tomberez dans une boucle infinie. De plus, nous devrons peut-être surveiller les changements dans de nombreuses valeurs d'attributs. Il serait fastidieux de les écrire une par une, nous écrivons donc simplement une fonction de surveillance. L'effet d'utilisation est le suivant :

var obj = {
    value: 1
}

watch(obj, "num", function(newvalue){
    document.getElementById(&#39;container&#39;).innerHTML = newvalue;
})

document.getElementById(&#39;button&#39;).addEventListener("click", function(){
    obj.value += 1
});
Copier après la connexion

Écrivons cette fonction de surveillance :

(function(){
    var root = this;
    function watch(obj, name, func){
        var value = obj[name];

        Object.defineProperty(obj, name, {
            get: function() {
                return value;
            },
            set: function(newValue) {
                value = newValue;
                func(value)
            }
        });

        if (value) obj[name] = value
    }

    this.watch = watch;
})()
Copier après la connexion

Nous pouvons maintenant surveiller les changements dans les valeurs d'attribut de l'objet, et en fonction des changements dans le valeurs d'attribut, ajoutez une fonction de rappel, génial ~

proxy

L'utilisation de definitionProperty ne peut redéfinir que les comportements de lecture (get) et de définition (set) des propriétés Dans ES6, le proxy est fourni et peut être. redéfini. Plus de comportements, tels que l'entrée, la suppression, les appels de fonction et plus encore.

Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。我们来看看它的语法:

var proxy = new Proxy(target, handler);
Copier après la connexion

proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

var proxy = new Proxy({}, {
    get: function(obj, prop) {
        console.log(&#39;设置 get 操作&#39;)
        return obj[prop];
    },
    set: function(obj, prop, value) {
        console.log(&#39;设置 set 操作&#39;)
        obj[prop] = value;
    }
});

proxy.time = 35; // 设置 set 操作

console.log(proxy.time); // 设置 get 操作 // 35
Copier après la connexion

除了 get 和 set 之外,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。

// 使用 has 方法隐藏某些属性,不被 in 运算符发现
var handler = {
  has (target, key) {
    if (key[0] === &#39;_&#39;) {
      return false;
    }
    return key in target;
  }
};
var target = { _prop: &#39;foo&#39;, prop: &#39;foo&#39; };
var proxy = new Proxy(target, handler);
console.log(&#39;_prop&#39; in proxy); // false
Copier après la connexion

又比如说 apply 方法拦截函数的调用、call 和 apply 操作。

apply 方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组,不过这里我们简单演示一下:

var target = function () { return &#39;I am the target&#39;; };
var handler = {
  apply: function () {
    return &#39;I am the proxy&#39;;
  }
};

var p = new Proxy(target, handler);

p();
// "I am the proxy"
Copier après la connexion

又比如说 ownKeys 方法可以拦截对象自身属性的读取操作。具体来说,拦截以下操作:

  • Object.getOwnPropertyNames()

  • Object.getOwnPropertySymbols()

  • Object.keys()

下面的例子是拦截第一个字符为下划线的属性名,不让它被 for of 遍历到。

let target = {
  _bar: &#39;foo&#39;,
  _prop: &#39;bar&#39;,
  prop: &#39;baz&#39;
};

let handler = {
  ownKeys (target) {
    return Reflect.ownKeys(target).filter(key => key[0] !== &#39;_&#39;);
  }
};

let proxy = new Proxy(target, handler);
for (let key of Object.keys(proxy)) {
  console.log(target[key]);
}
// "baz"
Copier après la connexion

更多的拦截行为可以查看阮一峰老师的 《ECMAScript 6 入门》

值得注意的是,proxy 的最大问题在于浏览器支持度不够,而且很多效果无法使用 poilyfill 来弥补。

watch API 优化

我们使用 proxy 再来写一下 watch 函数。使用效果如下:

(function() {
    var root = this;

    function watch(target, func) {

        var proxy = new Proxy(target, {
            get: function(target, prop) {
                return target[prop];
            },
            set: function(target, prop, value) {
                target[prop] = value;
                func(prop, value);
            }
        });

        if(target[name]) proxy[name] = value;
        return proxy;
    }

    this.watch = watch;
})()

var obj = {
    value: 1
}

var newObj = watch(obj, function(key, newvalue) {
    if (key == &#39;value&#39;) document.getElementById(&#39;container&#39;).innerHTML = newvalue;
})

document.getElementById(&#39;button&#39;).addEventListener("click", function() {
    newObj.value += 1
});
Copier après la connexion

我们也可以发现,使用 defineProperty 和 proxy 的区别,当使用 defineProperty,我们修改原来的 obj 对象就可以触发拦截,而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal