关于Object.defineProperty 的基础知识

Object.defineProperty 这个方法你们耳熟能详,能够对 对象的属性进行添加或修改的操做。便可以进行  数据劫持 。vue就是经过这个方法来劫持数据的。javascript

平时咱们建立对象的时候,通常经过对象字面量的方式建立:html

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.key() 枚举。
  3. [[Writable]]:属性的值可否被修改。
  4. [[Value]]:属性的值,能够是任何有效的 JavaScript 值(数值,对象,函数等)
b)访问器属性(存取描述符): 
  1. [[Configurable]] :可否经过 delete 删除属性,可否修改属性的特性,或者可否把属性修改成访问器属性。
  2. [[Enumerable]]:可否经过 for ·· in 或者 Object.key() 枚举。
  3. [[Get]]:在读取属性时调用的函数。
  4. [[Set]]:在写入属性时调用的函数。
 
属性描述符的默认值:有两种状况

 1) 当使用对象字面量或者构造函数的形式建立属性的时候,enumerable 、configurable 、 writable都为 true ,value、get、set都为undefined 。因此平时定义对象的时候,咱们能够随意增删改查。vue

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

  能够经过Object.getOwnPropertyDescriptor(对象名,属性名)获取属性描述符的默认值es6

  Object.defineProperty :数组

  Object.create :浏览器

 

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

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

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

之前可使用非标准的方式:  对象.__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。 

相关文章
相关标签/搜索