Object.defineProperty()
Object->defineProperty() 定义属性
-
(obj,prop,descriptor)括号里面有三个参数:
1、需要被定义的属性对象obj
2、定义的属性的名称
3、描述符:
即 descriptor 描述项集合 又叫配置集合 类似于:
//这样一个对象 { a:1, b:1 } -
声明一个属性
function defineProperty() { var _obj = {}; Object.defineProperty(_obj, 'a', { // 描述符第一个配置项就是value value: 1 }); return _obj } // 声明一个obj var obj = defineProperty(); // 通过上面这种方式可以将默认值定下来 console.log(obj);//{a: 1}**注:**相当于声明一个函数,在里面有一个空对象,
Object.defineProperty做的实际上就是给空对象增加一个 a 的属性,然后给其相应的配置 -
声明多个属性
function defineProperty() { var _obj = {}; // 声明多个属性 Object.defineProperties(_obj, { a: { value: 1 }, b: { value: 2 } }); return _obj; } //声明一个obj var obj = defineProperty(); //通过上面这种方式可以将默认值定下来 console.log(obj);//{a: 1, b: 2} obj.a = 5; console.log(obj);//{a:1,b:2} //发现结果的属性值不可修改 for(var k in obj){ console.log(k+':'+obj[k]); //无反应 //发现:属性也不可枚举 } delete obj.a; console.log(obj); //结果发现 不可删除 结果:{a:1,b:2} // 如果用 Object.defineProperty 的一系列的方法来对属性进行操作,发现没有用 // 结果:只要用 Object.defineProperty 定义后的属性,不可以修改,删除,枚举补充:
1、枚举:
JavaScript中不存在枚举类型,通俗的说枚举就是去拿对象的key值,能不能拿到key值,就说可不可以枚举,比如:{ a:1, b:2 } //去拿a 对应的值 1 ,能拿到说嘛可以枚举,反之,不能。 -
在操作过程中,我们总有需要修改属性的时候,那么怎么修改属性呢?
function defineProperty() { var _obj = {}; // 声明多个属性 Object.defineProperties(_obj,{ a:{ value:1, writable:true,//是否可写 enumerable:true,//是否可枚举 configurable:true//是否删除 }, b:{ value:2 } }); return _obj; } var obj = defineProperty(); //每一个对象的配置项里面有一个 writale(是否可写?)属性 默认值是flase ,修改为true obj.a=5; obj.b=6; console.log(obj);//{a: 5, b: 2} //enumerable (可否枚举) 默认值flase ,修改为true for(var k in obj){ console.log(k+':'+obj[k]); //a:5 } //configurable (是否可删除) 默认值flase ,修改为true delete obj.a; console.log(obj); //{b: 2}一般来说,在一个项目下:
conf -> 一般指文件夹
config -> 一般指文件
configurable -> 指可配置的 可操作的
-
get和set
//又比如:有一个对象obj var obj = {}; obj.a = 1; console.log(obj.a);//1 /* 实际上就是从 obj对象中中取出属性 a 的那个键值 但是我们往往觉得这样的东西很肤浅 ,不深度,往往我们在获取属性或者赋值的时候 有一些额外的操作 比如: this.data.a = 1 的时候 我们希望在视图部分 在HTML某个标签中显示'1' 原本是'0' 但是 我们一设置this.data.a = 1 的时候,这个地方需要变为'1' 需要这样的效果,应该怎么做呢? 这个时候就要用到getter 和setter 的内置方法 */ function defineProperty() { var _obj = {}; //每一个属性定义的时候 都会产生一个叫 getter 和setter 的机制。 // 这两个内置的方法本身是存在的 不写代码也存在。 // 这两个方法留给我们是做什么的呢? // 是让我们对属性进行各种操作的时候的反应,特别是对属性获取和重新赋值的时候,应该进行怎么样的程序逻辑 Object.defineProperties(_obj,{ a:{ //get get(){ }, set(newVal){ } }, b:{ } }) } -
实现get和set的使用
function defineProperty() { var _obj = {}; /* 每一个属性定义的时候,都会产生一个叫getter 和setter的机制,这两个内置的方法本身是存在的,不写代码也存在 这两个方法留给我们是做什么的呢? 是让我们对属性进行各种操作的时候的反应,特别是对属性获取和重新赋值的时候,应该进行怎么样的程序逻辑 */ Object.defineProperties(_obj,{ a:{ //get get(){ }, set(newVal){ //一设置的时候 内部的代码 a = newVal; var oP = document.getElementsByTagName('p')[0];//p标签的DOM节点 oP.innerHTML = a; } }, b:{ } }) return _obj; } //当我们一设置,就要自动的在HTML中设置上 var obj = defineProperty(); console.log(obj); obj.a = 1; //页面就会显示 1 -
数据劫持的自我理解
function defineProperty() { var _obj = {}; Object.defineProperties(_obj, { a: { //get get() { //get的时候操作 return '"a"\'s value is ' + a + '.'; }, set(newVal) { //set的时候的操作 console.log('The value "a" has been designed a new value "' + newVal + '".'); } }, b: { } }) return _obj; } //数据劫持 var obj = defineProperty(); console.log(obj.a);// 本身想返回 1 但是却 返回"a"'s value is1. obj.a = 1; //给一个属性设置值 本身不返回值 但是却返回 The value "a" has been designed a new value "1". /* 以上就是数据劫持: 对一个对象,它的取值和设置值,有一系列的配置和阻止的方法,这些东西就叫做对一组数据的属性的劫持 数据劫持:把一个对象里的属性,进行可配置,可写和可枚举的配置,通过get和set方法对存取值进行逻辑上的扩展 */ -
get、set、value、writable的理解
function defineProperty() { var _obj = {}; Object.defineProperties(_obj, { a: { value:1, writable:true, //如果此处选择声明 a,并设置value值为1 或者 writable设置为true,下面log处就会报错 //test.js:251 Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object> at Function.defineProperties /* 为什么会报错呢? 规定:value和writable任意一项的出现都不可在下面使用get或者set方法 如果a对象里面不存在get、set、value、writable,只存在enumerable和configurable就叫做纯数据描述符; 当产生了value、writable、get、set的话、就有一个特殊的要求,value和writable任意一项的出现,都不可在下面使用get或者set方法 也就是说value和writable任意一项跟get和set的任意一项不能同时出现 所以报错了 */ get() { //get的时候操作 return '"a"\'s value is ' + a + '.'; }, set(newVal) { //set的时候的操作 console.log('The value "a" has been designed a new value "' + newVal + '".'); } }, b: { } }) return _obj; } var obj = defineProperty(); console.log(obj.a); obj.a = 1; -
defineProperty对数据进行操作//defineProperty本身不具备对数组进行操作的,有没有办法对数组进行操作呢? function DataArr() { var _val = null; var _arr = [];//最后将arr抛出去,并运用方法拿到他 //也就是说每一次都需要先把val拿到,然后放到arr中,怎么做呢? //this 指向 dataArr 对象,在dataArr的下面去造一个个的属性:名称叫val Object.defineProperty(this,'val',{ get:function(){ return _val; }, set:function(newVal){ _val = newVal; _arr.push({ val:_val }); console.log('A new value "'+_val+'" has been pushed to _arr'); } }); this.getArr = function(){ return _arr; } } var dataArr = new DataArr(); dataArr.val = 123; dataArr.val = 234; console.log(dataArr.getArr()); //A new value "123" has been pushed to _arr //A new value "234" has been pushed to _arr /* (2) [{…}, {…}] 0: {val: 123} 1: {val: 234} length: 2 __proto__: Array(0) */ -
下面进行一个简单的 计算器 demo
//index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style type="text/css"> .btn-group button.current{ background-color:orange; color: #fff; } </style> </head> <body> <!-- 我们在html中有个p标签 --> <!-- <p>0</p> --> <!-- 计算器demo --> <div class="J_calculator"> <div class="result">0</div> <div class="input-group"> <input type="text" value="0" class="f-input"><br> <input type="text" value="0" class="s-input"> </div> <div class="btn-group"> <button data-field="plus" class="current">+</button> <button data-field="minus">-</button> <button data-field="mul">*</button> <button data-field="div">/</button> </div> </div> <script src="js/index.js"></script> <!-- <script src="js/test.js"></script> --> </body> </html>//index.js class Compute { plus(a,b){ return a+b; } minus(a,b){ return a - b; } mul(a,b){ return a * b; } div(a,b){ return a / b; } } class Calculator extends Compute{ constructor(doc) { super(); //拿到外层盒子 const oCal = doc.getElementsByClassName('J_calculator')[0]; //拿到两个input this.fInput = oCal.getElementsByTagName('input')[0]; this.sInput = oCal.getElementsByTagName('input')[1]; //做事件代理用 this.oBtnGroup = oCal.getElementsByClassName('btn-group')[0]; //所有button集合 this.oBtnItems = this.oBtnGroup.getElementsByTagName('button'); this.oResult = oCal.getElementsByClassName('result')[0]; this.data = this.defineData();//一组数据 this.btnIdx = 0;//button初始值 console.log(this.data); } init() { this.bindEvent(); } //事件处理函数的绑定执行 bindEvent(){ this.oBtnGroup.addEventListener('click',this.onFieldBtnClick.bind(this),false) this.fInput.addEventListener('input',this.onNumberInput.bind(this),false); this.sInput.addEventListener('input',this.onNumberInput.bind(this),false); } //数据 defineData() { let _obj = {}; let fNumber = 0; let sNumber = 0; let field = 'plus'; const _self = this;//保存外界的this指向 Object.defineProperties(_obj, { fNumber: { get() { console.log('"fNumber" is being got.'); return fNumber; }, set(newVal) { fNumber = newVal; _self.computeResult(fNumber,sNumber,field);//核心 console.log(`The value "fNumber" has been changed.[${fNumber}]`); } }, sNumber: { get() { console.log('"sNumber" is being got.'); return sNumber; }, set(newVal) { sNumber = newVal; _self.computeResult(fNumber,sNumber,field); console.log(`The value "sNumber" has been changed.[${sNumber}]`); } }, field: { get() { console.log('"field" is being got.'); return field; }, set(newVal) { field = newVal; _self.computeResult(fNumber,sNumber,field); console.log(`The value "field" has been changed.[${field}]`); } } }); return _obj; } //时间代理函数 onFieldBtnClick(ev){ const e = ev || window.event, tar = e.target || e.srcElement, tagName = tar.tagName.toLowerCase(); tagName === 'button' && this.fieldUpdate(tar); } //执行更新 fieldUpdate(target){ console.log(this.oBtnItems); this.oBtnItems[this.btnIdx].className = ''; this.btnIdx = [].indexOf.call(this.oBtnItems,target); target.className += 'current'; this.data.field = target.getAttribute('data-field'); } onNumberInput(ev){ const e = ev || window.Event, tar = e.target || e.srcElement, className = tar.className, val = Number(tar.value.replace(/\s+/g,'')) || 0; switch (className) { case "f-input": this.data.fNumber = val; break; case "s-input": this.data.sNumber = val; break; default: break; } } //两个input的值想操作,比如相加 相减 computeResult(fNumber,sNumber,field){ this.oResult.innerText = this[field](fNumber,sNumber); } } new Calculator(document).init(); //初始化