이제 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='counter']"); 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에는 새로운 Object.defineProperty가 추가되어 객체에 대한 새 속성을 직접 정의하거나 기존 속성을 수정하고 객체를 반환할 수 있습니다.
Object.defineProperty(obj, prop, descriptor)
받아들이는 세 번째 매개변수는 get과 set일 수 있으며, 각각은 getter와 setter 메서드에 해당합니다.
var a = { zhihu:0 };Object.defineProperty(a, 'zhihu', { get: function() { console.log('get:' + zhihu); return zhihu; }, set: function(value) { zhihu = value; console.log('set:' + 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 */;[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] .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 'push': inserted = args break case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify() return result }) });
프로토타입 메소드는 이런 방식으로 다시 작성되며 여전히 가능합니다. 배열 돌연변이 메서드를 실행한 후 트리거됩니다.
그러나 이것은 여전히 배열의 길이를 수정하고 인덱스를 직접 사용하여 items[0] = {}와 같은 요소를 설정하는 문제를 해결할 수 없습니다. 문제를 해결하려면 여전히 Vue의 접근 방식을 참조할 수 있습니다. 전자의 경우 이전 문제를 새 배열로 직접 바꿀 수 있습니다. 후자의 경우 배열에 대한 $set 메서드를 확장하고 수정이 수행된 후 뷰 업데이트를 트리거할 수 있습니다.
Object.observe는 ES7 초안에 있었고 제안서에서는 stage2로 진행되었으나 결국 폐기되었습니다. 다음은 MDN의 예입니다.
// 一个数据模型var user = { id: 0, name: 'Brendan Eich', title: 'Mr.'};// 创建用户的greetingfunction updateGreeting() { user.greeting = 'Hello, ' + user.title + ' ' + user.name + '!'; } updateGreeting();Object.observe(user, function(changes) { changes.forEach(function(change) { // 当name或title属性改变时, 更新greeting if (change.name === 'name' || change.name === 'title') { updateGreeting(); } }); });
이 기능은 Chrome에서 한때 지원했지만 지원도 중단되었습니다. 관심이 있으시면 검색해 보세요. 이전 기사 이것은 한 번 유망한 기능입니다(Object.observe()에 의해 발생하는 데이터 바인딩 변경). 물론 Polymer/observe-js에 대한 몇 가지 대안이 있습니다.
는 이름에서 알 수 있듯이 HTTP의 프록시와 유사합니다.
var p = new Proxy(target, handler);
target은 대상 개체이며 배열, 함수 또는 다른 개체와 같은 모든 유형의 개체일 수 있습니다. 프록시 객체. 핸들러는 생성된 프록시 개체의 다양한 동작을 제어하기 위한 프록시 메서드 집합이 포함된 프로세서 개체입니다.
예:
let a = new Proxy({}, { set: function(obj, prop, value) { obj[prop] = value; if (prop === 'zhihu') { 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 변수의 변화를 모니터링하는 방법의 예의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!