JavaScript defineProperty如何实现属性劫持_第1页
JavaScript defineProperty如何实现属性劫持_第2页
JavaScript defineProperty如何实现属性劫持_第3页
JavaScript defineProperty如何实现属性劫持_第4页
JavaScript defineProperty如何实现属性劫持_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第JavaScriptdefineProperty如何实现属性劫持目录前言描述符

细说get和set劫持对象的某个属性

劫持对象的所有属性

劫持对象的所有属性-包括对象类型的属性值

defineProperty的缺陷defineProperty还可以挂载属性

defineProperty还能写日志

总结

前言

defineProperty是vue实现数据劫持的核心,本文一点点的说明defineProperty怎么实现属性劫持的。

其实我们一般的操作对象属性的方式,增加或者修改属性,均可以使用Object.defineProperty。

letobj={};

//寻常操作:增加/修改新属性

obj.a=1;

//等同于:

Object.defineProperty(o,"a",{

value:1,

writable:true,

configurable:true,

enumerable:true

当然寻常的例子,我们是不会这么玩的,太啰嗦了。

但defineProperty可以更精确地添加或修改对象的属性。

描述符

先说个专有名词:描述符。

其实就是defineProperty的第三个参数,是个对象。这个对象的有以下属性:

configurable属性:能不能修改描述符,就是能不能再次修改描述符的其他属性

enumerable属性:能不能枚举该属性,就是a属性能不能被for到

writable属性:能不能修改属性值,就是能不能这样修改obj.a=1

value属性:该属性的值

get属性:是个函数,当访问该属性的时候,函数自动调用,函数返回值就是该属性的值

set属性:是个函数,当修改该属性的时候,函数自动调用,函数有且只有一个参数,赋值的新值

注意!!!

描述符里的value属性writable属性与get属性set属性是互斥的关系,只能存在一个

另外的属性默认值都是false,不想false的话,记得配置哈,不细说(主要我也不怎么用)。

细说get和set

get属性:是个函数,当访问该属性的时候,函数自动调用,函数返回值就是该属性的值

set属性:是个函数,当修改该属性的时候,函数自动调用,函数有且只有一个参数,赋值的新值

默念三遍,背诵。

写个get和set的例子辅助理解。

这个例子必须掌握,弄懂之后基本就掌握了数据劫持的精髓了

letobj={};

letvalue=1;

Object.defineProperty(obj,"b",{

get(){

console.log("读取b属性",value);

returnvalue;

set(newValue){

console.log("设置b属性",newValue);

value=newValue;

//触发get函数,get的返回值就是属性值

console.log(obj.b);

//触发set函数,value的值变成了2,注意!!!,此时内存里,属性值并没有改变

obj.b=2;

//但是,想要读取属性值的时候,就必然会触发get函数,属性值也自然就改变了,这个思想真的很赞

console.log(obj.b);

这里有个坑:get里是不能有读取的操作,不然一直死循环,所以使用到getset的地方,总需要借助一个变量

所以,这里,变量value的值就是属性的值,如果想要修改属性,修改value的值即可。

这个例子弄懂了,get,set的精髓,我觉得也就差不多了。

劫持对象的某个属性

有了刚刚例子的基础,试着写写劫持对象的任意一个属性。

functionobserveKey(obj,key){

letvalue=obj[key];

Object.defineProperty(obj,key,{

get(){

console.log("读取属性",value);

returnvalue;

set(newValue){

console.log("设置属性",newValue);

value=newValue;

letobj={a:1};

observeKey(obj,"a");

//读取a,触发get函数

console.log(obj.a);

//设置a,触发set函数

obj.a=1;

劫持对象的所有属性

再试试劫持对象的所有属性

其实就是遍历:

functionobserveObj(obj){

for(letkeyinobj){

//直接使用obj.hasOwnProperty会提示不规范

if(Ototype.hasOwnProperty.call(obj,key)){

observeKey(obj,key);

returnobj;

functionobserveKey(obj,key){

letvalue=obj[key];

Object.defineProperty(obj,key,{

get(){

console.log("读取属性",value);

returnvalue;

set(newValue){

console.log("设置属性",newValue);

value=newValue;

letobj={a:1,b:2};

observeObj(obj);

console.log(obj);

//读取a,触发get函数

console.log(obj.a);

//设置a,触发set函数

obj.a=1;

劫持对象的所有属性-包括对象类型的属性值

上面的有个缺陷,就是当属性值也是对象的时候,不能劫持属性值,如{a:1,c:{b:1}}

简单,递归,补上就行。

functionobserveObj(obj){

//加上参数限制,必须是对象才有劫持,也是递归的终止条件

if(typeofobj!=="object"||obj==null){

return;

for(letkeyinobj){

//直接使用obj.hasOwnProperty会提示不规范

if(Ototype.hasOwnProperty.call(obj,key)){

observeKey(obj,key);

//这里劫持该属性的属性值,如果不是对象直接返回,不影响

observeObj(obj[key]);

returnobj;

functionobserveKey(obj,key){

letvalue=obj[key];

Object.defineProperty(obj,key,{

get(){

console.log("读取属性",value);

returnvalue;

set(newValue){

console.log("设置属性",newValue);

value=newValue;

letobj={a:1,b:2,c:{name:"c"}};

observeObj(obj);

console.log(obj);

//读取a,触发get函数

console.log(obj.a);

//设置a,触发set函数

obj.a=1;

//触发set函数

="d";

注意,observeObj这个函数,不能劫持对象的新增属性,只能劫持对象已有的属性。

defineProperty的缺陷

不能监测对象增加属性

不能监测对象删除属性

不能劫持数组的修改

当然数组的修改可以通过别的方式监测到的,其是通过劫持改变数组方法实现的。

以上缺陷,也是vue里面为啥有$set/$delete以及对数组只能使用特定方法才能检测到。

letobj={a:1,b:[1,2]};

observeObj(obj);

//新增属性

obj.c=3;

//不会触发get函数

console.log(obj.c);

//不会触发set函数

obj.b.push(3);

defineProperty还可以挂载属性

其实就是访问可以简写成,专业话术,将data上的属性挂载到options上

相当于,用defineProperty,在options上增加新属性:

//先挂载单个属性

//options.data相当于sourceoptions相当于target

functionproxyKey(target,source,key){

Object.defineProperty(target,key,{

//这里的source[key]相当于变量value,所以说最简单的那个例子是核心

get(){

returnsource[key];

set(newValue){

if(newValue===source[key]){

return;

source[key]=newValue;

//遍历属性,挂载下

functionproxyObj(target,source){

for(letkeyinsource){

//直接使用obj.hasOwnProperty会提示不规范

if(Ototype.hasOwnProperty.call(source,key)){

proxyKey(target,source,key);

letoptions={

data:{name:1}

proxyObj(options,options.data);

console.log();

话说,vue的属性劫持和挂载属性,核心原理差不多就是上面这些。

defineProperty还能写日志

比如obj有个属性,此属性值经常变化,想要记录其所有变化的值,以此可以形成日志。

letobj={a:1};

letlog=[obj.a];

letvalue=obj.a;

Object.defineProperty(obj,"a",{

get(){

returnvalue;

set(newValue){

if(newValue===value){

return;

value=newValue;

log.push(newValue);

obj.a=2;

obj.a=3;

obj.a=4;

//[1,2,3,4]

console.log(log);

通用的可以抽离出一个类,专门记录某个值的变化

classArchiver{

constructor(){

letvalue=null;

this.archive=[];

Object.defineProperty(this,"a",{

get(){

retu

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论