Giriş
Bir devrim yaklaşıyor. JavaScript'e, veri bağlama hakkında bildiğiniz her şeyi değiştirecek yeni bir özellik eklendi. Ayrıca, MVC kitaplıklarınızın kaçının düzenlemeler ve güncellemeler için modelleri gözlemleme yaklaşımını da değiştirecek. Mülk gözlemini önemseyen uygulamalarda performansı artıracak bazı güzel özelliklere hazır mısınız?
Tamam. Tamam. Daha fazla uzatmadan Object.observe()
'in Chrome 36 kararlı sürümünde kullanıma sunulduğunu duyurmak isterim. [WOOOO. KİTLE ÇOK HEYECANLANIYOR].
Gelecekteki bir ECMAScript standardının parçası olan Object.observe()
, ayrı bir kitaplığa ihtiyaç duymadan JavaScript nesnelerindeki değişiklikleri eşzamansız olarak gözlemlemek için kullanılan bir yöntemdir. Bir gözlemcinin, gözlemlenen bir nesne grubunda gerçekleşen değişiklikleri açıklayan, zamana göre sıralanmış bir değişiklik kaydı dizisi almasına olanak tanır.
// Let's say we have a model with data
var model = {};
// Which we then observe
Object.observe(model, function(changes){
// This asynchronous callback runs
changes.forEach(function(change) {
// Letting us know what changed
console.log(change.type, change.name, change.oldValue);
});
});
Herhangi bir değişiklik yapıldığında bu değişiklik raporlanır:

Object.observe()
(O.o() veya Oooooooo olarak adlandırmayı tercih ederim) ile bir çerçeveye ihtiyaç duymadan iki yönlü veri bağlamayı uygulayabilirsiniz.
Ancak bu, bir önizleme kullanmamanız gerektiği anlamına gelmez. Karmaşık iş mantığına sahip büyük projeler için fikir sahibi çerçeveler paha biçilmezdir ve bunları kullanmaya devam etmeniz gerekir. Yeni geliştiricilerin oryantasyonunu basitleştirir, daha az kod bakımı gerektirir ve ortak görevlerin nasıl yapılacağına dair kalıplar uygular. Buna ihtiyacınız olmadığında Polymer gibi daha küçük ve daha odaklanmış kitaplıklar (O.o()'dan zaten yararlanan) kullanabilirsiniz.
Bir çerçeveyi veya MV* kitaplığını yoğun olarak kullanıyor olsanız bile O.o(), aynı API'yi korurken daha hızlı ve basit bir uygulamayla bu çerçevelere veya kitaplıklara bazı sağlıklı performans iyileştirmeleri sunma potansiyeline sahiptir. Örneğin, Angular geçen yıl bir modelde değişiklik yapılan bir karşılaştırmada, kirli kontrol işleminin güncelleme başına 40 ms, O.o() işlevinin ise güncelleme başına 1-2 ms (20-40 kat daha hızlı bir gelişme) sürdüğünü tespit etti.
Veri bağlama işlemini karmaşık kodlara gerek kalmadan gerçekleştirmek, artık değişiklikler için anket yapmanız gerekmediği anlamına gelir. Bu da daha uzun pil ömrü demektir.
O.o()'nun avantajlarından yararlanmaya hazırsanız özellik tanıtımına atlayabilir veya çözdüğü sorunlar hakkında daha fazla bilgi edinmek için okumaya devam edebilirsiniz.
Neleri gözlemlemek istiyoruz?
Veri gözleminden bahsederken genellikle belirli türde değişiklikleri takip etmeyi kastediyoruz:
- Ham JavaScript nesnelerinde yapılan değişiklikler
- Mülk eklendiğinde, değiştirildiğinde veya silindiğinde
- Dizilerde öğelerin eklenip çıkarılması
- Nesnenin prototipinde yapılan değişiklikler
Veri bağlamanın önemi
Veri bağlama, model-görüntü kontrolü ayırma konusunda dikkatli olduğunuzda önemli olmaya başlar. HTML, harika bir açıklayıcı mekanizmadır ancak tamamen statiktir. İdeal olarak, verileriniz ile DOM arasındaki ilişkiyi belirtmeniz ve DOM'u güncel tutmanız yeterlidir. Bu, avantaj sağlar ve uygulamanızın dahili durumu ile sunucu arasında DOM'a veri gönderip alan, gerçekten tekrar eden kodlar yazmak için harcayacağınız çok fazla zamandan tasarruf etmenizi sağlar.
Veri bağlama, özellikle veri modellerinizdeki birden fazla mülk ile görünümlerinizdeki birden fazla öğe arasındaki ilişkileri bağlamanız gereken karmaşık bir kullanıcı arayüzünüz olduğunda faydalıdır. Bu durum, günümüzde geliştirdiğimiz tek sayfalık uygulamalarda oldukça yaygındır.
Tarayıcıda verileri doğal olarak gözlemlemenin bir yolunu sunarak JavaScript çerçevelerine (ve yazdığınız küçük yardımcı kitaplıklara) günümüzde dünyanın kullandığı yavaş hilelerden bazılarına güvenmeden model verilerindeki değişiklikleri gözlemleme olanağı tanıyoruz.
Dünyanın bugünkü durumu
Kirli kontrol
Veri bağlamayı daha önce nerede gördünüz? Web uygulamalarınızı oluşturmak için modern bir MV* kitaplığı (ör.Angular, Knockout) kullanıyorsanız muhtemelen model verilerini DOM'a bağlamaya alışkınsınızdır. Verilerimizin ve kullanıcı arayüzünün her zaman senkronize olması için phones
dizisindeki (JavaScript'te tanımlanmıştır) her telefonun değerini bir liste öğesine bağladığımız bir telefon listesi uygulaması örneğini aşağıda bulabilirsiniz:
<html ng-app>
<head>
...
<script src='angular.js'></script>
<script src='controller.js'></script>
</head>
<body ng-controller='PhoneListCtrl'>
<ul>
<li ng-repeat='phone in phones'>
<p></p>
</li>
</ul>
</body>
</html>
ve denetleyicinin JavaScript'i:
var phonecatApp = angular.module('phonecatApp', []);
phonecatApp.controller('PhoneListCtrl', function($scope) {
$scope.phones = [
{'name': 'Nexus S',
'snippet': 'Fast just got faster with Nexus S.'},
{'name': 'Motorola XOOM with Wi-Fi',
'snippet': 'The Next, Next Generation tablet.'},
{'name': 'MOTOROLA XOOM',
'snippet': 'The Next, Next Generation tablet.'}
];
});
Temel model verileri her değiştiğinde DOM'daki listemiz güncellenir. Angular bunu nasıl başarır? Arka planda, kirli kontrol adı verilen bir işlem gerçekleştirir.

Kirli veri kontrolüyle ilgili temel fikir, verilerin değişmiş olabileceği her durumda kitaplığın bir özet veya değişiklik döngüsü aracılığıyla değişip değişmediğini kontrol etmesi gerektiğidir. Angular'da özet döngüsü, değişiklik olup olmadığını görmek için izlenmek üzere kaydedilen tüm ifadeleri tanımlar. Bir modelin önceki değerlerini bilir ve bu değerler değişirse bir değişiklik etkinliği tetiklenir. Geliştiriciler için bu yöntemin en büyük avantajı, kullanımı keyifli ve oldukça iyi bir şekilde derlenen ham JavaScript nesne verilerini kullanabilmeleridir. Bunun dezavantajı, algoritmik davranışının kötü olması ve potansiyel olarak çok pahalı olmasıdır.

Bu işlemin maliyeti, gözlemlenen nesnelerin toplam sayısıyla orantılıdır. Çok fazla kontrol yapmam gerekebilir. Ayrıca, veriler değişmiş olabileceğinde kirli kontrolü tetiklemenin bir yolu da gerekebilir. Çerçevelerin bu amaçla kullandığı birçok akıllıca numara vardır. Bu durumun düzelip düzelmeyeceği henüz net değil.
Web ekosistemi, kendi açıklayıcı mekanizmalarını (ör.
- Kısıtlamaya dayalı model sistemleri
- Otomatik kalıcılık sistemleri (ör.IndexedDB veya localStorage'ta kalıcı değişiklikler)
- Kapsayıcı nesneleri (Ember, Backbone)
Kapsayıcı nesneleri, bir çerçevenin içinde verileri barındıran nesneler oluşturduğu yerdir. Verilere erişen bu öğeler, ayarladığınız veya aldığınız verileri yakalayıp dahili olarak yayınlayabilir. Bu yöntem işe yarar. Nispeten iyi performans gösterir ve iyi bir algoritmik davranışa sahiptir. Ember kullanan kapsayıcı nesnelerinin bir örneğini aşağıda bulabilirsiniz:
// Container objects
MyApp.president = Ember.Object.create({
name: "Barack Obama"
});
MyApp.country = Ember.Object.create({
// ending a property with "Binding" tells Ember to
// create a binding to the presidentName property
presidentNameBinding: "MyApp.president.name"
});
// Later, after Ember has resolved bindings
MyApp.country.get("presidentName");
// "Barack Obama"
// Data from the server needs to be converted
// Composes poorly with existing code
Burada nelerin değiştiğini keşfetme maliyeti, değişen öğelerin sayısıyla orantılıdır. Bir diğer sorun da artık bu farklı nesne türünü kullanmanızdır. Genel olarak, gözlemlenebilir olmaları için sunucudan aldığınız verileri bu nesnelere dönüştürmeniz gerekir.
Çoğu kod ham veriler üzerinde çalışabileceğini varsaydığı için bu, mevcut JS koduyla özellikle iyi bir şekilde derlenmez. Bu özel nesne türleri için geçerli değildir.
Introducing Object.observe()
İdeal olarak, her iki dünyanın da en iyisini istiyoruz. Yani, istersek ham veri nesnelerini (normal JavaScript nesneleri) destekleyen bir veri gözlemleme yöntemi ve her zaman her şeyi kirli kontrol etme ihtiyacı olmadan. Algoritma açısından iyi bir davranışa sahip bir şey. İyi bir şekilde derlenmiş ve platforma yerleştirilmiş bir şey. Object.observe()
'ün sunduğu avantajlardan biri de budur.
Bir nesneyi gözlemlememize, özellikleri değiştirmemize ve değişen öğelerin değişiklik raporunu görmemize olanak tanır. Teoriden yeterince bahsettik. Şimdi bazı kodlara göz atalım.

Object.observe() ve Object.unobserve()
Bir modeli temsil eden basit bir JavaScript nesnesi olduğunu varsayalım:
// A model can be a simple vanilla object
var todoModel = {
label: 'Default',
completed: false
};
Ardından, nesnede her mutasyon (değişiklik) yapıldığında çağrılacak bir geri çağırma işlevi belirtebiliriz:
function observer(changes){
changes.forEach(function(change, i){
console.log('what property changed? ' + change.name);
console.log('how did it change? ' + change.type);
console.log('whats the current value? ' + change.object[change.name]);
console.log(change); // all changes
});
}
Ardından, O.o() işlevini kullanarak bu değişiklikleri gözlemleyebiliriz. İlk bağımsız değişkenimiz olarak nesneyi, ikinci bağımsız değişkenimiz olarak da geri çağırma işlevini iletiyoruz:
Object.observe(todoModel, observer);
Todos model nesnemizde bazı değişiklikler yapmaya başlayalım:
todoModel.label = 'Buy some more milk';
Konsoldaki bilgilere baktığımızda bazı yararlı bilgiler elde ettik. Hangi özelliğin değiştiğini, nasıl değiştiğini ve yeni değerin ne olduğunu biliriz.

Harika! Hoşça kal, kirli kontrol! Mezar taşınıza Comic Sans yazı tipi kullanılarak yazı yazılmalıdır. Başka bir mülkü değiştirelim. Bu sefer completeBy
:
todoModel.completeBy = '01/01/2014';
Gördüğümüz gibi, bir kez daha başarıyla bir değişiklik raporu aldık:

Harika. Şimdi "tamamlandı" özelliğini nesnemizden silmeye karar verelim:
delete todoModel.completed;

Gördüğümüz gibi, döndürülen değişiklikler raporunda silmeyle ilgili bilgiler yer alıyor. Beklenen gibi, mülkün yeni değeri artık "tanımsız". Artık mülklerin ne zaman eklendiğini öğrenebileceğinizi biliyoruz. Silinen mesajlar Temel olarak, bir nesnenin özelliklerinin grubu ("yeni", "silinmiş", "yeniden yapılandırılmış") ve prototipinin değişmesi (proto).
Her gözlem sisteminde olduğu gibi, değişiklikleri dinlemeyi durdurmak için de bir yöntem vardır. Bu durumda, O.o() ile aynı imzaya sahip olan ancak aşağıdaki şekilde çağrılabilen Object.unobserve()
işlevidir:
Object.unobserve(todoModel, observer);
Aşağıda görüldüğü gibi, bu çalıştırıldıktan sonra nesnede yapılan tüm mutasyonlar artık bir değişiklik kaydı listesi döndürmez.

İlgi alanı değişikliklerini belirtme
Bu nedenle, gözlemlenen bir nesneyle ilgili değişikliklerin listesini nasıl alacağınıza dair temel bilgilere göz attık. Bir nesnede yapılan değişikliklerin yalnızca bir alt kümesiyle ilgileniyorsanız ne olur? Herkesin spam filtresine ihtiyacı vardır. Gözlemciler, kabul listesi aracılığıyla yalnızca hakkında bilgi edinmek istedikleri değişiklik türlerini belirtebilir. Bu, O.o() işlevinin üçüncü bağımsız değişkeni kullanılarak aşağıdaki gibi belirtilebilir:
Object.observe(obj, callback, optAcceptList)
Bu özelliğin nasıl kullanılabileceğine dair bir örnek üzerinden gidelim:
// Like earlier, a model can be a simple vanilla object
var todoModel = {
label: 'Default',
completed: false
};
// We then specify a callback for whenever mutations
// are made to the object
function observer(changes){
changes.forEach(function(change, i){
console.log(change);
})
};
// Which we then observe, specifying an array of change
// types we're interested in
Object.observe(todoModel, observer, ['delete']);
// without this third option, the change types provided
// default to intrinsic types
todoModel.label = 'Buy some milk';
// note that no changes were reported
Ancak etiketi silerseniz bu tür değişikliklerin raporlandığını görebilirsiniz:
delete todoModel.label;
O.o() işlevine bir kabul türü listesi belirtmezseniz varsayılan olarak "özel" nesne değişikliği türleri (add
, update
, delete
, reconfigure
, preventExtensions
(bir nesnenin genişletilebilir olmaktan çıkmasının gözlemlenemediği durumlar için)) kullanılır.
Bildirimler
O.o() ayrıca bildirimler kavramıyla da birlikte gelir. Bunlar, telefonunuzda gördüğünüz can sıkıcı bildirimler gibi değildir. Aksine faydalıdır. Bildirimler, Mutasyon Gözlemcilerine benzer. Bu sorular, mikro görevin sonunda sorulur. Tarayıcı bağlamında bu, neredeyse her zaman geçerli etkinlik işleyicinin sonunda olur.
Genellikle bir iş birimi tamamlandığı için gözlemcilerin işlerini yapmasına olanak tanıyan bu zamanlama iyidir. Bu, sıra tabanlı bir işleme modelidir.
Bildirim gönderen kullanmayla ilgili iş akışı aşağıdaki gibidir:

Bir nesnenin özellikleri alındığında veya ayarlandığında özel bildirimler tanımlamak için bildiricilerin pratikte nasıl kullanılabileceğine dair bir örneğe göz atalım. Buradaki yorumları takip edin:
// Define a simple model
var model = {
a: {}
};
// And a separate variable we'll be using for our model's
// getter in just a moment
var _b = 2;
// Define a new property 'b' under 'a' with a custom
// getter and setter
Object.defineProperty(model.a, 'b', {
get: function () {
return _b;
},
set: function (b) {
// Whenever 'b' is set on the model
// notify the world about a specific type
// of change being made. This gives you a huge
// amount of control over notifications
Object.getNotifier(this).notify({
type: 'update',
name: 'b',
oldValue: _b
});
// Let's also log out the value anytime it gets
// set for kicks
console.log('set', b);
_b = b;
}
});
// Set up our observer
function observer(changes) {
changes.forEach(function (change, i) {
console.log(change);
})
}
// Begin observing model.a for changes
Object.observe(model.a, observer);

Burada, veri özelliklerinin değeri değiştiğinde ("güncelleme") raporlama yapılır. Nesnenin uygulamasının raporlamayı seçtiği diğer tüm veriler (notifier.notifyChange()
).
Web platformunda yıllar süren deneyimlerimiz, senkronize yaklaşımın en kolay anlaşıldığı için ilk denemeniz olması gerektiğini bize öğretti. Sorun, temel olarak tehlikeli bir işleme modeli oluşturmasıdır. Bir nesnenin özelliğini güncellemeyi kodunuzda belirtiyorsanız bu nesnenin özelliğinin güncellenmesi, rastgele bir kodun istediği her şeyi yapmasını davet edebilir. Bir işlevin ortasındayken varsayımlarınızın geçersiz kılınması ideal değildir.
Gözlemciyseniz ideal olarak, bir şeyin ortasında olan kullanıcılar tarafından çağrılmayı istemezsiniz. Dünyanın tutarsız bir durumunda çalışma yapmanız istenmesini istemezsiniz. Çok daha fazla hata kontrolü yapmanız gerekir. Çok daha fazla kötü duruma tahammül etmeye çalışıyoruz ve genellikle bu modelle çalışmak zor. Asenkron işleyişle uğraşmak daha zordur ancak sonuçta daha iyi bir modeldir.
Bu sorunun çözümü, sentetik değişiklik kayıtlarıdır.
Sentetik değişiklik kayıtları
Temel olarak, erişim sağlayıcılara veya hesaplanmış özelliklere sahip olmak istiyorsanız bu değerlerin değiştiğini bildirmek sizin sorumluluğunuzdadır. Bu işlem biraz fazladan çalışma gerektirse de bu mekanizmanın birinci sınıf bir özelliği olarak tasarlanmıştır ve bu bildirimler, temel veri nesnelerinden gelen diğer bildirimlerle birlikte gönderilir. Veri özelliklerinden.

Erişim sağlayıcıları ve hesaplanmış özellikleri gözlemleme sorunu, O.o() işlevinin başka bir parçası olan notifier.notify ile çözülebilir. Çoğu gözlem sistemi, türetilmiş değerlerin bir şekilde gözlemlenmesini ister. Bunu yapmanın birçok yolu vardır. O.o, "doğru" yol konusunda herhangi bir yargıda bulunmaz. Hesaplanmış özellikler, dahili (özel) durum değiştiğinde bildiren erişim sağlayıcılar olmalıdır.
Yine de web geliştiricileri, kitaplıkların, hesaplanan mülklere yönelik bildirimleri ve çeşitli yaklaşımları kolaylaştırmaya (ve standart metinleri azaltmaya) yardımcı olmasını beklemelidir.
Bir sonraki örneği (daire sınıfı) oluşturalım. Buradaki fikir, bu daireye ve yarıçap özelliğine sahip olduğumuzdur. Bu durumda yarıçap bir erişim aracısıdır ve değeri değiştiğinde aslında değerin değiştiğini kendisi bildirir. Bu, bu nesne veya başka bir nesneyle ilgili diğer tüm değişikliklerle birlikte yayınlanır. Özetle, sentetik veya hesaplanmış özelliklere sahip olmasını istediğiniz bir nesneyi uyguluyorsanız ya da bunun nasıl çalışacağıyla ilgili bir strateji seçmeniz gerekiyorsa. Bunu yaptığınızda bu, sisteminizin tamamına uygun olur.
Bu işlemin DevTools'da nasıl çalıştığını görmek için kodu atlayın.
function Circle(r) {
var radius = r;
var notifier = Object.getNotifier(this);
function notifyAreaAndRadius(radius) {
notifier.notify({
type: 'update',
name: 'radius',
oldValue: radius
})
notifier.notify({
type: 'update',
name: 'area',
oldValue: Math.pow(radius * Math.PI, 2)
});
}
Object.defineProperty(this, 'radius', {
get: function() {
return radius;
},
set: function(r) {
if (radius === r)
return;
notifyAreaAndRadius(radius);
radius = r;
}
});
Object.defineProperty(this, 'area', {
get: function() {
return Math.pow(radius, 2) * Math.PI;
},
set: function(a) {
r = Math.sqrt(a/Math.PI);
notifyAreaAndRadius(radius);
radius = r;
}
});
}
function observer(changes){
changes.forEach(function(change, i){
console.log(change);
})
}

Erişim özellikleri
Erişim özelliklerine dair kısa bir not. Veri mülklerinde yalnızca değer değişikliklerinin gözlemlenebilir olduğundan daha önce bahsetmiştik. Hesaplanmış özellikler veya erişim işlevleri için geçerli değildir. Bunun nedeni, JavaScript'te erişim sağlayıcıların değerindeki değişiklikler kavramının olmamasıdır. Erişim aracısı, yalnızca bir işlev koleksiyonudur.
Bir erişim sağlayıcıya atarsanız JavaScript yalnızca işlevi orada çağırır ve kendi açısından hiçbir şey değişmez. Yalnızca bazı kodların çalışma fırsatı verildi.
Sorun, anlamsal olarak yukarıdaki -5 değerine atamamıza bakabileceğimizdir. Burada ne olduğunu öğrenmemiz gerekiyor. Bu aslında çözülemez bir sorundur. Örnekte bunun nedeni gösterilmektedir. Bu, rastgele bir kod olabileceğinden hiçbir sistemin bunun ne anlama geldiğini bilmesi mümkün değildir. Bu durumda istediği her şeyi yapabilir. Değere her erişildiğinde değer güncellendiğinden, değerin değişip değişmediğini sormak pek anlamlı değildir.
Tek bir geri çağırma işleviyle birden fazla nesneyi gözlemleme
O.o() ile kullanılabilecek başka bir kalıp da tek bir geri çağırma gözlemcisi kavramıdır. Bu sayede, tek bir geri çağırma işlevi birçok farklı nesne için "gözlemci" olarak kullanılabilir. Geri çağırma işlevine, "mikro görevin sonunda" gözlemlediği tüm nesnelerdeki değişikliklerin tamamı gönderilir (Mutasyon Gözlemcileriyle olan benzerliğe dikkat edin).

Büyük ölçekli değişiklikler
Çok büyük bir uygulama üzerinde çalışıyor ve düzenli olarak büyük ölçekli değişiklikler yapmanız gerekiyor olabilir. Nesneler, çok sayıda mülkü etkileyecek daha büyük anlamsal değişiklikleri daha kompakt bir şekilde açıklamak isteyebilir (tonlarca mülk değişikliği yayınlamak yerine).
O.o(), daha önce tanıttığımız iki özel yardımcı program şeklinde bu konuda yardımcı olur: notifier.performChange()
ve notifier.notify()
.

Bu konuyu, bazı matematik yardımcı programları (çarpma, artırma, artırmaVeÇarpma) içeren bir Thingy nesnesi tanımladığımızda büyük ölçekli değişikliklerin nasıl açıklanabileceğine dair bir örnekle inceleyelim. Bir yardımcı program her kullanıldığında sisteme, bir çalışma koleksiyonunun belirli bir değişiklik türü içerdiğini bildirir.
Örneğin: notifier.performChange('foo', performFooChangeFn);
function Thingy(a, b, c) {
this.a = a;
this.b = b;
}
Thingy.MULTIPLY = 'multiply';
Thingy.INCREMENT = 'increment';
Thingy.INCREMENT_AND_MULTIPLY = 'incrementAndMultiply';
Thingy.prototype = {
increment: function(amount) {
var notifier = Object.getNotifier(this);
// Tell the system that a collection of work comprises
// a given changeType. e.g
// notifier.performChange('foo', performFooChangeFn);
// notifier.notify('foo', 'fooChangeRecord');
notifier.performChange(Thingy.INCREMENT, function() {
this.a += amount;
this.b += amount;
}, this);
notifier.notify({
object: this,
type: Thingy.INCREMENT,
incremented: amount
});
},
multiply: function(amount) {
var notifier = Object.getNotifier(this);
notifier.performChange(Thingy.MULTIPLY, function() {
this.a *= amount;
this.b *= amount;
}, this);
notifier.notify({
object: this,
type: Thingy.MULTIPLY,
multiplied: amount
});
},
incrementAndMultiply: function(incAmount, multAmount) {
var notifier = Object.getNotifier(this);
notifier.performChange(Thingy.INCREMENT_AND_MULTIPLY, function() {
this.increment(incAmount);
this.multiply(multAmount);
}, this);
notifier.notify({
object: this,
type: Thingy.INCREMENT_AND_MULTIPLY,
incremented: incAmount,
multiplied: multAmount
});
}
}
Ardından, nesnemiz için iki gözlemci tanımlarız: Biri değişiklikler için her şeyi kapsayan, diğeri ise yalnızca tanımladığımız belirli kabul türleri (Thingy.INCREMENT, Thingy.MULTIPLY, Thingy.INCREMENT_AND_MULTIPLY) hakkında rapor gönderen.
var observer, observer2 = {
records: undefined,
callbackCount: 0,
reset: function() {
this.records = undefined;
this.callbackCount = 0;
},
};
observer.callback = function(r) {
console.log(r);
observer.records = r;
observer.callbackCount++;
};
observer2.callback = function(r){
console.log('Observer 2', r);
}
Thingy.observe = function(thingy, callback) {
// Object.observe(obj, callback, optAcceptList)
Object.observe(thingy, callback, [Thingy.INCREMENT,
Thingy.MULTIPLY,
Thingy.INCREMENT_AND_MULTIPLY,
'update']);
}
Thingy.unobserve = function(thingy, callback) {
Object.unobserve(thingy);
}
Artık bu kodla oynamaya başlayabiliriz. Yeni bir şey tanımlayalım:
var thingy = new Thingy(2, 4);
İzleyin ve ardından bazı değişiklikler yapın. Çok eğlenceli. ÇOK fazla şey var.
// Observe thingy
Object.observe(thingy, observer.callback);
Thingy.observe(thingy, observer2.callback);
// Play with the methods thingy exposes
thingy.increment(3); // { a: 5, b: 7 }
thingy.b++; // { a: 5, b: 8 }
thingy.multiply(2); // { a: 10, b: 16 }
thingy.a++; // { a: 11, b: 16 }
thingy.incrementAndMultiply(2, 2); // { a: 26, b: 36 }

"perform function" içindeki her şey "big-change" işleminin sonucu olarak kabul edilir. "big-change"i kabul eden gözlemciler yalnızca "big-change" kaydını alır. Bu özelliği etkinleştirmeyen gözlemciler, "işlevi gerçekleştirme" işleminin yaptığı çalışmadan kaynaklanan temel değişiklikleri alır.
Dizileri gözlemleme
Nesnelerdeki değişiklikleri gözlemlemekten uzun süredir bahsediyoruz. Peki diziler ne olacak? Harika bir soru. Birisi bana "Mükemmel bir soru" dediğinde Böylesine harika bir soru sorduğum için kendimi tebrik etmekle meşgul olduğumdan yanıtlarını hiç duymam. Ama konuyu dağıtıyorum. Dizilerle çalışmayla ilgili yeni yöntemlerimiz de var.
Array.observe()
, kendisinde yapılan büyük çaplı değişiklikleri (ör. ekleme, kaydırma veya uzunluğunu dolaylı olarak değiştiren herhangi bir işlem) "ekleme" değişiklik kaydı olarak işleyen bir yöntemdir. Dahili olarak notifier.performChange("splice",...)
kullanılır.
Aşağıda, bir model "dizisini" gözlemlediğimiz ve temel verilerde değişiklik olduğunda benzer şekilde değişikliklerin listesini aldığımız bir örnek verilmiştir:
var model = ['Buy some milk', 'Learn to code', 'Wear some plaid'];
var count = 0;
Array.observe(model, function(changeRecords) {
count++;
console.log('Array observe', changeRecords, count);
});
model[0] = 'Teach Paul Lewis to code';
model[1] = 'Channel your inner Paul Irish';

Performans
O.o() işlevinin hesaplama performansı üzerindeki etkisini okuma önbelleği olarak düşünebilirsiniz. Genel olarak, önbelleğe aşağıdaki durumlarda (önem sırasına göre) başvurabilirsiniz:
- Okuma sıklığı, yazma sıklığına hakimdir.
- Yazma sırasındaki sabit miktarda çalışmayı, okuma sırasında algoritma açısından daha iyi performansla takas eden bir önbellek oluşturabilirsiniz.
- Yazma işlemlerinin sürekli olarak yavaşlaması kabul edilebilir.
O.o(), 1) gibi kullanım alanları için tasarlanmıştır.
Kirli veri kontrolü, gözlemlediğiniz tüm verilerin bir kopyasının tutulmasını gerektirir. Bu, kirli kontrol için yapısal bir bellek maliyetine katlanacağınız anlamına gelir. Bu maliyeti O.o() ile ödemezsiniz. Kirli kontrol, iyi bir ara çözüm olsa da temelde uygulamaların gereksiz yere karmaşıklaşmasına neden olabilecek bir soyutlama eksikliğidir.
Neden? Verilerin değişebileceği her durumda kirli veri kontrolü çalıştırılmalıdır. Bunu yapmanın çok sağlam bir yolu yoktur ve bu konudaki her yaklaşımın önemli dezavantajları vardır (ör.bir anket aralığını kontrol etmek, görsel yapıların ve kod sorunları arasındaki yarış koşullarının riskini artırır). Kirli kontrol, gözlemcilerin küresel bir sicil kaydını da gerektirir. Bu da bellek sızıntısı tehlikeleri ve O.o()'nun önlediği yıkım maliyetleri oluşturur.
Bazı rakamlara göz atalım.
Aşağıdaki karşılaştırma testleri (GitHub'da mevcuttur), kirli kontrol ile O.o() işlevini karşılaştırmamıza olanak tanır. Bu testler, Gözlemlenen-Nesne-Seti-Boyutu ve Mutasyon-Sayısı grafikleri olarak yapılandırılmıştır. Genel sonuç, kirli kontrol performansının algoritmik olarak gözlemlenen nesnelerin sayısına, O.o() performansının ise yapılan mutasyonların sayısına orantılı olmasıdır.
Kirli kontrol

Object.observe() etkinleştirilmiş Chrome

Object.observe() işlevini doldurma
O.o() işlevi Chrome 36'da kullanılabiliyor. Peki diğer tarayıcılarda kullanılabilir mi? Sizin için kısa bir hatırlatma hazırladık. Polymer'ın Observe-JS, O.o() için bir polyfill'dir. Mevcutsa yerel uygulamayı kullanır, aksi takdirde polyfill yapar ve üzerine bazı yararlı süslemeler ekler. Dünyanın toplu bir görünümünü sunarak değişiklikleri özetler ve nelerin değiştiğini raporlar. Bu API'nin sunduğu iki güçlü özellik şunlardır:
- Yolları gözlemleyebilirsiniz. Yani belirli bir nesnenin "foo.bar.baz" değerini gözlemlemek istediğinizi söyleyebilirsiniz. Bu durumda, söz konusu yoldaki değer ne zaman değiştiyse size bildirilir. Yola ulaşılamazsa değer tanımsız olarak kabul edilir.
Belirli bir nesnenin yolundaki bir değeri gözlemleme örneği:
var obj = { foo: { bar: 'baz' } };
var observer = new PathObserver(obj, 'foo.bar');
observer.open(function(newValue, oldValue) {
// respond to obj.foo.bar having changed value.
});
- Dizi birleştirme hakkında bilgi edinebilirsiniz. Dizi birleştirme işlemleri, temel olarak bir dizinin eski sürümünü yeni sürümüne dönüştürmek için bir diziyle yapmanız gereken minimum birleştirme işlemleri grubudur. Bu, bir tür dönüştürme işlemi veya dizinin farklı bir görünümüdür. Eski durumdan yeni duruma geçmek için yapmanız gereken minimum çalışma miktarıdır.
Bir dizideki değişiklikleri minimum birleştirme grubu olarak raporlama örneği:
var arr = [0, 1, 2, 4];
var observer = new ArrayObserver(arr);
observer.open(function(splices) {
// respond to changes to the elements of arr.
splices.forEach(function(splice) {
splice.index; // index position that the change occurred.
splice.removed; // an array of values representing the sequence of elements which were removed
splice.addedCount; // the number of elements which were inserted.
});
});
Çerçeveler ve Object.observe()
Daha önce de belirtildiği gibi, O.o(), çerçevelere ve kitaplıklara, özelliği destekleyen tarayıcılarda veri bağlama performanslarını iyileştirmek için büyük bir fırsat sunar.
Ember'den Yehuda Katz ve Erik Bryn, O.o() işlevine destek eklemenin Ember'in yakın vadeli yol haritasında olduğunu doğruladı. Angular'ın Misko Hervy, Angular 2.0'ın iyileştirilmiş değişiklik algılama özelliğiyle ilgili bir tasarım dokümanı yazdı. Uzun vadeli yaklaşımları, Chrome'un kararlı sürümüne eklendiğinde Object.observe() işlevinden yararlanmak olacak. O zamana kadar kendi değişiklik algılama yaklaşımları olan Watchtower.js'i tercih edecekler. Çok heyecan verici.
Sonuçlar
O.o(), web platformuna eklenen ve hemen kullanabileceğiniz güçlü bir işlevdir.
Zamanla bu özelliğin daha fazla tarayıcıda kullanıma sunulmasını ve JavaScript çerçevelerinin yerel nesne gözlemleme özelliklerine erişerek performans artışı elde etmesini umuyoruz. Chrome'u hedefleyenler, Chrome 36 (ve sonraki sürümler) ile O.o() işlevini kullanabilir. Bu özellik, gelecekteki bir Opera sürümünde de kullanıma sunulacaktır.
Bu nedenle, JavaScript çerçevelerinin yazarlarıyla Object.observe()
ve uygulamalarınızdaki veri bağlama performansını iyileştirmek için bu özelliği nasıl kullanmayı planladıkları hakkında konuşun. Önümüzdeki günlerde heyecan verici gelişmeler olacak.
Kaynaklar
- Harmony wiki'sinde Object.observe()>
- Rick Waldron tarafından yazılan "Object.observe() ile veri bağlama"
- Object.observe() hakkında bilmek istediğiniz her şey - JSConf
- Object.observe(), neden en iyi ES7 özelliğidir?
Yorumları ve katkıları için Rafael Weinstein, Jake Archibald, Eric Bidelman, Paul Kinlan ve Vivian Cromwell'a teşekkür ederiz.