Table of Contents
definePropety
Setters and Getters
watch API
proxy
watch API 优化
Home Web Front-end JS Tutorial Detailed introduction to defineProperty and proxy in ES6 (code example)

Detailed introduction to defineProperty and proxy in ES6 (code example)

Nov 15, 2018 pm 04:59 PM
handler javascript Interactive Design function front end

This article brings you a detailed introduction (code example) about defineProperty and proxy in ES6. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

We have all heard the word "data binding" more or less. The key to "data binding" is to monitor changes in data, but for such an object: var obj = {value: 1}, how do we know that obj has changed?

definePropety

ES5 provides the Object.defineProperty method, which can define a new property on an object, or modify an existing property of an object, and return the object.

Syntax

Object.defineProperty(obj, prop, descriptor)
Copy after login

Parameters

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

For example:

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

Although we can add it directly Properties and values, but this way we can do more configuration.

The attribute descriptor represented by the third parameter descriptor of the function has two forms: data descriptor and access descriptor.

Both have the following two key values:

configurable

If and only if the attribute's configurable is When true, the property descriptor can be changed or deleted. Default is false.

enumerable

If and only if the enumerable of the property is true, the property can appear in the enumeration property of the object. Default is false.

The data descriptor also has the following optional key values:

value

The value corresponding to this attribute . Can be any valid JavaScript value (number, object, function, etc.). Default is undefined.

writable

The property can be changed by the assignment operator if and only if the property's writable is true. Default is false.

The access descriptor also has the following optional key values:

get

One provided for the attribute Getter method, if there is no getter, it is undefined. The return value of this method is used as the attribute value. Default is undefined.

set

A method that provides a setter for a property. If there is no setter, it will be undefined. This method will accept a unique parameter and assign the parameter's new value to the property. Default is undefined.

It is worth noting:

The attribute descriptor must be one of two forms: data descriptor or access descriptor, not both at the same time . This means that you can:

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

You can also:

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

But you cannot:

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

In addition, all attribute descriptors are optional, But the descriptor field is required. If you don’t configure anything, you can do this:

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

Setters and Getters

The reason why we talk about defineProperty is because we want to use the access descriptor get and set, these two methods are also called getter and setter. Properties defined by getters and setters are called "accessor properties".

When a program queries the value of an accessor property, JavaScript calls the getter method. The return value of this method is the value of the attribute access expression. When a program sets the value of an accessor property, JavaScript calls the setter method, passing the value on the right side of the assignment expression as a parameter to the setter. In a sense, this method is responsible for "setting" the property value. The return value of the setter method can be ignored.

For example:

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
Copy after login

Isn’t this the method we want to monitor data changes? Let’s encapsulate it again:

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 }]
Copy after login

watch API

Since we can monitor data changes, I can imagine that when the data changes, the rendering work will be automatically performed. For example:

There is a span tag and a button tag in HTML

<span id="container">1</span>
<button id="button">点击加 1</button>
Copy after login

When the button is clicked, the value in the span tag is increased by 1.

The traditional approach is:

document.getElementById(&#39;button&#39;).addEventListener("click", function(){
    var container = document.getElementById("container");
    container.innerHTML = Number(container.innerHTML) + 1;
});
Copy after login

If defineProperty is used:

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;
});
Copy after login

The code seems to increase, but when we need to change the value in the span tag, directly Just modify the value of obj.value.

However, with the current way of writing, we still need to declare a separate variable to store the value of obj.value, because if you directly obj.value = newValue in the set, you will fall into an infinite loop. middle. In addition, we may need to monitor changes in many attribute values. It would be tiring to write them one by one, so we simply write a watch function. The effect of use is as follows:

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
});
Copy after login

Let’s write this watch function:

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

Now we can monitor changes in object attribute values, and add callback functions based on changes in attribute values, great Great~

proxy

Using defineProperty can only redefine the read (get) and set (set) behaviors of properties. In ES6, Proxy is provided, and more behaviors can be redefined. , such as in, delete, function call and more behaviors.

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

var proxy = new Proxy(target, handler);
Copy after login

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
Copy after login

除了 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
Copy after login

又比如说 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"
Copy after login

又比如说 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"
Copy after login

更多的拦截行为可以查看阮一峰老师的 《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
});
Copy after login

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

The above is the detailed content of Detailed introduction to defineProperty and proxy in ES6 (code example). For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Tips for dynamically creating new functions in golang functions Tips for dynamically creating new functions in golang functions Apr 25, 2024 pm 02:39 PM

Go language provides two dynamic function creation technologies: closure and reflection. closures allow access to variables within the closure scope, and reflection can create new functions using the FuncOf function. These technologies are useful in customizing HTTP routers, implementing highly customizable systems, and building pluggable components.

Considerations for parameter order in C++ function naming Considerations for parameter order in C++ function naming Apr 24, 2024 pm 04:21 PM

In C++ function naming, it is crucial to consider parameter order to improve readability, reduce errors, and facilitate refactoring. Common parameter order conventions include: action-object, object-action, semantic meaning, and standard library compliance. The optimal order depends on the purpose of the function, parameter types, potential confusion, and language conventions.

Complete collection of excel function formulas Complete collection of excel function formulas May 07, 2024 pm 12:04 PM

1. The SUM function is used to sum the numbers in a column or a group of cells, for example: =SUM(A1:J10). 2. The AVERAGE function is used to calculate the average of the numbers in a column or a group of cells, for example: =AVERAGE(A1:A10). 3. COUNT function, used to count the number of numbers or text in a column or a group of cells, for example: =COUNT(A1:A10) 4. IF function, used to make logical judgments based on specified conditions and return the corresponding result.

How to write efficient and maintainable functions in Java? How to write efficient and maintainable functions in Java? Apr 24, 2024 am 11:33 AM

The key to writing efficient and maintainable Java functions is: keep it simple. Use meaningful naming. Handle special situations. Use appropriate visibility.

Comparison of the advantages and disadvantages of C++ function default parameters and variable parameters Comparison of the advantages and disadvantages of C++ function default parameters and variable parameters Apr 21, 2024 am 10:21 AM

The advantages of default parameters in C++ functions include simplifying calls, enhancing readability, and avoiding errors. The disadvantages are limited flexibility and naming restrictions. Advantages of variadic parameters include unlimited flexibility and dynamic binding. Disadvantages include greater complexity, implicit type conversions, and difficulty in debugging.

What are the benefits of C++ functions returning reference types? What are the benefits of C++ functions returning reference types? Apr 20, 2024 pm 09:12 PM

The benefits of functions returning reference types in C++ include: Performance improvements: Passing by reference avoids object copying, thus saving memory and time. Direct modification: The caller can directly modify the returned reference object without reassigning it. Code simplicity: Passing by reference simplifies the code and requires no additional assignment operations.

What is the difference between custom PHP functions and predefined functions? What is the difference between custom PHP functions and predefined functions? Apr 22, 2024 pm 02:21 PM

The difference between custom PHP functions and predefined functions is: Scope: Custom functions are limited to the scope of their definition, while predefined functions are accessible throughout the script. How to define: Custom functions are defined using the function keyword, while predefined functions are defined by the PHP kernel. Parameter passing: Custom functions receive parameters, while predefined functions may not require parameters. Extensibility: Custom functions can be created as needed, while predefined functions are built-in and cannot be modified.

C++ Function Exception Advanced: Customized Error Handling C++ Function Exception Advanced: Customized Error Handling May 01, 2024 pm 06:39 PM

Exception handling in C++ can be enhanced through custom exception classes that provide specific error messages, contextual information, and perform custom actions based on the error type. Define an exception class inherited from std::exception to provide specific error information. Use the throw keyword to throw a custom exception. Use dynamic_cast in a try-catch block to convert the caught exception to a custom exception type. In the actual case, the open_file function throws a FileNotFoundException exception. Catching and handling the exception can provide a more specific error message.

See all articles