Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Einführung in defineProperty und Proxy in ES6 (Codebeispiel)

Detaillierte Einführung in defineProperty und Proxy in ES6 (Codebeispiel)

不言
不言nach vorne
2018-11-15 16:59:312308Durchsuche

Dieser Artikel bietet Ihnen eine detaillierte Einführung (Codebeispiel) zu defineProperty und Proxy. Ich hoffe, dass er für Sie hilfreich ist.

Wir haben alle mehr oder weniger das Wort „Datenbindung“ gehört. Der Schlüssel zur „Datenbindung“ liegt in der Überwachung von Datenänderungen, aber für ein solches Objekt: var obj = {value: 1}, woher wissen wir, obj Was hat sich geändert?

definePropety

ES5 stellt die Methode Object.defineProperty bereit, die eine neue Eigenschaft für ein Objekt definieren oder eine vorhandene Eigenschaft eines Objekts ändern und das Objekt zurückgeben kann.

Syntax

Object.defineProperty(obj, prop, descriptor)

Parameter

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

Zum Beispiel:

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

Obwohl wir es direkt hinzufügen können Eigenschaften und Werte, aber mit diesem Ansatz können wir mehr Konfigurationen vornehmen. Der Attributdeskriptor, der durch den dritten Parameterdeskriptor der Funktion

dargestellt wird, hat zwei Formen: Datendeskriptor und Zugriffsdeskriptor .

Beide haben die folgenden zwei Schlüsselwerte :

konfigurierbar

genau dann, wenn das konfigurierbare Attribut „Wann“ ist true, der Eigenschaftsdeskriptor kann geändert oder gelöscht werden. Der Standardwert ist falsch.

aufzählbar

Nur ​​wenn die Aufzählung des Attributs wahr ist, kann das Attribut im Aufzählungsattribut des Objekts erscheinen. Der Standardwert ist falsch.

Der Datendeskriptor verfügt außerdem über die folgenden optionalen Schlüsselwerte :

Wert

Der entsprechende Wert zu diesem Attribut. Kann jeder gültige JavaScript-Wert sein (Zahl, Objekt, Funktion usw.). Der Standardwert ist undefiniert.

beschreibbar

Diese Eigenschaft kann vom Zuweisungsoperator genau dann geändert werden, wenn beschreibbar der Eigenschaft wahr ist. Der Standardwert ist falsch.

Der Zugriffsdeskriptor verfügt außerdem über die folgenden optionalen Schlüsselwerte :

get

eins vorgesehen Die Attribut-Getter-Methode. Wenn kein Getter vorhanden ist, ist sie undefiniert. Der Rückgabewert dieser Methode wird als Attributwert verwendet. Der Standardwert ist undefiniert.

set

Eine Methode, die einen Setter für eine Eigenschaft bereitstellt. Wenn kein Setter vorhanden ist, ist er undefiniert . Diese Methode akzeptiert einen eindeutigen Parameter und weist der Eigenschaft den neuen Wert dieses Parameters zu. Der Standardwert ist undefiniert.

Es ist erwähnenswert:

Attributdeskriptoren müssen entweder Datendeskriptoren oder Zugriffsdeskriptoren sein, nicht beides gleichzeitig. Das heißt, Sie können:

Object.defineProperty({}, "num", {
    value: 1,
    writable: true,
    enumerable: true,
    configurable: true
});

oder Sie können:

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

, aber nicht:

// 报错
Object.defineProperty({}, "num", {
    value: 1,
    get: function() {
        return 1;
    }
});

Darüber hinaus sind alle Eigenschaftsdeskriptoren optional, das Deskriptorfeld ist jedoch erforderlich . Wenn Sie nichts konfigurieren, können Sie Folgendes tun:

var obj = Object.defineProperty({}, "num", {});
console.log(obj.num); // undefined

Setter und Getter

Der Grund, warum wir über defineProperty sprechen, ist, dass wir den Zugriffsdeskriptor get und set verwenden möchten. Diese beiden Methoden werden auch Getter und Setter genannt. Durch Getter und Setter definierte Eigenschaften werden als „Accessor-Eigenschaften“ bezeichnet.

Wenn ein Programm den Wert einer Accessor-Eigenschaft abfragt, ruft JavaScript die Getter-Methode auf. Der Rückgabewert dieser Methode ist der Wert des Attributzugriffsausdrucks. Wenn ein Programm den Wert einer Accessor-Eigenschaft festlegt, ruft JavaScript die Setter-Methode auf und übergibt den Wert auf der rechten Seite des Zuweisungsausdrucks als Parameter an den Setter. In gewissem Sinne ist diese Methode für das „Festlegen“ des Eigenschaftswerts verantwortlich. Der Rückgabewert der Setter-Methode kann ignoriert werden.

Zum Beispiel:

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

Ist das nicht die Art und Weise, wie wir Datenänderungen überwachen wollen? Fassen wir es noch einmal zusammen:

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 }]

watch API

Da Datenänderungen überwacht werden können, kann ich mir vorstellen, dass bei Datenänderungen die Rendering-Arbeit automatisch ausgeführt wird. Beispiel:

Es gibt ein Span-Tag und ein Button-Tag in HTML

e729387c80c75e8349ab06d70943ab81154bdf357c58b8a65c66d7c19c8e4d114
be83eb51f340b921e483a6b46a36fe5f点击加 165281c5ac262bf6d81768915a4a77ac0

Wenn auf die Schaltfläche geklickt wird, wird der Wert im Span-Tag um 1 erhöht.

Der traditionelle Ansatz ist:

document.getElementById('button').addEventListener("click", function(){
    var container = document.getElementById("container");
    container.innerHTML = Number(container.innerHTML) + 1;
});

Wenn defineProperty verwendet wird:

var obj = {
    value: 1
}

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

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

document.getElementById('button').addEventListener("click", function() {
    obj.value += 1;
});

Der Code scheint zu wachsen, aber wenn wir den Wert im Span-Tag direkt ändern müssen Ändern Sie einfach den Wert von obj.value.

Bei der aktuellen Schreibweise müssen wir jedoch immer noch eine separate Variable deklarieren, um den Wert von obj.value zu speichern, denn wenn Sie direkt obj.value = newValue in set eingeben, geraten Sie in eine Endlosschleife. Darüber hinaus müssen wir möglicherweise Änderungen in vielen Attributwerten überwachen. Es wäre ermüdend, sie einzeln zu schreiben, also schreiben wir einfach eine Überwachungsfunktion. Die Auswirkung der Verwendung ist wie folgt:

var obj = {
    value: 1
}

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

document.getElementById('button').addEventListener("click", function(){
    obj.value += 1
});

Schreiben wir diese Überwachungsfunktion:

(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;
})()

Jetzt können wir Änderungen in Objektattributwerten überwachen und Rückruffunktionen basierend auf Änderungen in Attributwerten hinzufügen ~

Proxy

Die Verwendung von defineProperty kann nur das Leseverhalten (Get) und das Festlegen (Setzen) von Eigenschaften neu definieren. In ES6 wird Proxy bereitgestellt, und es können weitere Verhaltensweisen neu definiert werden in, löschen, Funktionsaufruf und weitere Verhaltensweisen.

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

var proxy = new Proxy(target, handler);

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

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

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

console.log(proxy.time); // 设置 get 操作 // 35

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

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

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

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

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

var p = new Proxy(target, handler);

p();
// "I am the proxy"

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

  • Object.getOwnPropertyNames()

  • Object.getOwnPropertySymbols()

  • Object.keys()

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

let target = {
  _bar: 'foo',
  _prop: 'bar',
  prop: 'baz'
};

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

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

更多的拦截行为可以查看阮一峰老师的 《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 == 'value') document.getElementById('container').innerHTML = newvalue;
})

document.getElementById('button').addEventListener("click", function() {
    newObj.value += 1
});

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

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in defineProperty und Proxy in ES6 (Codebeispiel). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen