Object.defineProperty 这个方法大家耳熟能详,可以对 对象的属性进行添加或修改的操作。即可以进行  数据劫持 。vue就是通过这个方法来劫持数据的。

平时我们创建对象的时候,一般通过对象字面量的方式创建:

var student = {
name:"小明",
age:10
}
对象的属性在创建的时候,都带有一些特征值(特性),JS通过这些特征值来定义它们的行为。 
 
ECMA-262 第 5 版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。
ECMA-262 定义这些特性是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问它们。
为了表示特性是内部值,该规范把它们放在了两对儿方括号中,例如[[Enumerable]]。
 
ECMAScript 中有两种属性:数据属性和访问器属性。
 
                    -------------《JavaScript高级程序设计(第三版)》 第六章
属性描述符
 
MDN上,对于对象的属性在创建的时候,带有的特征值,叫的是属性描述符。包含数据描述符和存取描述符。
 
a)数据属性(数据描述符):
  1. [[Configurable]] :能否通过 delete 删除属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
  2. [[Enumerable]]:能否通过 for ·· in 或者 Object.keys() 枚举。
  3. [[Writable]]:属性的值能否被修改。
  4. [[Value]]:属性的值,可以是任何有效的 JavaScript 值(数值,对象,函数等)
b)访问器属性(存取描述符): 
  1. [[Configurable]] :能否通过 delete 删除属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
  2. [[Enumerable]]:能否通过 for ·· in 或者 Object.keys() 枚举。
  3. [[Get]]:在读取属性时调用的函数。
  4. [[Set]]:在写入属性时调用的函数。
 
属性描述符的默认值:有两种情况

 1) 当使用对象字面量或者构造函数的形式创建属性的时候,enumerable 、configurable 、 writable都为 true ,value、get、set都为undefined 。所以平时定义对象的时候,我们可以随意增删改查。

2) 当使用Object.defineProperty、Object.defineProperties 或 Object.create 函数的情况下添加的属性。enumerable 、configurable、writable都为 false;value、get、set都为undefined。

  可以通过Object.getOwnPropertyDescriptor(对象名,属性名)来获取属性描述符的默认值

  Object.defineProperty :

  Object.create :

怎么修改默认属性默认值?

这种两个方括号 [[ ]] 的方式,我感觉就和指向对象的原型的指针类似,ECMA-262 第 5 版 称这个指针为 [[prototype]] ,也是没有标准的方式访问,但是主流浏览器都提供了__proto__属性来访问。

这上面的属性描述符都有自己的默认值,但是如果我想修改某些数据描述符的默认值呢?它并不能直接访问啊,比如 obj.age.[[Enumerable]] 这样是不行的。既然不能直接访问,那么我怎么去修改对象中某些属性的指定特性呢?

以前可以使用非标准的方式:  对象.__defineGetter__( "属性", function(){} )  或者  对象.__defineSetter__( "属性", function(){} )  。不过这方法已经被废弃了,虽然有些浏览器还支持,但是不建议使用。

这时候就需要用到 Object.defineProperty 这个方法了。

语法:Object.defineProperty(obj,prop,descriptor)
  1. obj,即需要修改属性的对象。必填。
  2. prop,需要修改的属性。必填。
  3. descriptor,属性修饰符配置项,是个对象。属性修饰符不填的情况下,这个参数也不能少,最少也要是一个 { } 空对象。
  4. 最终返回处理后的 obj 对象
descriptor 也是分数据描述符存取描述符。功能也是一样
a) 数据描述符:
  1. configurable
  2. enumerable
  3. writable
  4. value

b) 存取描述符

  1. configurable
  2. enumerable
  3. get
  4. set

上面的这些属性都是可以直接访问配置的。

数据描述符和存取描述符用法都很简单。不过需要注意的是:

  1. 数据属性符的writable或value 与 存取描述符的get或set不能同时存在 。会报错。
  2. 存取描述符的get与set也可以不同时存在,如果只指定get表示属性不能写(意思进行赋值操作,最后属性还是为undefined,即使最初属性定义了初始值),只指定set表示属性不能读(意思是获取属性的时候是undefined,整个对象都为{ }。即使最初定义了一些属性的)。
  3. 存取描述符的get与set是个函数,函数里的 this 指向的是 需要修改属性的对象即obj

还有个Object.defineProperties() 可以劫持多个属性。有兴趣的可以去 MDN 看看

如果对象的属性中还有对象,那么这时候需要深层遍历,一般的方法是:

var obj = {
name:"zjj",
sex:'male',
money:100,
info:{
face:'smart'
}
} observe(obj)
console.log(obj)
obj.sex = 'female'
obj.info.face = 20;
obj.info.hobit = 'girl';
console.log(obj) function observe(target){
if (!target || typeof target !== 'object') return; Object.keys(target).forEach(function(val){
defineProp(target,target[val],val)
})
} function defineProp(curObj,curVal,curKey){
observe(curVal) //再次遍历子属性
Object.defineProperty(curObj,curKey,{
enumerable:true,
configurable:true,
get:function(){
console.log('获取了属性',curVal)
return curVal
},
set:function(newData){
console.log('设置了属性',newData)
curObj = newData;
}
})
}

这样,目标对象中的属性的值为对象的时候也能进行数据劫持了。不过我疑惑的点是:添加不存在的属性时,为什么调用的是get方法???后面搞懂了再来解决这个问题

Object.defineProperty的缺点:

  1. 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。所以vue才设置了7个变异数组(push、pop、shift、unshift、splice、sort、reverse)的 hack 方法来解决问题。
  2. 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。如果能直接劫持一个对象,就不需要递归 + 遍历了。所以 vue3.0 会使用 Proxy 来替代Object.defineProperty

Proxy:代理

  听说vue3.0 会用 proxy 替代 Object.defineProperty()方法。所以预先了解一些用法是有必要的。

  proxy 能够直接 劫持整个对象,而不是对象的属性,并且劫持的方法有多种。而且最后会返回劫持后的新对象。所以相对来讲,这个方法还是挺好用的。不过兼容性不太好。

  关于proxy的介绍与用法,可以看看 阮一峰老师的 这篇文章

题外话:ECMAScript  与  JavaScript  的关系

参考:这里

Netscape 公司最初创建了一个用于浏览器的脚本语言,后与Sun 公司(创建了Java)联合发布了该脚本语言,命名为Javascript;后来微软也出了一个 JScript,用于IE3.0浏览器;还有Cenvi的ScriptEase。于是Netscape 公司决定将 JavaScript 提交给国际标准化组织 ECMA,希望 JavaScript 能够成为国际标准。

1997年7月,ECMA的TC93(39号技术委员会)发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准。由于商标和其它协议的原因,只有Netscape 公司能使用Javascript 这个名称,所以最后将这种语言称为 ECMAScript。而现在我们所说的 JavaScript 是 ECMAScript + DOM + BOM的集合。DOM和BOM是W3C制定的规范。

现在说的ES5就是 ECMAScript 5.0版,而ES6就是 ECMAScript 6 后更名为 ECMAScript 2015(简称ES2015),后面每一年的6月份都会发布一个新的版本,不过增加的内容并不多。ES7(ES2016)、ES8 (ES2017)、ES9 (ES2018),现在2019.7月了,这个时候都已经出了ES10(ES2019)。不过ES10还是一个草案,并没有多少浏览器支持。主流的都是ES5 和 ES6。

关于Object.defineProperty 的基础知识的更多相关文章

  1. 开发工具、Object类(java基础知识十一)

    1.常见开发工具介绍 * A:操作系统自带的记事本软件 * B:高级记事本软件 * C:集成开发环境 IDE     * (Integrated Development Environment) *  ...

  2. js--对象内部属性与 Object.defineProperty()

    前言 JavaScript 中允许使用一些内部特性来描述属性的特征,本文来总结一下对象内部属性与 Object.defineProperty() 的相关知识. 正文 1.属性类型 js中使用某些内部属 ...

  3. 第二十九节:Java基础知识-类,多态,Object,数组和字符串

    前言 Java基础知识-类,多态,Object,数组和字符串,回顾,继承,类的多态性,多态,向上转型和向下转型,Object,数组,多维数组,字符串,字符串比较. 回顾 类的定义格式: [类的修饰符] ...

  4. Object c 基础知识

    文件类型说明:.h 头文件,用于定义类.实例变量及类中的方法等定义信息(interface). .m 源文件,定义方法体,可实现objce-c和c方法(implementation). .mm c++ ...

  5. javascript之Object.defineProperty的奥妙

    直切主题 今天遇到一个这样的功能: 写一个函数,该函数传递两个参数,第一个参数为返回对象的总数据量,第二个参数为初始化对象的数据.如: var o = obj (4, {name: 'xu', age ...

  6. Vue2基础知识学习

    Vue2基础知识学习 01.初识 new Vue({ el: '#root', //用于指定当前Vue实例为哪个容器服务,值通常为css选择器符 data () { return { } } }); ...

  7. .NET面试题系列[1] - .NET框架基础知识(1)

    很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...

  8. 【干货】用大白话聊聊JavaSE — ArrayList 深入剖析和Java基础知识详解(二)

    在上一节中,我们简单阐述了Java的一些基础知识,比如多态,接口的实现等. 然后,演示了ArrayList的几个基本方法. ArrayList是一个集合框架,它的底层其实就是一个数组,这一点,官方文档 ...

  9. Python开发【第二篇】:Python基础知识

    Python基础知识 一.初识基本数据类型 类型: int(整型) 在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,即-2147483648-2147483647 在64位 ...

随机推荐

  1. SQL server 2008 防火墙设置

    zh以前应为有特殊需求,需要在副武器外连接数据库,需要打开TCPIP服务. 但是因为有防火墙,经常连接不成功. 根据网上的资料总结,写了一个小的bat,来解决这个问题: @echo ========= ...

  2. SqlServer 动态SQL(存储过程)中Like 传入参数无正确返回值的问题

    最近在做项目时,以动态Sql进行Like语句查询时发现应该返回的结果却一直返回空,后来发现是写法错误: 错误SQL: DECLARE @0 varchar(20) SET @0 = 'XA-LZ' S ...

  3. C++ 王者归来:对编程语言的需求总结为四个:效率,灵活,抽象,生产率(C++玩的是前三个,Java和C#玩的是后两个)

    Why C++ ? 王者归来(转载) 因为又有人邀请我去Quora的C2C网站去回答问题去了,这回是 关于 @laiyonghao 的这篇有点争议的博文<2012 不宜进入的三个技术点>A ...

  4. C# — WinForm TCP连接IPv4和IPv6的判断

    大家都知道, XP系统默认使用的是IPv4格式的IP地址, 而Win7系统默认使用的是IPv6格式的IP地址. 所以有些关于TCP连接的WinForm系统,在XP下可能运行正常,但在Win7下却不能运 ...

  5. reset.css(样式重置)

    CSS Reset,意为重置默认样式.HTML中绝大部分标签元素在网页显示中都有一个默认属性值,通常为了避免重复定义元素样式,需要进行重置默认样式(CSS Reset).举几个例子:1.淘宝(CSS ...

  6. 02、MySQL—数据库基本操作

    数据库是数据存储的最外层(最大单元) 1.创建数据库 基本语法:create database 数据库名字 [库选项]; 范例:使用create database 创建数据库 库选项:数据库的相关属性 ...

  7. kubernetes实战篇之为默认账户创建镜像拉取密钥

    系列目录 上一节我们分别使用纯文本账户密码和docker的config文件一创建一个kubernetes secret对象,并且把它添加到containers的imagePullSecrets字段用以 ...

  8. 自定义实现一个loghub(或kafka)的动态分片消费者负载均衡?

    一般地,像kafka之类的消息中间件,作为一个可以保持历史消息的组件,其消费模型一般是主动拉取方式.这是为了给消费者足够的自由,回滚或者前进. 然而,也正是由于将消费消息的权力交给了消费者,所以,消费 ...

  9. CentOS 7使用Elasticsearch

    安装ElasticSearch 下载依赖 Elasticsearch依赖jdk, 在官网下载jdk压缩包, 或者直接安装. 下载压缩包, 解压tar -xzvf jdk-8u181-linux-x64 ...

  10. 将GMap封装为Activex供QT使用(工具:VS2017,QT5.12) 更新部署到其它电脑

    由于一开始定的开发平台在QT下,到后面要加入地图控件.qt里本身带有地图qmap(在qt的官方案例中可以找到,用qml做的),但只有固定的几个地图源,要做google或者bing地图,时间和人力不允许 ...