vue2原理初探-数据代理和数据劫持
- 几个基础知识点
- 数据代理
- 数据劫持
- 完整demo
一、几个基础知识点
1.普通函数和箭头函数的区别

如上图所示,a函数定义在全局,其作用域链,只有GO对象,当其执行的时候会临时产生一个aAO对象,所以b函数的作用域链就是 aAO -> GO
函数每次执行都会产生一个新的AO对象挂在作用域链头部,函数被解释执行的时候,其内部标识符的检索都是在作用域链上检索的。
根据以上理论,我们来执行b函数。

控制台输出如下:

为什么b函数中看到的this是window呢?是因为其顺着作用域链找,bBO -> aAO -> GO,只有GO上有this,就是window。
当然对于普通函数,我们可以改变其this指向:

控制台输出如下:

于是我们得到一个结论:
函数执行生成的临时AO对象中,包含了arguments隐式变量来保存实参列表。
函数执行看到的this变量,可以修改,通过对象调用,call,apply来修改。
但是,但是,但是。。。。
箭头函数,它就不是这样的。。。

控制台输出:

箭头函数,没有arguments隐式变量了。而且,this它居然修改不了。。
那么说白了,this只能在其作用域链上找了,生成的临时AO对象上没有this,没有this。
| 是否有arguments隐式变量 | 是否能改变this指向 | |
| 普通函数 | 是 | 是 |
| 箭头函数 | 否 | 否 |
2.闭包

由于plus,minus,showCount三个函数的作用域链中有aaa的AO对象,所以当他们被返回后,形成了闭包。
3.defineProperty函数的使用

但是其实除了这样给对象加属性外,我们也可以通过defineProperty来给对象加属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo01-defineProperty的使用</title>
</head>
<body>
<script type="application/javascript">
// defineProperty()
let obj = {
name: 'zhangsan',
age: 33,
showInfo() {
console.log(this.name + "--" + this.age)
}
}
obj.showInfo(); Object.defineProperty(obj, 'ccc', {
value: 10,
enumerable: true, // 是否能枚举
configurable: true, // 是否能删除
writable: true // 是否能写入
})
// 枚举
var keys = Object.keys(obj);
console.log(keys);
// 写入
obj.ccc = 100;
console.log(obj);
// 删除
delete obj.ccc;
console.log(obj); </script>
</body>
</html>
上述代码控制台输入如下:

其实这样的话,定义属性和我们直接写属性没什么太大区别,关键是下面这样的写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo01-defineProperty的使用</title>
</head>
<body>
<script type="application/javascript">
// defineProperty()
let obj = {
name: 'zhangsan',
age: 33,
showInfo() {
console.log(this.name + "--" + this.age)
}
} let ccc = 10;
Object.defineProperty(obj, 'ccc', {
// value: 10,
enumerable: true, // 是否能枚举
configurable: true, // 是否能删除
// writable: true, // 是否能写入
get: function proxyGet() {
return ccc;
},
set: function proxySet(value) {
ccc = value;
}
})
console.log(obj.ccc);
obj.ccc = 100;
console.log(obj.ccc); console.log(obj); </script>
</body>
</html>
注意,如果我们要定义属性的get/set,那么就不能定义value和writable了,否则会报错。
此时我们对属性ccc的写入和读取将走get/set方法了。
控制台输出如下:

这个ccc属性的三个点,是不是特别想我们使用vue的时候点开的组件对象里面的一些属性。
在这里我插一句,我点开set/get给大家看看,其实能看见[[scopes]]作用域链了。
如下图:

当然,这里我们看见了,set/get函数定义的时候的作用域链[[scopes]],其实是SO -> GO,这个SO其实就是外层包裹的script标签。
可以理解成,script标签执行流程就像一个函数执行一样,也会产生作用域对象挂在[[scopes]]上。
二、数据代理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo01-vue简单使用</title>
<!--引入vue-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script> </head>
<body> <div id="app">
<h3>姓名:{{name}}</h3>
<h3>年龄:{{age}}</h3>
<button @click="agePlusOne">年龄+1</button>
</div> <script type="application/javascript"> let vm = new Vue({
el: '#app',
data(){
return {
name: '张三',
age: 33
}
},
methods: {
agePlusOne(){
this.age ++;
console.log(this)
}
}
}); </script>
</body>
</html>
点击按钮,我把Vue对象打印出来:

可以清晰的看到,我们配置的data对象中的属性都被定义在了Vue组件对象中。
起码,这里看到了,vue做了数据代理,我们在组件对象中对data中同名属性的set和get都走了其对应的代理方法。
三、数据劫持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo01-数据劫持</title>
</head>
<body>
<div id="app">
</div>
<script type="application/javascript">
function setAppInnerText (value) {
document.querySelector("#app").innerText = value;
} let obj = {
name: '张三',
age: 100,
showInfo() {
return this.name + "---" + this.age;
}
}; setAppInnerText(obj.showInfo())
// 数据劫持
Object.keys(obj).forEach(key => {
let value = obj[key];
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
set(newValue) {
if(newValue === value) {
return ;
} else {
value = newValue;
setAppInnerText(obj.showInfo());
}
},
get() {
return value;
}
})
})
obj.age = 1000;
</script>
</body>
</html>
上面的代码,就是数据劫持,每次属性设置的时候,都触发了setAppInnerText函数的调用。
上述代码执行完之后,只要我们对obj对象的属性进行修改,都会触发页面的变化。
四、完整demo
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>data_observer</title>
</head> <body>
<div id="root">
a = {{a}}
<br>
b = {{b}}
</div>
<script> function Vue(config) {
this._data = config.data;
// 数据代理 方便程序员操作
for (let key in config.data) {
Object.defineProperty(this, key, {
enumerable: true,
get: function proxyGet() {
return this._data[key];
},
set: function proxySet(value) {
this._data[key] = value;
}
})
}
this.mounted = false;
if (config.el) {
this.$mount(config.el);
}
} Vue.prototype.$mount = function (id) {
if (!this.mounted) {
this.originInnerHtml = document.getElementById(id).innerHTML;
// 编译模板生成render
let _self = this;
function render() {
let innerHtml = _self.originInnerHtml;
for (let key in _self._data) {
innerHtml = innerHtml.replaceAll('{{' + key + '}}', _self._data[key]);
}
document.getElementById(id).innerHTML = innerHtml;
} // 数据劫持
for (let key in this._data) {
let value = this._data[key];
Object.defineProperty(this._data, key, {
enumerable: true,
configurable: true,
get: function getObserver() {
return value;
},
set: function setObserver(newValue) {
if (value !== newValue) {
value = newValue;
render();
}
}
})
}
// 执行render
render();
this.mounted = true;
}
}
let config = {
el: 'root',
data: {
a: '牛逼的消息',
b: '学习vue2底层实现'
}
};
let vm = new Vue(config);
</script>
</body> </html>
控制台打印如下:

vue2原理初探-数据代理和数据劫持的更多相关文章
- 对数据劫持 OR 数据代理 的研究------------引用
数据劫持,也叫数据代理. 所谓数据劫持,指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果.比较典型的是 Object.defineProperty() 和 ...
- vue中的数据代理原理
const vm = new Vue({ data:{ name:'boos' } }) // 注意 :使用构造函数构建vue实例时,传入的是一个option对象,它包含了data,computed等 ...
- es6中的Proxy和vue中的数据代理的异同
1:概述 1-1:Proxy 用于修改某些操作的默认行为,Proxy可以说在对对象进行各种访问或者操作的时候在外层进行一层拦截,在操作之前都需要经过这种拦截.proxy返回的是一个新对象,可以通过操作 ...
- 8_vue是如何进行数据代理的
在了解了关于js当中的Object.defineProperty()这个方法后,我们继续对vue当中的数据代理做一个基于现在的解析 建议观看之前先了解下js当中的Obejct.defineProper ...
- Vue源码中的数据代理
直接开讲: 由于这个Vue底层封装的函数太多了,我这里只讲思路不说具体的执行了什么函数. const vm=new Vue({这里写一个data,可以是对象也可以是函数}) 在写这段代码的时候 ...
- MVVM模型 && 数据代理
MVVM模型 观察发现 data中所有属性,最后都出现在vm身上 vm身上所有属性及Vue原型身上所有属性,在Vue模板中都可以直接使用 Vue中的数据代理 通过vm对象来代理data对象中属性的操作 ...
- WCF初探-16:WCF数据协定之基础知识
数据协定概念 “数据协定”是在服务与客户端之间达成的正式协议,用于以抽象方式描述要交换的数据. 也就是说,为了进行通信,客户端和服务不必共享相同的类型,而只需共享相同的数据协定. 数据协定为每一个做数 ...
- WCF初探-17:WCF数据协定之等效性
数据协定等效性特点概述 对于客户端要将某种类型的数据成功发送到服务,或者服务要将数据成功发送到客户端的情况,接收端上并不一定必须存在此发送数据类型. 唯一的要求是两种类型的数据协定应该等效. 要使数据 ...
- WCF初探-18:WCF数据协定之KnownType
KnownTypeAttribute 类概述 在数据到达接收终结点时,WCF 运行库尝试将数据反序列化为公共语言运行库 (CLR) 类型的实例.通过首先检查传入消息选择为反序列化而实例化的类型,以确定 ...
- ElasticSearch 学习记录之 分布式文档存储往ES中存数据和取数据的原理
分布式文档存储 ES分布式特性 屏蔽了分布式系统的复杂性 集群内的原理 垂直扩容和水平扩容 真正的扩容能力是来自于水平扩容–为集群添加更多的节点,并且将负载压力和稳定性分散到这些节点中 ES集群特点 ...
随机推荐
- 「学习笔记」模运算与 BSGS 算法
取模 取模符号:\(x \bmod y\),表示 \(x\) 除以 \(y\) 得到的余数. 例如, \[5 \bmod 3 = 2\\ 7 \bmod 4 = 3\\ 3 \bmod 3 = 0\\ ...
- 基于ggplot2的解剖图和组织模块可视化
摘要 将数据显示到解剖结构上,是一种可以快速观察组织相关信息的便捷技术.然而,绘制组织是一项复杂的任务(a complex task),需要解剖学和艺术方面的专业知识.虽然已经存在可用于在解剖图上显示 ...
- VLAN——提高网络性能、安全性和灵活性的利器
前言 VLAN是Virtual Local Area Network的缩写,它是一种通过网络交换机虚拟划分局域网的技术.VLAN可以将一个物理局域网划分成多个逻辑上的虚拟局域网,各个虚拟局域网之间相互 ...
- RT-Thread线程构建
RT-Thread 操作系统的启动过程如下 main()函数作为其中的一个线程在运行. 如果想新建一个线程,和main()线程并行运行,步骤如下: 第一步:线程初始化函数申明 static voi ...
- 尚医通-day10【微信扫码登录】(内附源码)
第01章-准备工作 1.申请微信登录 https://open.weixin.qq.com (1)注册开发者账号:准备营业执照 (2)邮箱激活 (3)完善开发者资料 (4)开发者资质认证:1-2个工作 ...
- PlayWright(十二)- PO模式
1.PO模式是什么? PO,即Page Object,直译为页面对象,代表 Web 应用程序的一部分 具体什么意思呢,通俗来讲,一个页面有输入.点击.搜索功能,而且有很多页面,这时候我们就采用每个 ...
- go web学习(四)
跟着b站https://space.bilibili.com/361469957 杨旭老师学习做的笔记 中间件 什么是中间件 请求----> 中间件 ----> Handler 响应 &l ...
- 兰姆达 x AnayticDB 降本30%的数据湖最佳实践
1. 客户介绍 上海兰姆达数据科技有限公司(简称"兰姆达数据")是一家提供卓越的数据科学软件产品和解决方案的初创高科技公司.兰姆达核心团队专注于大数据,机器学习算法和精准营销Saa ...
- 落地微服务架构v2.0
网关+服务治理 将服务注册到Consul中,需要首先系统该插件. Ocelot:网关组件,腾讯和微软都在使用. 可以使用dotnet run命令启动.NET Core项目,启动时可以在命令时传递监听的 ...
- Centos静默安装Oracle11G
环境准备 Oracle 11gR2 64位 Linux版安装包 linux.x64_11gR2_database_1of2.zip linux.x64_11gR2_database_2of2.zip ...