>  기사  >  웹 프론트엔드  >  JS 변수의 변화를 모니터링하는 방법의 예

JS 변수의 변화를 모니터링하는 방법의 예

小云云
小云云원래의
2018-03-06 14:24:472225검색

이제 js에서 특정 변수의 변경 사항을 모니터링해야 합니다. 변수가 변경되면 일부 이벤트가 트리거됩니다. timeinterval 및 기타 예약된 모니터링 방법을 사용할 수 없습니다.

인기 있는 MVVM JS 라이브러리/프레임워크의 공통 기능은 데이터 바인딩으로, 이는 관련 계산을 자동으로 수행하고 데이터 변경 후 반응 방식으로 DOM 표시를 변경합니다. 따라서 이 질문은 MVVM 라이브러리/프레임워크의 데이터 바인딩을 구현하는 방법으로도 이해될 수 있습니다.

일반적인 데이터 바인딩 구현에는 더티 값 감지, ES5 기반 getter 및 setter, ES에서 버려진 Object.observe 및 ES6에 추가된 프록시가 포함됩니다.

더티 값 감지

Angular는 더티 값 감지를 사용합니다. 원칙은 새 값을 이전 값과 비교한 다음 값이 실제로 변경되면 DOM을 변경하는 것이므로 Angular에는 $digest가 있습니다. 그렇다면 ng-click과 같은 내장 명령이 트리거된 후 자동으로 변경되는 이유는 무엇입니까? 원리도 매우 간단합니다. 마지막에 ng-click과 같은 내장 명령에 $digest가 추가됩니다.

더티 값 감지의 쉬운 구현:

<!DOCTYPE html><html>
    <head>
        <meta charset="utf-8" />
        <title>two-way binding</title>
    </head>
    <body onload="init()">
        <button ng-click="inc">
            Increase        </button>
        <button ng-click="reset">
            Reset        </button>
        <span style="color:red" ng-bind="counter"></span>
        <span style="color:blue" ng-bind="counter"></span>
        <span style="color:green" ng-bind="counter"></span>
        <script type="text/javascript">
            /* 数据模型区开始 */
            var counter = 0;            function inc() {
                counter++;
            }            function reset() {
                counter = 0;
            }            /* 数据模型区结束 */
            /* 绑定关系区开始 */
            function init() {
                bind();
            }            function bind() {                var list = document.querySelectorAll("[ng-click]");                for (var i=0; i<list.length; i++) {
                    list[i].onclick = (function(index) {                        return function() {                            window[list[index].getAttribute("ng-click")]();
                            apply();
                        };
                    })(i);
                }
            }            function apply() {                var list = document.querySelectorAll("[ng-bind=&#39;counter&#39;]");                for (var i=0; i<list.length; i++) {                    if (list[i].innerHTML != counter) {
                        list[i].innerHTML = counter;
                    }
                }
            }            /* 绑定关系区结束 */
        </script>
    </body></html>

이것의 단점은 데이터를 변경한 후 DOM을 자동으로 변경할 수 없다는 것입니다. apply()를 트리거하는 방법을 찾아야 하므로 ng-만 사용할 수 있습니다. 클릭 패키징 ng-click에는 실제 클릭 이벤트 모니터링이 포함되어 있으며 DOM 업데이트 여부를 결정하는 더티 값 감지 기능이 추가되어 있습니다.

또 다른 단점은 조심하지 않으면 각 더티 값 감지에서 많은 양의 데이터가 감지되며 감지에 많은 데이터가 필요하지 않아 성능에 쉽게 영향을 미칠 수 있다는 것입니다.

Angular와 유사한 더티 값 감지를 구현하는 방법에 관해서는 원리를 알고 난 후에도 최적화 방법뿐만 아니라 수행해야 할 작업이 많이 있습니다. 관심이 있다면 Migong Shu가 추천한 "Build Your Own Angular.js"를 살펴보세요. 첫 번째 장인 Scope에서는 각도의 범위와 더티 값 감지를 구현하는 방법에 대해 설명합니다. 그런데 위의 예시도 이주노동자 아저씨 블로그를 살짝 수정한 내용이니, 맨 마지막에 링크가 있으니 참고자료를 읽어보시길 권합니다.

ES5 getters and setters

ES5에는 새로운 Object.defineProperty가 추가되어 객체에 대한 새 속성을 직접 정의하거나 기존 속성을 수정하고 객체를 반환할 수 있습니다.

Object.defineProperty(obj, prop, descriptor)

받아들이는 세 번째 매개변수는 get과 set일 수 있으며, 각각은 getter와 setter 메서드에 해당합니다.

var a = { zhihu:0 };Object.defineProperty(a, &#39;zhihu&#39;, {  get: function() {    console.log(&#39;get:&#39; + zhihu);    return zhihu;
  },  set: function(value) {
    zhihu = value;    console.log(&#39;set:&#39; + zhihu);
  }
});
a.zhihu = 2; // set:2console.log(a.zhihu); // get:2
                      // 2

ES5 기반의 getter와 setter는 요구 사항을 거의 완벽하게 충족한다고 할 수 있습니다. 왜 거의 말합니까?

먼저 IE8 이하 버전의 IE는 사용할 수 없으며, 이 기능은 폴리필이 없어 지원되지 않는 플랫폼에서는 구현할 수 없습니다. 이것이 바로 ES5 getter 및 setter 기반 Vue.js가 IE8 이하 버전을 지원하지 않는 이유입니다. IE 버전. 아마도 누군가가 Avalon을 언급할 것입니다. Avalon은 낮은 버전의 IE에서 유사한 기능을 달성하기 위해 vbscript의 일부 마법을 사용합니다.

또 다른 문제는 배열의 길이를 수정하는 것, items[0] = {}와 같이 인덱스별로 요소를 직접 설정하는 것, 배열의 push와 같은 변이 방법이 setter를 트리거할 수 없다는 점입니다. 이 문제를 해결하려면 Vue의 관찰자/array.js에서 Vue가 배열의 프로토타입 메소드를 직접 수정하는 방법을 참조할 수 있습니다.

const arrayProto = Array.prototypeexport const arrayMethods = Object.create(arrayProto)/**
 * Intercept mutating methods and emit events
 */;[  &#39;push&#39;,  &#39;pop&#39;,  &#39;shift&#39;,  &#39;unshift&#39;,  &#39;splice&#39;,  &#39;sort&#39;,  &#39;reverse&#39;]
.forEach(function (method) {  // cache original method
  var original = arrayProto[method]
  def(arrayMethods, method, function mutator () {    // avoid leaking arguments:
    // http://jsperf.com/closure-with-arguments
    var i = arguments.length    var args = new Array(i)    while (i--) {
      args[i] = arguments[i]
    }    var result = original.apply(this, args)    var ob = this.__ob__    var inserted    switch (method) {      case &#39;push&#39;:
        inserted = args        break
      case &#39;unshift&#39;:
        inserted = args        break
      case &#39;splice&#39;:
        inserted = args.slice(2)        break
    }    if (inserted) ob.observeArray(inserted)    // notify change
    ob.dep.notify()    return result
  })
});

프로토타입 메소드는 이런 방식으로 다시 작성되며 여전히 가능합니다. 배열 돌연변이 메서드를 실행한 후 트리거됩니다.

그러나 이것은 여전히 ​​배열의 길이를 수정하고 인덱스를 직접 사용하여 items[0] = {}와 같은 요소를 설정하는 문제를 해결할 수 없습니다. 문제를 해결하려면 여전히 Vue의 접근 방식을 참조할 수 있습니다. 전자의 경우 이전 문제를 새 배열로 직접 바꿀 수 있습니다. 후자의 경우 배열에 대한 $set 메서드를 확장하고 수정이 수행된 후 뷰 업데이트를 트리거할 수 있습니다.

Deprecated Object.observe

Object.observe는 ES7 초안에 있었고 제안서에서는 stage2로 진행되었으나 결국 폐기되었습니다. 다음은 MDN의 예입니다.

// 一个数据模型var user = {  id: 0,  name: &#39;Brendan Eich&#39;,  title: &#39;Mr.&#39;};// 创建用户的greetingfunction updateGreeting() {
  user.greeting = &#39;Hello, &#39; + user.title + &#39; &#39; + user.name + &#39;!&#39;;
}
updateGreeting();Object.observe(user, function(changes) {
  changes.forEach(function(change) {    // 当name或title属性改变时, 更新greeting
    if (change.name === &#39;name&#39; || change.name === &#39;title&#39;) {
      updateGreeting();
    }
  });
});

이 기능은 Chrome에서 한때 지원했지만 지원도 중단되었습니다. 관심이 있으시면 검색해 보세요. 이전 기사 이것은 한 번 유망한 기능입니다(Object.observe()에 의해 발생하는 데이터 바인딩 변경). 물론 Polymer/observe-js에 대한 몇 가지 대안이 있습니다.

ES6에서 가져온 Proxy

는 이름에서 알 수 있듯이 HTTP의 프록시와 유사합니다.

var p = new Proxy(target, handler);

target은 대상 개체이며 배열, 함수 또는 다른 개체와 같은 모든 유형의 개체일 수 있습니다. 프록시 객체. 핸들러는 생성된 프록시 개체의 다양한 동작을 제어하기 위한 프록시 메서드 집합이 포함된 프로세서 개체입니다.

예:

let a = new Proxy({}, {  set: function(obj, prop, value) {
    obj[prop] = value;    if (prop === &#39;zhihu&#39;) {      console.log("set " + prop + ": " + obj[prop]);
    }    return true;
  }
});
a.zhihu = 100;

물론 프록시의 기능은 이보다 훨씬 뛰어나며 프록시 전달 등을 구현할 수도 있습니다.

그러나 현재 Firefox 18만이 브라우저에서 이 기능을 지원하며 babel은 공식적으로 이 기능을 지원하지 않는다고 명시하고 있습니다.

Unsupported feature
Due to the limitations of ES5, Proxies cannot be transpiled or polyfilled.

현재 이를 구현할 수 있는 babel 플러그인이 있지만 구현이 더 복잡하다고 말했습니다. Node인 경우 최신 버전으로 업그레이드 후 사용하시면 됩니다. 위 예시의 테스트 환경은 Node v6.4.0입니다.

관련 권장사항:

js 변수 승격에 대한 자세한 설명

JS 변수 및 해당 범위 지식 포인트 소개

js 변수 승격 및 함수 선언 사전 구문 분석 예제에 대한 자세한 설명

위 내용은 JS 변수의 변화를 모니터링하는 방법의 예의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.