vue.js原理和实现双向绑定MVVM(1)--Observer基础知识

今天是周五,今天一行代码也没有写,心血来潮想看看vue.js的实现原理和双向绑定MVVM。来吧BB那么多费话真没用,讲一点实现基础理论是必要的

我相信大家对Angular.JS的成为下一代最主流的MVC架构还记忆在心,但好景不长,angular2.0 vue.js react 纷纷居上。为什么我把vue.js夹在中间
因为1.0抄angular的,2.0抄react的,这我也能理解尤大神,没办法,一个想搞过一个团队,只有抄,说错了,(借鉴)我只能说真心好,快,轻。

数据劫持:vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调。

已经了解到vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()
来实现对属性的劫持,达到监听数据变动的目的,无疑这个方法是本文中最重要、最基础的内容之一,要实现mvvm的双向绑定,就必须要实现以下几点:
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图4、mvvm入口函数,整合以上三者

我写的文章永远是详解,不然就不是我的个性了,什么github,这种里面的东西是治标不治本,拿来用用可以,换种自己想要的东西,就是一头苦恼,所以这次分享内面内容分n次说,我也不确定几次!反正就是详细说!我也不知道最后分几部!就当看抗日剧吧

我做解析还是喜欢先把要用到的知识点先拿出来解析一下,结合一下之后你就会发现,看代码一点都不难

第一章Object.keys()和 Object.defineProperty用法

Object.keys()
--引用MDN
Object.keys()方法会返回一个由给定对象的所有可枚举自身属性的属性名组成的数组,数组中属性名的排列顺序和使用for-in
循环遍历该对象时返回的顺序一致 (顺序一致不包括数字属性)(两者的主要区别是 for-in 还会遍历出一个对象从其原型链上继承到的可枚举属性)。

返回参数
返回可枚举的自身属性的属性名

描述
Object.keys
返回一个所有元素为字符串的数组,其元素来自于从给定的对象上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。

var obj = {
    name : "ziksang",
    age : 20
}//声明一个对象
console.log(Object.keys(obj))
//["name", "age"]打印出来的是对象的属性名
//>>>>-----------------------------
//以下所有返回的都是字符串数组
var arr = ["a", "b", "c"];
console.log(Object.keys(arr)); 
//["0", "1", "2"] 返回的是合并合后数组的下标

// 类数组对象,简称类似数组的对象
var obj2 = { 0 : "a", 1 : "b", 2 : "c"};
console.log(Object.keys(obj2));
//["0", "1", "2"]打印出来健值,简称属性名
function demo(){
    console.log(arguments)
    //这里的arguments也是一个类数组对象
    return Object.keys(arguments)
}
console.log(demo(1,2,3))
////["0", "1", "2"]打印出来是下标,怎么说呢也可以叫为下标,也可以叫我属性名
//>>>>------------------------------------
//具有随机键排序的数组类对象
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
//果如遇到排序不正常的类数组对象
//最后的返回值会给你从小到大排序
console.log(Object.keys(an_obj)); // console: ['2', '7', '100']

以上有那些用法,我已经给大家很祥细的讲出来了

细节注意点

var obj = {
    name : "ziksang",
}
//声明一个obj对象
obj.__proto__.a = 1
//在obj的原型上加一个a的属性
for( prop in obj){
    console.log(prop)
    //name,a //for in 是可以枚举
}
console.log(Object.keys(obj))
//["name"]不可以枚举对象上原型的属性名


//>>>>-----------------------------------------------------
console.log(Object.keys("foo"));
// TypeError: "foo" is not an object (ES5 code)

console.log(Object.keys("foo"));
//因为在ES6里,字符串也是一个Iterator(可以遍历的对象)
//分把字符串当作数组分隔,然后取下标
// ["0", "1", "2"]                   (ES6 code)

Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。

语法
Object.defineProperty(obj, prop, descriptor)

参数
obj
需要定义属性的对象。
prop
需定义或修改的属性的名字。
descriptor
将被定义或修改的属性的描述符。
返回值
返回传入函数的对象,即第一个参数obj

描述

虽然以下这句话不是我写的,但是一定要细读,这才是Object.defineProperty精髓

该方法允许精确添加或修改对象的属性。一般情况下,我们为对象添加属性是通过赋值来创建并显示在属性枚举中(for...in
Object.keys
方法),但这种方式添加的属性值可以被改变,也可以被删除。而使用Object.defineProperty()则允许改变这些额外细节的默认设置。例如,默认情况下,使用 Object.defineProperty()增加的属性值是不可改变的。

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个拥有可写或不可写值的属性。存取描述符是由一对 getter-setter 函数功能来描述的属性。描述符必须是两种形式之一;不能同时是两者

数据描述符和存取描述符均具有以下可选键值:
configurable

当且仅当该属性的 configurable 为 true 时,该属性描述符
才能够被改变,也能够被删除。**默认为false
**。
enumerable

当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。
**默认为false。
**

数据描述符同时具有以下可选键值:
value

该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为undefined

writable

当且仅当该属性的 writable 为 true 时,该属性才能被
赋值运算符改变。
**默认为false
**。

存取描述符同时具有以下可选键值:
get

一个给属性提供 getter 的方法,如果没有 getter 则为undefined
。该方法返回值被用作属性值。默认为undefined

set

一个给属性提供 setter 的方法,如果没有 setter 则为undefined
。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为undefined

记住,这些选项不一定是自身属性,如果是继承来的也要考虑。为了确认保留这些默认值,你可能要在这之前冻结Object.prototype
,明确指定所有的选项,或者将proto
属性指向null

//这上面的是一种隐式写法
//所有数据描述符都是默认为false的
Object.defineProperty(obj, "key", {
  __proto__: null, // 没有继承的属性
                   // 不可 enumerable(枚举)
                   // 不可 configurable(配置)
                   // 不可 writable(从写)
  value: "static"  // 作为默认值
});

// 显式写法
Object.defineProperty(obj, "key", {
  __proto__ : null
  enumerable: false,
  configurable: false,
  writable: false,
  value: "static"
});

如果对象中不存在指定的属性,Object.defineProperty()
就创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。拥有布尔值的字段的默认值都是false。value,get和set字段的默认值为undefined。定义属性时如果没有get/set/value/writable,那它被归类为数据描述符。

var o = {};
//声明一个对象
var a;//声明一个变量a
Object.defineProperty(o, "b", {
    get : function(){ return a },
    //调用o.b返回的是一个Undefined
    set : function(newValue){ a = newValue },
    //调用o.b = 3  然后b 的 属性值就为3
    enumerable : true,//可枚举,为数据描述符
    configurable : true//可配置 ,为数据描述符
});

//>>>>-----------------------------------------------------
var o = {};
//声明一个对象
Object.defineProperty(o, "a", {
    value : 37,  //存储描述 ,设置a的值为37
    writable : false,  //数据描述,不可修改
    enumerable : true,  //可枚举
    configurable : true //可配置
});
console.log(o.a) //=>37
o.a = 4
//因为是不可修改的,所以改了没用
console.log(o.a) //=>4
//>>>---------------------------------------------------
var o ={}
Object.defineProperty(o, "conflict", { 
    value: "0x9f91102",
    get: function() { return "0xdeadbeef" }  //存储描述符不能同样 
});
//底下报错
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors

举两个例子
1.例子1

function Archiver() {
  var count = 1;
  //声明一个变量count =1 
  var archive = [];
  //声明空数组
  Object.defineProperty(this, 'temperature', {
  //在构造函数定义一个属性,this指向这个构造函数
  //temperature是定义的属性名
    get: function() {
      console.log('get!');
      return count;
      //返回count变量,就是给他初时值义定
    },
    set: function(value) {
      count = value;
      //当给属性从新定义值时把新值给初始值
      archive.push({ val: count });
      //把每次设置的值扔进一个数组里
    }
  });
  this.getArchive = function() { return archive; };
  //在构造函数里的方法,返回数组值
}

var arc = new Archiver();
console.log(arc.temperature); // 'get!'
arc.temperature = 11;
arc.temperature = 13;
console.log(arc.getArchive()); // [{ val: 11 }, { val: 13 }]

2.例子2

    //定义一个对象,这个对象用于定义的描述
var pattern = {
    get: function () {
        return 'I alway return this string,whatever you have assigned';
    },
    //get方法返回一个字符串
    set: function () {
        this.myname = 'this is my name string';
    }
    //设置myname的值,这里的this是运行时执行的,所以这里不指向pattern对象
};


function TestDefineSetAndGet() {
    Object.defineProperty(this, 'myproperty', pattern);
}
//声明一个构造函数
//1.this.指向这个构造函数
//2.myproperty是指定构造函数的一个属性
//parttern是里面的描述


var instance = new TestDefineSetAndGet();
instance.myproperty = 'test';
//设置了定义属性的值,这里有什么用呢?改变myproperty的值
//非也,本质上是出发set方法让this.myname有值
//如果去掉的话,你会发现console.log(instance.myname);//undefined
console.log(instance.myproperty);
// 'I alway return this string,whatever you have assigned'
console.log(instance.myname);
// 'this is my name string'

就光这些知识点,有没有发觉已经离数据驱动的原理很相近了,就是通过get 和 set的方式来劫持这些属性,所以造房子,必须把根基打好,所以下一章我就要开始讲 Observe如何监听,观察这些属性

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,517评论 6 13
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 在iOS中,实现多线程的方式有很多,比如GCD,NSOperation,NSThread等等,但是一直对线程的概念...
    未之阅读 1,005评论 5 3
  • 引用参数的函数 引用参数的函数格式说明:void funName(array &args)相对于按值传递模式,并不...
    曹渊说创业阅读 470评论 0 0
  • 单身还是已婚,将愈来愈走向一种选择——你选择去承担什么。 我希望你敢等。人人生来都是一个人。并无所惧。 我想说,一...
    磐石VS蒲苇阅读 244评论 0 0