Object.defineProperty誰にでも馴染みのこの方法は、あなたがオブジェクトのプロパティで操作を追加または変更することができます。そのデータは、ハイジャックすることができます。VUEは、データをハイジャックするために、この方法です。
我々は、オブジェクトを作成するときに通常、通常はオブジェクトリテラルの方法で作成します。
VaRの学生= {
名: "小の明" 、
年齢: 10
}
オブジェクトのプロパティが作成され、すべての特性値(特性)の一部で、JSはこれらの特性値を介して自分の振る舞いを定義します。定義されたECMA-262第5版のみ、種々の特性(プロパティ)を記述する特徴(属性)の内部でのみ使用されます。ECMA-262にはJavaScriptを有効にしてこれらの特性は、JavaScriptエンジンを達成するために使用されている定義に直接アクセスすることはできませんそれらを。内部特性値を表示するには、[[列挙]]例えば、本明細書中で子ども2つの角かっこのペアを入れます。データアクセス属性とプロパティ:ECMAScriptの2つのプロパティがあります。-------------「JavaScriptの高度なプログラミング(第3版)」の章VI
- [コンフィグレーション]:プロパティを削除する削除、プロパティの特性を変更する能力を渡すことができ、またはプロパティは、プロパティにアクセスするように変更することができます。
-
[列挙]:で··又はObject.keyためにより列挙でき()。
-
[書き込み可能]:プロパティの値が変更されるかどうか。
-
[値]:プロパティの値は、任意の有効なJavaScriptの値(数値、オブジェクト、機能、等)であることができます
- [コンフィグレーション]:プロパティを削除する削除、プロパティの特性を変更する能力を渡すことができ、またはプロパティは、プロパティにアクセスするように変更することができます。
- [列挙]:で··又はObject.keyためにより列挙でき()。
- [[取得]:属性を読んだときに呼び出される関数。
- [[設定する]:プロパティを書くときに呼び出される関数。
1) 使用している場合、オブジェクトリテラルまたはコンストラクタフォームの作成は属性、設定、列挙、時間を、書き込み可能で、値が真である、取得、セットが定義されていません。オブジェクトが定義されているときに、通常、私たちは、CRUDに無料です。
2)使用する場合Object.defineProperty、Object.defineProperties又はObject.createの機能ケース属性を追加します。列挙は、設定可能、書き込み可能では偽であり、値、取得、セットが定義されていません。
Object.getOwnPropertyDescriptor(オブジェクト名、属性名)する属性記述子のデフォルト値を取得します。
Object.defineProperty:
Object.create:
デフォルト属性のデフォルトを変更する方法?
这种两个方括号 [[ ]] 的方式,我感觉就和指向对象的原型的指针类似,ECMA-262 第 5 版 称这个指针为 [[prototype]] ,也是没有标准的方式访问,但是主流浏览器都提供了__proto__属性来访问。
这上面的属性描述符都有自己的默认值,但是如果我想修改某些数据描述符的默认值呢?它并不能直接访问啊,比如 obj.age.[[Enumerable]] 这样是不行的。既然不能直接访问,那么我怎么去修改对象中某些属性的指定特性呢?
以前可以使用非标准的方式: 对象.__defineGetter__( "属性", function(){} ) 或者 对象.__defineSetter__( "属性", function(){} ) 。不过这方法已经被废弃了,虽然有些浏览器还支持,但是不建议使用。
这时候就需要用到 Object.defineProperty 这个方法了。
- obj,即需要修改属性的对象。必填。
- prop,需要修改的属性。必填。
- descriptor,属性修饰符配置项,是个对象。属性修饰符不填的情况下,这个参数也不能少,最少也要是一个 { } 空对象。
- 最终返回处理后的 obj 对象
- configurable
- enumerable
- writable
- value
b) 存取描述符
- configurable
- enumerable
- get
- set
上面的这些属性都是可以直接访问配置的。
数据描述符和存取描述符用法都很简单。不过需要注意的是:
- 数据属性符的writable或value 与 存取描述符的get或set不能同时存在 。会报错。
- 存取描述符的get与set也可以不同时存在,如果只指定get表示属性不能写(意思进行赋值操作,最后属性还是为undefined,即使最初属性定义了初始值),只指定set表示属性不能读(意思是获取属性的时候是undefined,整个对象都为{ }。即使最初定义了一些属性的)。
- 存取描述符的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的缺点:
- 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。所以vue才设置了7个变异数组(push、pop、shift、unshift、splice、sort、reverse)的 hack 方法来解决问题。
- 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。如果能直接劫持一个对象,就不需要递归 + 遍历了。所以 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。