Home > Web Front-end > JS Tutorial > body text

Use Object.defineProperty to implement simple js two-way binding_javascript skills

WBOY
Release: 2016-05-16 15:05:23
Original
1600 people have browsed it

Origin

A few days ago I was looking at the implementation of some popular mini mvvm frameworks (such as lighter frameworks such as avalon.js and vue.js, rather than heavier frameworks such as Angularjs and Emberjs). Modern popular mvvm frameworks generally remove two-ways data binding as a selling point of the framework itself (Ember.js does not seem to support two-way data binding.), and each framework has two-way data binding. The implementation methods of binding are not consistent. For example, Anguarjs uses dirty checking internally, while the essence of the internal implementation method of avalon.js is to set property accessors.

I am not going to discuss the specific implementation of two-way data binding by each framework here. I will only talk about several common methods of implementing two-way data binding on the front end, and focus on the technology selection of avalon.js to implement two-way data binding. type.

General implementation of two-way data binding

First, let’s talk about what front-end two-way data binding is. To put it simply, it is the controller layer of the framework (the controller layer here is a general term, which can be understood as the middleware that controls the view behavior and contacts the model layer) and the UI presentation layer (view layer) to establish a two-way data channel. . When a change occurs in either layer, the other layer will automatically make corresponding changes immediately (or seemingly immediately).

Generally speaking, to achieve this two-way data binding relationship (the association process between the controller layer and the display layer), there are currently three ways on the front end,

1.Dirty Check

2. Observation mechanism

3. Encapsulated property accessor

Dirty Check

We say that the technical implementation of two-way data binding in Angularjs (here specifically refers to the AngularJS 1.x.x version, not the AngularJS 2.x.x version) is dirty checking. The general principle is that Angularjs will maintain a sequence internally to combine all required The monitored attributes are placed in this sequence. When certain events occur (note that this is not scheduled but triggered by certain special events), Angularjs will call the $digest method. The internal logic of this method is to traverse All watchers compare the monitored attributes to see if their attribute values ​​have changed before and after the method is called. If they change, the corresponding handler is called. There are many articles on the Internet that analyze the implementation principles of Angularjs two-way data binding, such as this article, and this article, and so on.

The disadvantage of this method is obvious. Traversing and training watchers is very performance-consuming, especially when the number of monitors on a single page reaches an order of magnitude.

Observation mechanism

The blogger had a reprinted and translated article before. The data binding changes brought about by Object.observe() are about using the Object.observe method in ECMAScript7 to monitor and observe objects (or their properties). Once they are When a change occurs, the corresponding handler will be executed.

This is currently the most perfect way to monitor attribute data changes. The language (browser) supports it natively. There is nothing better than this. The only regret is that the current breadth of support is not enough and needs to be fully promoted.

Encapsulated property accessor

There is a concept of magic method in PHP, such as the __get() and __set() methods in PHP. There is a similar concept in JavaScript, but it is not called a magic method, but an accessor. Let’s take a look at a sample code,

var data = {
name: "erik",
getName: function() {
return this.name;
},
setName: function(name) {
this.name = name;
}
};
Copy after login

从上面的代码中我们可以管中窥豹,比如 data 中的 getName() 和 setName() 方法,我们可以简单的将其看成 data.name 的访问器(或者叫做 存取器 )。

其实,针对上述的代码,更加严格一点的话,不允许直接访问 data.name 属性,所有对 data.name 的读写都必须通过 data.getName() 和 data.setName() 方法。所以,想象一下,一旦某个属性不允许对其进行直接读写,而必须是通过访问器进行读写时,那么我当然通过重写属性的访问器方法来做一些额外的情,比如属性值变更监控。使用属性访问器来做数据双向绑定的原理就是在此。

这种方法当然也有弊端,最突出的就是每添加一个属性监控,都必须为这个属性添加对应访问器方法,否则这个属性的变更就无法捕获。

Object.defineProperty 方法

国产mvvm框架avalon.js实现数据双向绑定的原理就是属性访问器。不过它当然不会像上述示例代码一样原始。它使用了ECMAScript5.1(ECMA-262)中定义的标准属性 Object.defineProperty 方法。针对国内行情,部分还不支持 Object.defineProperty 低级浏览器采用VBScript作了完美兼容,不像其他的mvvm框架已经逐渐放弃对低端浏览器的支持。

我们先来MDN上对 Object.defineProperty 方法的定义,

The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object.

意义很明确, Object.defineProperty 方法提供了一种直接的方式来定义对象属性或者修改已有对象属性。其方法原型如下,

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

其中,

obj ,待修改的对象
prop ,带修改的属性名称
descriptor ,待修改属性的相关描述
descriptor 要求传入一个对象,其默认值如下,

/**
* @{param} descriptor
*/
{
configurable: false,
enumerable: false,
writable: false,
value: null,
set: undefined,
get: undefined
}
Copy after login

configurable ,属性是否可配置。可配置的含义包括:是否可以删除属性( delete ),是否可以修改属性的 writable 、 enumerable 、 configurable 属性。

enumerable ,属性是否可枚举。可枚举的含义包括:是否可以通过 for...in 遍历到,是否可以通过 Object.keys() 方法获取属性名称。

writable ,属性是否可重写。可重写的含义包括:是否可以对属性进行重新赋值。

value ,属性的默认值。

set ,属性的重写器(暂且这么叫)。一旦属性被重新赋值,此方法被自动调用。

get ,属性的读取器(暂且这么叫)。一旦属性被访问读取,此方法被自动调用。

下面来一段示例代码,

var o = {};
Object.defineProperty(o, 'name', {
value: 'erik'
});
console.log(Object.getOwnPropertyDescriptor(o, 'name')); // Object {value: "erik", writable: false, enumerable: false, configurable: false}
Object.defineProperty(o, 'age', {
value: 26,
configurable: true,
writable: true
});
console.log(o.age); // 26
o.age = 18;
console.log(o.age); // 18. 因为age属性是可重写的
console.log(Object.keys(o)); // []. name和age属性都不是可枚举的
Object.defineProperty(o, 'sex', {
value: 'male',
writable: false
});
o.sex = 'female'; // 这里的赋值其实是不起作用的
console.log(o.sex); // 'male';
delete o.sex; // false, 属性删除的动作也是无效的
Copy after login

经过上述的示例,正常情况下 Object.definePropert() 的使用都是比较简单的。

不过还是有一点需要额外注意一下, Object.defineProperty() 方法设置属性时,属性不能同时声明访问器属性( set 和 get )和 writable 或者 value 属性。 意思就是,某个属性设置了 writable 或者 value 属性,那么这个属性就不能声明 get 和 set 了,反之亦然。

因为 Object.defineProperty() 在声明一个属性时,不允许同一个属性出现两种以上存取访问控制。

示例代码,

var o = {},
myName = 'erik';
Object.defineProperty(o, 'name', {
value: myName,
set: function(name) {
myName = name;
},
get: function() {
return myName;
}
});
Copy after login

上面的代码看起来貌似是没有什么问题,但是真正执行时会报错,报错如下,

TypeError: Invalid property. A property cannot both have accessors and be writable or have a value, #<Object>
Copy after login

因为这里的 name 属性同时声明了 value 特性和 set 及 get 特性,这两者提供了两种对 name 属性的读写控制。这里如果不声明 value 特性,而是声明 writable 特性,结果也是一样的,同样会报错。

Related labels:
source:php.cn
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
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!