ǰÑÔ
JavaScriptµÄ¶ÔÏó£¬ÊÇÒ»×é¼üÖµ¶ÔµÄ¼¯ºÏ£¬¿ÉÒÔÓµÓÐÈÎÒâÊýÁ¿µÄΨһ¼ü£¬¼ü¿ÉÒÔÊÇ×Ö·û´®£¨String£©ÀàÐÍ»ò±ê¼Ç£¨Symbol£¬ES6ÐÂÔöµÄ»ù±¾Êý¾ÝÀàÐÍ£©ÀàÐÍ£¬Ã¿¸ö¼ü¶ÔÓ¦Ò»¸öÖµ£¬Öµ¿ÉÒÔÊÇÈÎÒâÀàÐ͵ÄÈÎÒâÖµ¡£¶ÔÓÚ¶ÔÏóÄÚµÄÊôÐÔ£¬JavaScriptÌṩÁËÒ»¸öÊôÐÔÃèÊöÆ÷½Ó¿ÚPropertyDescriptor£¬´ó²¿·Ö¿ª·¢Õß²¢²»ÐèÒªÖ±½ÓʹÓÃËü£¬µ«ÊǺܶà¿ò¼ÜºÍÀà¿âÄÚ²¿ÊµÏÖʹÓÃÁËËü£¬Èçavalon.js£¬Vue.js£¬±¾Æª½éÉÜÊôÐÔÃèÊöÆ÷¼°Ïà¹ØÓ¦Óá£
¶¨Òå¶ÔÏóÊôÐÔ
ÔÚ½éÉܶÔÏóÊôÐÔÃèÊö֮ǰ£¬ÏȽéÉÜÒ»ÏÂÈçºÎ¶¨Òå¶ÔÏóÊôÐÔ¡£×î³£Óõķ½Ê½¾ÍÊÇʹÓÃÈçÏ·½Ê½£º
var a = {
name: 'jh'
};
// or
var b = {};
b.name = 'jh';
// or
var c = {};
var key = 'name';
c[key] = 'jh'; |
±¾ÎÄʹÓÃ×ÖÃæÁ¿·½Ê½´´½¨¶ÔÏ󣬵«ÊÇJavaScript»¹ÌṩÆäËû·½Ê½£¬È磬new Object()£¬Object.create()£¬Á˽â¸ü¶àÇë²é¿´¶ÔÏó³õʼ»¯¡£
Object.defineProperty()
ÉÏÃæÍ¨³£Ê¹Óõķ½Ê½²»ÄÜʵÏÖ¶ÔÊôÐÔÃèÊöÆ÷µÄ²Ù×÷£¬ÎÒÃÇÐèҪʹÓÃdefineProperty()·½·¨£¬¸Ã·½·¨ÎªÒ»¸ö¶ÔÏó¶¨ÒåÐÂÊôÐÔ»òÐÞ¸ÄÒ»¸öÒѶ¨ÒåÊôÐÔ£¬½ÓÊÜÈý¸ö²ÎÊýObject.defineProperty(obj,
prop, descriptor)£¬·µ»ØÖµÎª²Ù×÷ºóµÄ¶ÔÏó£º
1.obj, ´ý²Ù×÷¶ÔÏó
2.ÊôÐÔÃû
3.²Ù×÷ÊôÐÔµÄÊôÐÔÃèÊö¶ÔÏó
var x = {};
Object.defineProperty(x, 'count', {});
console.log(x); // Object {count: undefined} |
ÓÉÓÚ´«ÈëÒ»¸ö¿ÕµÄÊôÐÔÃèÊö¶ÔÏó£¬ËùÒÔÊä³ö¶ÔÏóÊôÐÔֵΪundefined£¬µ±Ê¹ÓÃdefineProperty()·½·¨²Ù×÷ÊôÐÔʱ£¬ÃèÊö¶ÔÏóĬÈÏֵΪ£º
1.value: undefined
2.set: undefined
3.get: undefined
4.writable: false
5.enumerable: false,
6.configurable: false
²»Ê¹Óø÷½·¨¶¨ÒåÊôÐÔ£¬ÔòÊôÐÔĬÈÏÃèÊöΪ£º
1.value: undefined
2.set: undefined
3.get: undefined
4.writable: true
5.enumerable: true,
6.configurable: true
ĬÈÏÖµ¾ù¿É±»Ã÷È·²ÎÊýÖµÉèÖø²¸Ç¡£
µ±È»»¹Ö§³ÖÅúÁ¿¶¨Òå¶ÔÏóÊôÐÔ¼°ÃèÊö¶ÔÏó£¬Ê¹Óá°Object.defineProperties()`·½·¨£¬È磺
var x = {};
Object.defineProperties(x, {
count: {
value: 0
},
name: {
value: 'jh'
}
});
console.log(x); // Object {count: 0, name: 'jh'} |
¶ÁÈ¡ÊôÐÔÃèÊö¶ÔÏó
JavaScriptÖ§³ÖÎÒÃǶÁȡij¶ÔÏóÊôÐÔµÄÃèÊö¶ÔÏó£¬Ê¹Óà Object
. getOwnPropertyDescriptor ( obj , prop )·½·¨
var x = {
name: 'jh'
};
Object.defineProperty(x, 'count', {});
Object.getOwnPropertyDescriptor(x, 'count');
Object.getOwnPropertyDescriptor(x, 'name');
// Object {value: undefined, writable:
false, enumerable: false, configurable: false}
// Object {value: "jh", writable:
true,
enumerable: true, configurable: true} |
¸ÃʵÀýÒ²Ó¡Ö¤ÁËÉÏÃæ½éÉܵÄÒÔ²»Í¬·½Ê½¶¨ÒåÊôÐÔʱ£¬ÆäĬÈÏÊôÐÔÃèÊö¶ÔÏóÊDz»Í¬µÄ¡£
ÊôÐÔÃèÊö¶ÔÏó
PropertyDescriptor APIÌṩÁËÁù´óʵÀýÊôÐÔÒÔÃèÊö¶ÔÏóÊôÐÔ£¬°üÀ¨£º
configurable , enumerable , get , set , value , writable
.
value
Ö¸¶¨¶ÔÏóÊôÐÔÖµ£º
var x = {};
Object.defineProperty(x, 'count', {
value: 0
});
console.log(x); // Object {count: 0}
|
writable
Ö¸¶¨¶ÔÏóÊôÐÔÊÇ·ñ¿É±ä£º
var x = {};
Object.defineProperty(x, 'count', {
value: 0
});
console.log(x); // Object {count: 0}
x.count = 1; // ¾²Ä¬Ê§°Ü£¬²»»á±¨´í
console.log(x); // Object {count: 0}
|
ʹÓÃdefineProperty()·½·¨Ê±£¬Ä¬ÈÏÓÐwritable: false£¬ ÐèÒªÏÔʾÉèÖÃwritable:
true¡£
´æÈ¡Æ÷º¯Êý(getter/setter)
¶ÔÏóÊôÐÔ¿ÉÒÔÉèÖôæÈ¡Æ÷º¯Êý£¬Ê¹ÓÃgetÉùÃ÷´æÈ¡Æ÷getterº¯Êý£¬setÉùÃ÷´æÈ¡Æ÷setterº¯Êý£»Èô´æÔÚ´æÈ¡Æ÷º¯Êý£¬ÔòÔÚ·ÃÎÊ»òÉèÖøÃÊôÐÔʱ£¬½«µ÷ÓöÔÓ¦µÄ´æÈ¡Æ÷º¯Êý:
GET
¶ÁÈ¡¸ÃÊôÐÔֵʱµ÷Óøú¯Êý²¢½«¸Ãº¯Êý·µ»ØÖµ¸³Öµ¸øÊôÐÔÖµ£»
var x = {};
Object.defineProperty(x, 'count', {
get: function() {
console.log('¶ÁÈ¡countÊôÐÔ +1');
return 0;
}
});
console.log(x); // Object {count: 0}
x.count = 1;
// '¶ÁÈ¡countÊôÐÔ +1'
console.log(x.count); // 0
|
SET
µ±ÉèÖú¯Êýֵʱµ÷Óøú¯Êý£¬¸Ãº¯Êý½ÓÊÕÉèÖõÄÊôÐÔÖµ×÷²ÎÊý£º
var x = {};
Object.defineProperty(x, 'count', {
set: function(val) {
this.count = val;
}
});
console.log(x);
x.count = 1;
|
Ö´ÐÐÉÏËß´úÂ룬»á·¢ÏÖ±¨´í£¬Ö´ÐÐÕ»Òç³ö:

ÉÏÊö´úÂëÔÚÉèÖÃcountÊôÐÔʱ£¬»áµ÷ÓÃset·½·¨£¬¶øÔڸ÷½·¨ÄÚΪcountÊôÐÔ¸³Öµ»áÔٴδ¥·¢set·½·¨£¬ËùÒÔÕâÑùÊÇÐв»Í¨µÄ£¬JavaScriptʹÓÃÁíÒ»ÖÖ·½Ê½£¬Í¨³£´æÈ¡Æ÷º¯ÊýµÃͬʱÉùÃ÷£¬´úÂëÈçÏ£º
var x = {};
Object.defineProperty(x, 'count', {
get: function() {
return this._count;
},
set: function(val) {
console.log('ÉèÖÃcountÊôÐÔ +1');
this._count = val;
}
});
console.log(x); // Object {count: undefined}
x.count = 1;
// 'ÉèÖÃcountÊôÐÔ +1'
console.log(x.count); 1
|
ÊÂʵÉÏ£¬ÔÚʹÓÃdefineProperty()·½·¨ÉèÖÃÊôÐÔʱ£¬Í¨³£ÐèÒªÔÚ¶ÔÏóÄÚ²¿Î¬»¤Ò»¸öÐÂÄÚ²¿±äÁ¿£¨ÒÔÏ»®Ïß_¿ªÍ·£¬±íʾ²»Ï£Íû±»Íⲿ·ÃÎÊ£©£¬×÷Ϊ´æÈ¡Æ÷º¯ÊýµÄÖн顣
×¢£ºµ±ÉèÖÃÁË´æÈ¡Æ÷ÃèÊöʱ£¬²»ÄÜÉèÖÃvalueºÍwritableÃèÊö¡£
ÎÒÃÇ·¢ÏÖ£¬ÉèÖÃÊôÐÔ´æÈ¡Æ÷º¯Êýºó£¬ÎÒÃÇ¿ÉÒÔʵÏÖ¶Ô¸ÃÊôÐÔµÄʵʱ¼à¿Ø£¬ÕâÔÚʵ¼ùÖкÜÓÐÓÃÎäÖ®µØ£¬ºóÎÄ»áÓ¡Ö¤ÕâÒ»µã¡£
enumerable
Ö¸¶¨¶ÔÏóÄÚijÊôÐÔÊÇ·ñ¿Éö¾Ù£¬¼´Ê¹ÓÃfor in²Ù×÷ÊÇ·ñ¿É±éÀú£º
var x = {
name: 'jh'
};
Object.defineProperty(x, 'count', {
value: 0
});
for (var key in x) {
console.log(key + ' is ' + x[key]);
}
// name is jh
|
ÉÏÃæÎÞ·¨±éÀúcountÊôÐÔ£¬ÒòΪʹÓÃdefineProperty()·½·¨Ê±£¬Ä¬ÈÏÓÐenumerable:
false£¬ÐèÒªÏÔʾÉùÃ÷¸ÃÃèÊö£º
var x = {
name: 'jh'
};
Object.defineProperty(x, 'count', {
value: 0,
enumerable: true
});
for (var key in x) {
console.log(key + ' is ' + x[key]);
}
// name is jh
// count is 0
x.propertyIsEnumerable('count'); // true |
configurable
¸ÃÖµÖ¸¶¨¶ÔÏóÊôÐÔÃèÊöÊÇ·ñ¿É±ä£º
var x = {};
Object.defineProperty(x, 'count', {
value: 0,
writable: false
});
Object.defineProperty(x, 'count', {
value: 0,
writable: true
});
|
Ö´ÐÐÉÏÊö´úÂë»á±¨´í£¬ÒòΪʹÓÃdefineProperty()·½·¨Ê±Ä¬ÈÏÊÇ
configurable : false £¬Êä³öÈçͼ£º

ÐÞ¸ÄÈçÏ£¬¼´¿É£º
var x = {};
Object.defineProperty(x, 'count', {
value: 0,
writable: false,
configurable: true
});
x.count = 1;
console.log(x.count); // 0
Object.defineProperty(x, 'count', {
writable: true
});
x.count = 1;
console.log(x.count); // 1
|
ÊôÐÔÃèÊöÓëÊÓͼģÐͰó¶¨
½éÉÜÍêÊôÐÔÃèÊö¶ÔÏó£¬ÎÒÃÇÀ´¿´¿´ÆäÔÚÏÖ´úJavaScript¿ò¼ÜºÍÀà¿âÉϵÄÓ¦Óá£Ä¿Ç°Óкܶà¿ò¼ÜºÍÀà¿âʵÏÖÊý¾ÝºÍDOMÊÓͼµÄµ¥ÏòÉõÖÁË«Ïò°ó¶¨£¬ÈçReact£¬angular.js£¬avalon.js,£¬Vue.jsµÈ£¬Ê¹ÓÃËüÃǺÜÈÝÒ××öµ½¶ÔÊý¾Ý±ä¸ü½øÐÐÏìӦʽ¸üÐÂDOMÊÓͼ£¬ÉõÖÁÊÓͼºÍÄ£ÐÍ¿ÉÒÔʵÏÖË«Ïò°ó¶¨£¬Í¬²½¸üС£µ±È»ÕâЩ¿ò¼Ü¡¢Àà¿âÄÚ²¿ÊµÏÖÔÀíÖ÷Òª·ÖΪÈý´óÕóÓª¡£±¾ÎÄÒÔVue.jsΪÀý£¬Vue.jsÊǵ±Ï±ȽÏÁ÷ÐеÄÒ»¸öÏìӦʽµÄÊÓͼ²ãÀà¿â£¬ÆäÄÚ²¿ÊµÏÖÏìӦʽÔÀí¾ÍÊDZ¾ÎĽéÉܵÄÊôÐÔÃèÊöÔÚ¼¼ÊõÖеľßÌåÓ¦Óá£
¿ÉÒÔµã»÷´Ë´¦£¬²é¿´Ò»¸öÔÉúJavaScriptʵÏֵļòÒ×Êý¾ÝÊÓͼµ¥Ïò°ó¶¨ÊµÀý£¬ÔÚ¸ÃʵÀýÖУ¬µã»÷°´Å¥¿ÉÒÔʵÏÖ¼ÆÊý×ÔÔö£¬ÔÚÊäÈë¿òÊäÈëÄÚÈÝ»áͬ²½¸üе½Õ¹Ê¾DOM£¬ÉõÖÁÔÚ¿ØÖÆÌ¨¸Ä±ädata¶ÔÏóÊôÐÔÖµ£¬DOM»áÏìÓ¦¸üУ¬Èçͼ£º

Êý¾ÝÊÓͼµ¥Ïò°ó¶¨
ÏÖÓÐÈçÏ´úÂ룺
var data = {};
var contentEl = document.querySelector('.content');
Object.defineProperty(data, 'text', {
writable: true,
configurable: true,
enumerable: true,
get: function() {
return contentEl.innerHTML;
},
set: function(val) {
contentEl.innerHTML = val;
}
});
|
ºÜÈÝÒ׿´³ö£¬µ±ÎÒÃÇÉèÖÃdata¶ÔÏóµÄtextÊôÐÔʱ£¬»á½«¸ÃÖµÉèÖÃΪÊÓͼDOMÔªËØµÄÄÚÈÝ£¬¶ø·ÃÎʸÃÊôÐÔֵʱ£¬·µ»ØµÄÊÇÊÓͼDOMÔªËØµÄÄÚÈÝ£¬Õâ¾Í¼òµ¥µÄʵÏÖÁËÊý¾Ýµ½ÊÓͼµÄµ¥Ïò°ó¶¨£¬¼´Êý¾Ý±ä¸ü£¬ÊÓͼҲ»á¸üС£
ÒÔÉϽöÊÇÕë¶ÔÒ»¸öÔªËØµÄÊý¾ÝÊÓͼ°ó¶¨£¬µ«ÉÔ΢ÓоÑéµÄ¿ª·¢Õß±ã¿ÉÒÔ¸ù¾ÝÒÔÉÏ˼·£¬½øÐзâ×°£¬ºÜÈÝÒ×µÄʵÏÖÒ»¸ö¼òÒ×µÄÊý¾Ýµ½ÊÓͼµ¥Ïò°ó¶¨µÄ¹¤¾ßÀà¡£
³éÏó·â×°
½ÓÏÂÀ´¶ÔÒÔÉÏʵÀý½øÐмòµ¥³éÏó·â×°£¬µã»÷²é¿´ÍêÕûʵÀý´úÂë¡£
Ê×ÏÈÉùÃ÷Êý¾Ý½á¹¹£º
window.data = {
title: 'Êý¾ÝÊÓͼµ¥Ïò°ó¶¨',
content: 'ʹÓÃÊôÐÔÃèÊöÆ÷ʵÏÖÊý¾ÝÊÓͼ°ó¶¨',
count: 0
};
var attr = 'data-on'; // Ô¼¶¨ºÃµÄÓï·¨£¬
ÉùÃ÷DOM°ó¶¨¶ÔÏóÊôÐÔ |
È»ºó·â×°º¯ÊýÅúÁ¿´¦Àí¶ÔÏ󣬱éÀú¶ÔÏóÊôÐÔ£¬ÉèÖÃÃèÊö¶ÔÏóͬʱΪÊôÐÔ×¢²á±ä¸üʱµÄ»Øµ÷£º
// Ϊ¶ÔÏóÖÐÿһ¸öÊôÐÔÉèÖÃÃèÊö¶ÔÏó£¬ÓÈÆäÊÇ´æÈ¡Æ÷º¯Êý
function defineDescriptors(obj) {
for (var key in obj) {
// ±éÀúÊôÐÔ
defineDescriptor(obj, key, obj[key]);
}
// ÎªÌØ¶¨ÊôÐÔÉèÖÃÃèÊö¶ÔÏó
function defineDescriptor(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
var value = val;
return value;
},
set: function(newVal) {
if (newVal !== val) {
// Öµ·¢Éú±ä¸ü²ÅÖ´ÐÐ
val = newVal;
Observer.emit(key, newVal); // ´¥·¢¸üÐÂDOM
}
}
});
Observer.subscribe(key); // Ϊ¸ÃÊôÐÔ×¢²á»Øµ÷
}
} |
¹ÜÀíʼþ
ÒÔ·¢²¼¶©ÔÄģʽ¹ÜÀíÊôÐÔ±ä¸üʼþ¼°»Øµ÷£º
// ʹÓ÷¢²¼/¶©ÔÄģʽ£¬¼¯ÖйÜÀí¼à¿ØºÍ´¥·¢»Øµ÷ʼþ
var Observer = {
watchers: {},
subscribe: function(key) {
var el = document.querySelector('[' +
attr + '="'+
key + '"]');
// demo
var cb = function react(val) {
el.innerHTML = val;
}
if (this.watchers[key]) {
this.watchers[key].push(cb);
} else {
this.watchers[key] = [].concat(cb);
}
},
emit: function(key, val) {
var len = this.watchers[key] && this.
watchers[key].length;
if (len && len > 0) {
for(var i = 0; i < len; i++) {
this.watchers[key][i](val);
}
}
}
}; |
³õʼ»¯ÊµÀý
×îºó³õʼ»¯ÊµÀý£º
// ³õʼ»¯demo
function init() {
defineDescriptors(data); // ´¦ÀíÊý¾Ý¶ÔÏó
var eles = document.querySelectorAll('[' + attr
+ ']');
// ³õʼ±éÀúDOMչʾÊý¾Ý
// Æäʵ¿ÉÒÔ½«¸Ã²Ù×÷·Åµ½ÊôÐÔÃèÊö¶ÔÏóµÄget·½·¨ÄÚ
£¬ÔòÔÚ³õʼ»¯Ê±Ö»ÐèÒª¶ÔÊôÐÔ±éÀú·ÃÎʼ´¿É
for (var i = 0, len = eles.length; i < len;
i++) {
eles[i].innerHTML = data[eles[i].getAttribute(attr)];
}
// ¸¨Öú²âÊÔʵÀý
document.querySelector('.add').addEventListener
('click', function(e) {
data.count += 1;
});
}
init(); |
html´úÂë²Î¿¼ÈçÏ£º
<h2 class="title" data-on="title"></h2>
<div class="content" data-on="content"></div>
<div class="count" data-on="count"></div>
<div>
ÇëÊäÈëÄÚÈÝ£º
<input type="text" class="content-input"
placeholder="ÇëÊäÈëÄÚÈÝ">
</div>
<button class="add" onclick="">¼Ó1</button> |
Vue.jsµÄÏìӦʽÔÀí
ÉÏÒ»½ÚʵÏÖÁËÒ»¸ö¼òµ¥µÄÊý¾ÝÊÓͼµ¥Ïò°ó¶¨ÊµÀý£¬ÏÖÔÚ¶ÔVue.jsµÄÏìӦʽµ¥Ïò°ó¶¨½øÐмòÒª·ÖÎö£¬Ö÷ÒªÐèÒªÀí½âÆäÈçºÎ×·×ÙÊý¾Ý±ä¸ü¡£
ÒÀÀµ×·×Ù
Vue.jsÖ§³ÖÎÒÃÇͨ¹ýdata²ÎÊý´«µÝÒ»¸öJavaScript¶ÔÏó×öΪ×é¼þÊý¾Ý£¬È»ºóVue.js½«±éÀú´Ë¶ÔÏóÊôÐÔ£¬Ê¹ÓÃObject.defineProperty·½·¨ÉèÖÃÃèÊö¶ÔÏó£¬Í¨¹ý´æÈ¡Æ÷º¯Êý¿ÉÒÔ×·×Ù¸ÃÊôÐԵıä¸ü£¬±¾ÖÊÔÀíºÍÉÏÒ»½ÚʵÀý²î²»¶à£¬µ«ÊDz»Í¬µÄÊÇ£¬Vue.js´´½¨ÁËÒ»²ãWatcher²ã£¬ÔÚ×é¼þäÖȾµÄ¹ý³ÌÖаÑÊôÐԼǼΪÒÀÀµ£¬Ö®ºóµ±ÒÀÀµÏîµÄsetter±»µ÷ÓÃʱ£¬»á֪ͨWatcherÖØÐ¼ÆË㣬´Ó¶øÊ¹Ëü¹ØÁªµÄ×é¼þµÃÒÔ¸üÐÂ,ÈçÏÂͼ£º

Vue.jsÏìӦʽÔÀíͼ
×é¼þ¹ÒÔØÊ±£¬ÊµÀý»¯watcherʵÀý£¬²¢°Ñ¸ÃʵÀý´«µÝ¸øÒÀÀµ¹ÜÀíÀ࣬×é¼þäÖȾʱ£¬Ê¹ÓöÔÏó¹Û²ì½Ó¿Ú±éÀú´«ÈëµÄdata¶ÔÏó£¬ÎªÃ¿¸öÊôÐÔ´´½¨Ò»¸öÒÀÀµ¹ÜÀíʵÀý²¢ÉèÖÃÊôÐÔÃèÊö¶ÔÏó£¬ÔÚ´æÈ¡Æ÷º¯Êýgetº¯ÊýÖУ¬ÒÀÀµ¹ÜÀíʵÀýÌí¼Ó£¨¼Ç¼£©¸ÃÊôÐÔΪһ¸öÒÀÀµ£¬È»ºóµ±¸ÃÒÀÀµ±ä¸üʱ£¬´¥·¢setº¯Êý£¬Ôڸú¯ÊýÄÚ֪ͨÒÀÀµ¹ÜÀíʵÀý£¬ÒÀÀµ¹ÜÀíʵÀý·Ö·¢¸Ã±ä¸ü¸øÆäÄÚ´æ´¢µÄËùÓÐwatcherʵÀý£¬watcherʵÀýÖØÐ¼ÆË㣬¸üÐÂ×é¼þ¡£
Òò´Ë¿ÉÒÔ×ܽá˵Vue.jsµÄÏìӦʽÔÀíÊÇÒÀÀµ×·×Ù£¬Í¨¹ýÒ»¸ö¹Û²ì¶ÔÏó£¬ÎªÃ¿¸öÊôÐÔ£¬ÉèÖôæÈ¡Æ÷º¯Êý²¢×¢²áÒ»¸öÒÀÀµ¹ÜÀíʵÀýdep£¬depÄÚΪÿ¸ö×é¼þʵÀýά»¤Ò»¸öwatcherʵÀý£¬ÔÚÊôÐÔ±ä¸üʱ£¬Í¨¹ýsetter֪ͨdepʵÀý,depʵÀý·Ö·¢¸Ã±ä¸ü¸øÃ¿Ò»¸öwatcherʵÀý£¬watcherʵÀý¸÷×Ô¼ÆËã¸üÐÂ×é¼þʵÀý£¬¼´watcher×·×ÙdepÌí¼ÓµÄÒÀÀµ£¬Object.defineProperty()·½·¨ÌṩÕâÖÖ×·×ٵļ¼ÊõÖ§³Ö£¬depʵÀýά»¤ÕâÖÖ×·×Ù¹ØÏµ¡£
Ô´Âë¼òµ¥·ÖÎö
½ÓÏÂÀ´¶ÔVue.jsÔ´Âë½øÐмòµ¥·ÖÎö£¬´Ó¶ÔJavaScript¶ÔÏóºÍÊôÐԵĴ¦Àí¿ªÊ¼£º
¹Û²ì¶ÔÏó£¨OBSERVER£©
Ê×ÏÈ£¬Vue.jsÒ²ÌṩÁËÒ»¸ö³éÏó½Ó¿Ú¹Û²ì¶ÔÏó£¬Îª¶ÔÏóÊôÐÔÉèÖô洢Æ÷º¯Êý£¬ÊÕ¼¯ÊôÐÔÒÀÀµÈ»ºó·Ö·¢ÒÀÀµ¸üУº
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep(); // ¹ÜÀí¶ÔÏóÒÀÀµ
this.vmCount = 0;
def(value, '__ob__', this); // »º´æ´¦ÀíµÄ¶ÔÏó£¬
±ê¼Ç¸Ã¶ÔÏóÒÑ´¦Àí
if (Array.isArray(value)) {
var augment = hasProto
? protoAugment
: copyAugment;
augment(value, arrayMethods, arrayKeys);
this.observeArray(value);
} else {
this.walk(value);
}
}; |
ÉÏÃæ´úÂë¹Ø×¢Á½¸ö½Úµã£¬ this . observeArray(value)
ºÍ this . walk (value) ;£º
ÈôΪ¶ÔÏó£¬Ôòµ÷ÓÃwalk()·½·¨£¬±éÀú¸Ã¶ÔÏóÊôÐÔ£¬½«ÊôÐÔת»»ÎªÏìӦʽ£º
Observer.prototype.walk = function walk (obj)
{
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i], obj[keys[i]]);
}
};
|
¿ÉÒÔ¿´µ½£¬×îÖÕÉèÖÃÊôÐÔÃèÊö¶ÔÏóÊÇͨ¹ýµ÷Óà defineReactive$$1()
·½·¨¡£
ÈôvalueΪ¶ÔÏóÊý×飬ÔòÐèÒª¶îÍâ´¦Àí£¬µ÷Óà observeArray()
·½·¨¶Ôÿһ¸ö¶ÔÏó¾ù²úÉúÒ»¸ö Observer ʵÀý£¬±éÀú¼àÌý¸Ã¶ÔÏóÊôÐÔ£º
Observer.prototype.observeArray =
function observeArray
(items) {
for (var i = 0, l = items.length; i < l;
i++) {
observe(items[i]);
}
}; |
ºËÐÄÊÇΪÿ¸öÊý×éÏîµ÷ÓÃobserveº¯Êý£º
function observe(value, asRootData) {
if (!isObject(value)) {
return // Ö»ÐèÒª´¦Àí¶ÔÏó
}
var ob;
if (hasOwn(value, '__ob__') && value.__
ob__ instanceof
Observer) {
ob = value.__ob__; // ´¦Àí¹ýµÄÔòÖ±½Ó¶ÁÈ¡»º´æ
} else if (
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value))
&&
Object.isExtensible(value) &&
!value._isVue) {
ob = new Observer(value); // ´¦Àí¸Ã¶ÔÏó
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob
} |
µ÷ÓÃob = new Observer(value) ;ºó¾Í»Øµ½µÚÒ»ÖÖÇé¿öµÄ½á¹û£ºµ÷ÓÃd
efineReactive$$1() ·½·¨Éú³ÉÏìӦʽÊôÐÔ¡£
Éú³ÉÏìӦʽÊôÐÔ
Ô´ÂëÈçÏ£º
function
defineReactive$$1 (obj,key,
val,customSetter)
{
var dep = new Dep(); // ¹ÜÀíÊôÐÔÒÀÀµ
var property = Object.getOwnProperty
Descriptor(obj, key);
if (property && property.configurable
=== false) {
return
}
// ֮ǰÒѾÉèÖÃÁ˵Äget/setÐèÒªºÏ²¢µ÷ÓÃ
var getter = property && property.get;
var setter = property && property.set;
var childOb = observe(val); // ÊôÐÔÖµÒ²¿ÉÄÜ
ÊǶÔÏó£¬ÐèÒªµÝ¹é¹Û²ì´¦Àí
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) { // ¹ÜÀíÒÀÀµ¶ÔÏó´æÔÚÖ¸ÏòµÄ
watcherʵÀý
dep.depend(); // Ìí¼ÓÒÀÀµ£¨¼Ç¼£©
if (childOb) { // ÊôÐÔֵΪ¶ÔÏó
childOb.dep.depend(); // ÊôÐÔÖµ¶ÔÏóÒ²ÐèÒª
Ìí¼ÓÒÀÀµ
}
if (Array.isArray(value)) {
dependArray(value); // ´¦ÀíÊý×é
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal
&& value !== value)) {
return // δ·¢Éú±ä¸ü²»ÐèÒªÍùºóÖ´ÐÐ
}
/* eslint-enable no-self-compare */
if ("development" !== 'production'
&&
customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal); // ¸üÐÂÊôÐÔÖµ
} else {
val = newVal; // ¸üÐÂÊôÐÔÖµ
}
childOb = observe(newVal); // ÿ´ÎÖµ±ä¸üʱÐèÒª
ÖØÐ¹۲죬ÒòΪ¿ÉÄÜֵΪ¶ÔÏó
dep.notify(); // ·¢²¼¸üÐÂʼþ
}
});
} |
¸Ã·½·¨Ê¹Óà Object . defineProperty() ·½·¨ÉèÖÃÊôÐÔÃèÊö¶ÔÏó£¬Âß¼¼¯ÖÐÔÚÊôÐÔ´æÈ¡Æ÷º¯ÊýÄÚ£º
1.get: ·µ»ØÊôÐÔÖµ£¬Èç¹ûwatcher´æÔÚ£¬ÔòµÝ¹é¼Ç¼ÒÀÀµ£»
2.set: ÊôÐÔÖµ·¢Éú±ä¸üʱ£¬¸üÐÂÊôÐÔÖµ£¬²¢µ÷ÓÃdep.notify()·½·¨·¢²¼¸üÐÂʼþ£»
¹ÜÀíÒÀÀµ
Vue.jsÐèÒª¹ÜÀí¶ÔÏóµÄÒÀÀµ£¬ÔÚÊôÐÔ¸üÐÂʱ֪ͨwatcher¸üÐÂ×é¼þ£¬½ø¶ø¸üÐÂÊÓͼ£¬Vue.js¹ÜÀíÒÀÀµ½Ó¿Ú²ÉÓ÷¢²¼¶©ÔÄģʽʵÏÖ£¬Ô´ÂëÈçÏ£º
var uid$1 = 0;
var Dep = function Dep () {
this.id = uid$1++; // ÒÀÀµ¹ÜÀíʵÀýid
this.subs = []; // ¶©ÔĸÃÒÀÀµ¹ÜÀíʵÀýµÄwatcherʵÀýÊý×é
};
Dep.prototype.depend = function depend () {
// Ìí¼ÓÒÀÀµ
if (Dep.target) {
Dep.target.addDep(this); // µ÷ÓÃwatcherʵÀý·½·¨¶©ÔÄ´ËÒÀÀµ¹ÜÀíʵÀý
}
};
Dep.target = null; // watcherʵÀý
var targetStack = []; // ά»¤watcherʵÀýÕ»
function pushTarget (_target) {
if (Dep.target) { targetStack.push(Dep.target);
}
Dep.target = _target; // ³õʼ»¯DepÖ¸ÏòµÄwatcherʵÀý
}
function popTarget () {
Dep.target = targetStack.pop();
} |
¶©ÔÄ
Èç֮ǰ£¬Éú³ÉÏìӦʽÊôÐÔΪÊôÐÔÉèÖôæÈ¡Æ÷º¯Êýʱ£¬getº¯ÊýÄÚµ÷Óà dep
. depend () ·½·¨Ìí¼ÓÒÀÀµ£¬¸Ã·½·¨ÄÚµ÷Óà Dep . target . addDep (this)
;£¬¼´µ÷ÓÃÖ¸ÏòµÄ watcher ʵÀýµÄ addDep ·½·¨£¬¶©ÔÄ´ËÒÀÀµ¹ÜÀíʵÀý£º
Watcher.prototype.addDep = function addDep (dep)
{
var id = dep.id;
if (!this.newDepIds.has(id)) { // ÊÇ·ñÒѶ©ÔÄ
this.newDepIds.add(id); // watcher
ʵÀýά»¤µÄÒÀÀµ¹ÜÀíʵÀýid¼¯ºÏ
this.newDeps.push(dep); // watcher
ʵÀýά»¤µÄÒÀÀµ¹ÜÀíʵÀýÊý×é
if (!this.depIds.has(id)) { // watcher
ʵÀýά»¤µÄÒÀÀµ¹ÜÀíʵÀýid¼¯ºÏ
// µ÷Óô«µÝ¹ýÀ´µÄÒÀÀµ¹ÜÀíʵÀý·½·¨£¬
Ìí¼Ó´ËwatcherʵÀýΪ¶©ÔÄÕß
dep.addSub(this);
}
}
}; |
watcherʵÀý¿ÉÄÜͬʱ׷×Ù¶à¸öÊôÐÔ£¨¼´¶©ÔĶà¸öÒÀÀµ¹ÜÀíʵÀý£©£¬ËùÒÔÐèҪά»¤Ò»¸öÊý×飬´æ´¢¶à¸ö¶©ÔĵÄÒÀÀµ¹ÜÀíʵÀý£¬Í¬Ê±¼Ç¼ÿһ¸öʵÀýµÄid£¬±ãÓÚÅжÏÊÇ·ñÒѶ©ÔÄ£¬¶øºóµ÷ÓÃÒÀÀµ¹ÜÀíʵÀýµÄ
addSub ·½·¨£º
Dep.prototype.addSub = function addSub (sub)
{
this.subs.push(sub); // ʵÏÖwatcherµ½ÒÀÀµ¹ÜÀíʵÀýµÄ¶©ÔĹØÏµ
};
|
¸Ã·½·¨Ö»ÊǼòµ¥µÄÔÚ¶©ÔÄÊý×éÄÚÌí¼ÓÒ»¸ö¶©ÔĸÃÒÀÀµ¹ÜÀíʵÀýµÄ watcher
ʵÀý¡£
·¢²¼
ÊôÐÔ±ä¸üʱ£¬ÔÚÊôÐԵĴæÈ¡Æ÷setº¯ÊýÄÚµ÷ÓÃÁË dep.notify()
·½·¨£¬·¢²¼´ËÊôÐÔ±ä¸ü£º
Dep.prototype.notify = function notify () {
// ¸´Öƶ©ÔÄÕßÊý×é
var subs = this.subs.slice();
for (var i = 0, l = subs.length; i < l; i++)
{
subs[i].update(); // ·Ö·¢±ä¸ü
}
}; |
´¥·¢¸üÐÂ
Ç°ÃæÌáµ½£¬Vue.jsÖÐÓÉwatcher²ã×·×ÙÒÀÀµ±ä¸ü£¬·¢Éú±ä¸üʱ£¬Í¨Öª×é¼þ¸üУº
Watcher.prototype.update = function update ()
{
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true;
} else if (this.sync) { // ͬ²½
this.run();
} else { // Òì²½
queueWatcher(this); // ×îºóÒ²Êǵ÷ÓÃrun()·½·¨
}
};
|
µ÷ÓÃrun·½·¨£¬Í¨Öª×é¼þ¸üУº
Watcher.prototype.run = function run () {
if (this.active) {
var value = this.get(); // »ñÈ¡ÐÂÊôÐÔÖµ
if (value !== this.value || // ÈôÖµ
isObject(value) || this.deep) {
var oldValue = this.value; // »º´æ¾ÉÖµ
this.value = value; // ÉèÖÃÐÂÖµ
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, ("callback for
watcher \"" + (this.expression) +
"\""));
}
} else {
this.cb.call(this.vm, value, oldValue);
}
}
}
}; |
µ÷ÓÃthis.get()·½·¨£¬Êµ¼ÊÉÏ£¬ºóÃæ»á¿´µ½Ôڸ÷½·¨ÄÚ´¦ÀíÁËÊôÐÔÖµµÄ¸üÐÂÓë×é¼þµÄ¸üУ¬ÕâÀïÅжϵ±ÊôÐÔ±ä¸üʱµ÷Óóõʼ»¯Ê±´«¸øÊµÀýµÄcb»Øµ÷º¯Êý£¬²¢Çһص÷º¯Êý½ÓÊÜÊôÐÔоÉÖµÁ½¸ö²ÎÊý£¬´Ë»Øµ÷ͨ³£ÊǶÔÓÚwatchÉùÃ÷µÄ¼àÌýÊôÐÔ²Å»á´æÔÚ£¬·ñÔòĬÈÏΪ¿Õº¯Êý¡£
×·×ÙÒÀÀµ½Ó¿ÚʵÀý»¯
ÿһ¸öÏìӦʽÊôÐÔ¶¼ÊÇÓÉÒ»¸öWatcherʵÀý×·×ÙÆä±ä¸ü£¬¶øÕë¶Ô²»Í¬ÊôÐÔ£¨
data , computed , watch £©£¬Vue.js½øÐÐÁËһЩ²îÒì´¦Àí£¬ÈçÏÂÊǽӿÚÖ÷ÒªÂß¼£º
var Watcher = function Watcher (vm,expOrFn,cb,options)
{
this.cb = cb;
...
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
} else {
this.getter = parsePath(expOrFn);
}
this.value = this.lazy
? undefined
: this.get();
}; |
ÔÚ³õʼ»¯WatcherʵÀýʱ£¬»á½âÎöexpOrFn²ÎÊý£¨±í´ïʽ»òÕߺ¯Êý£©³ÉÍØÕ¹
getterthis.getter £¬È»ºóµ÷ÓÃthis.get£¨£©·½·¨£¬·µ»ØÖµ×÷Ϊthis.valueÖµ£º
Watcher.prototype.get = function get () {
pushTarget(this); // ÈëÕ»watcherʵÀý
var value;
var vm = this.vm;
if (this.user) {
try {
value = this.getter.call(vm, vm); // ͨ¹ýthis.getter»ñÈ¡ÐÂÖµ
} catch (e) {
handleError(e, vm, ("getter for watcher
\"" +
(this.expression) + "\""));
}
} else {
value = this.getter.call(vm, vm); // ͨ¹ýthis.getter»ñÈ¡ÐÂÖµ
}
if (this.deep) { // Éî¶ÈµÝ¹é±éÀú¶ÔÏó×·×ÙÒÀÀµ
traverse(value);
}
popTarget(); // ³öÕ»watcherʵÀý
this.cleanupDeps(); // Çå¿Õ»º´æÒÀÀµ
return value // ·µ»ØÐÂÖµ
}; |
ÕâÀïÐèҪעÒâµÄÊǶÔÓÚdataÊôÐÔ£¬¶ø·ÇcomputedÊôÐÔ»òwatchÊôÐÔ£¬¶øÑÔ£¬ÆäwatcherʵÀýµÄthis.getterͨ³£¾ÍÊÇupdateComponentº¯Êý£¬¼´äÖȾ¸üÐÂ×é¼þ£¬get·½·¨·µ»Øundefined£¬¶ø¶ÔÓÚcomputed¼ÆËãÊôÐÔ¶øÑÔ£¬»á´«Èë¶ÔÓ¦Ö¸¶¨º¯Êý¸øthis.getter£¬Æä·µ»ØÖµ¾ÍÊÇ´Ëget·½·¨·µ»ØÖµ¡£
dataÆÕͨÊôÐÔ
Vue.jsdataÊôÐÔÊÇÒ»¸ö¶ÔÏó£¬ÐèÒªµ÷ÓöÔÏó¹Û²ì½Ó¿Ú new
Observer (value) £º
function observe (value, asRootData) {
if (!isObject(value)) {
return
}
var ob;
ob = new Observer(value); // ¶ÔÏó¹Û²ìʵÀý
return ob;
}
// ³õʼ´¦ÀídataÊôÐÔ
function initData (vm) {
// µ÷ÓÃobserveº¯Êý
observe(data, true /* asRootData */);
} |
¼ÆËãÊôÐÔ
Vue.js¶Ô¼ÆËãÊôÐÔ´¦ÀíÊÇÓвîÒìµÄ£¬ËüÊÇÒ»¸ö±äÁ¿£¬¿ÉÒÔÖ±½Óµ÷ÓÃWatcher½Ó¿Ú£¬°ÑÆäÊôÐÔÖ¸¶¨µÄ¼ÆËã¹æÔò´«µÝΪ£¬ÊôÐÔµÄÍØÕ¹getter£¬¼´£º
// ³õʼ´¦Àícomputed¼ÆËãÊôÐÔ
function initComputed (vm, computed) {
for (var key in computed) {
var userDef = computed[key]; // ¶ÔÓ¦µÄ¼ÆËã¹æÔò
// ´«µÝ¸øwatcherʵÀýµÄthis.getter -- ÍØÕ¹getter
var getter = typeof userDef === 'function' ?
userDef : userDef.get;
watchers[key] = new Watcher(vm,
getter, noop, computedWatcherOptions);
}
}
|
watchÊôÐÔ
¶ø¶ÔÓÚwatchÊôÐÔÓÖÓв»Í¬£¬¸ÃÊôÐÔÊDZäÁ¿»ò±í´ïʽ£¬¶øÇÒÓë¼ÆËãÊôÐÔ²»Í¬µÄÊÇ£¬ËüÐèÒªÖ¸¶¨Ò»¸ö±ä¸üʼþ·¢ÉúºóµÄ»Øµ÷º¯Êý£º
function initWatch (vm, watch) {
for (var key in watch) {
var handler = watch[key];
createWatcher(vm, key, handler[i]); // ´«µÝ»Øµ÷
}
}
function createWatcher (vm, key, handler) {
vm.$watch(key, handler, options); // »Øµ÷
}
Vue.prototype.$watch = function (expOrFn, cb,
options) {
// ʵÀý»¯watcher£¬²¢´«µÝ»Øµ÷
var watcher = new Watcher(vm, expOrFn, cb, options);
}
|
³õʼ»¯WatcherÓëÒÀÀµ¹ÜÀí½Ó¿ÚµÄÁ¬½Ó
ÎÞÂÛÄÄÖÖÊôÐÔ×îºó¶¼ÊÇÓÉwatcher½Ó¿ÚʵÏÖ×·×ÙÒÀÀµ£¬¶øÇÒ×é¼þÔÚ¹ÒÔØÊ±£¬¼´»á³õʼ»¯Ò»´ÎWatcherʵÀý£¬°ó¶¨µ½Dep.target£¬Ò²¾ÍÊǽ«WatcherºÍDep½¨Á¢Á¬½Ó£¬Èç´ËÔÚ×é¼þäÖȾʱ²ÅÄܶÔÊôÐÔÒÀÀµ½øÐÐ×·×Ù£º
function mountComponent (vm, el, hydrating)
{
...
updateComponent = function () {
vm._update(vm._render(), hydrating);
...
};
...
vm._watcher = new Watcher(vm, updateComponent,
noop);
...
} |
ÈçÉÏ£¬´«µÝ updateComponent ·½·¨¸ø watcher
ʵÀý£¬¸Ã·½·¨ÄÚ´¥·¢×é¼þʵÀýµÄ vm._render() äÖȾ·½·¨£¬´¥·¢×é¼þ¸üУ¬´Ë mountComponent()
·½·¨»áÔÚ$mount()¹ÒÔØ×é¼þ¹«¿ª·½·¨Öе÷Óãº
// public mount method
Vue$3.prototype.$mount = function (el, hydrating)
{
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
};
|
×ܽá
µ½´ËΪֹ£¬¶ÔÓÚJavaScriptÊôÐÔÃèÊöÆ÷½Ó¿ÚµÄ½éÉܼ°ÆäÓ¦Ó㬻¹ÓÐÆäÔÚVue.jsÖеÄÏìӦʽʵ¼ùÔÀí»ù±¾²ûÊöÍêÁË£¬Õâ´Î×ܽá´ÓÔÀíµ½Ó¦Óã¬ÔÙµ½Êµ¼ùÆÊÎö£¬»¨·Ñ±È½Ï¶à¾«Á¦£¬µ«ÊÇÊÕ»ñÊdzÉÕý±ÈµÄ£¬²»½ö¶ÔJavaScript»ù´¡ÓиüÉîµÄÀí½â£¬»¹¸üÊìϤÁËVue.jsÏìӦʽµÄÉè¼ÆÔÀí£¬¶ÔÆäÔ´ÂëÊìϤ¶ÈÒ²ÓнϴóÌáÉý£¬Ö®ºóÔÚ¹¤×÷ºÍѧϰ¹ý³ÌÖУ¬»á½øÐиü¶àµÄ×ܽá·ÖÏí¡£ |