ECMAS-262第5版在定义只有内部采用的特性时,提供了描述了属性特征的几种属性。ECMAScript对象中目前存在的属性描述符主要有两种,数据描述符(数据属性)和存取描述符(访问器属性),数据描述符是一个拥有可写或不可写值的属性。存取描述符是由一对 getter-setter 函数功能来描述的属性。

Object的definePropertydefineProperties这两个方法在js中的重要性十分重要,主要功能就是用来定义或修改这些内部属性,与之相对应的getOwnPropertyDescriptorgetOwnPropertyDescriptors就是获取这行内部属性的描述。

数据(数据描述符)属性

数据属性有4个描述内部属性的特性

Configurable

表示能否通过delete删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true

Enumerable

表示该属性是否可枚举,即是否通过for-in循环或Object.keys()返回属性,如果直接使用字面量定义对象,默认值为true

Writable

能否修改属性的值,如果直接使用字面量定义对象,默认值为true

Value

该属性对应的值,默认为undefined

访问器(存取描述符)属性

访问器属性也有4个描述内部属性的特性

Configurable

和数据属性的[[Configurable]]一样,表示能否通过delete删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true

Enumerable

和数据属性的[[Configurable]]一样,表示该属性是否可枚举,即是否通过for-in循环或Object.keys()返回属性,如果直接使用字面量定义对象,默认值为true

Get

一个给属性提供 getter 的方法(访问对象属性时调用的函数,返回值就是当前属性的值),如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined

Set

一个给属性提供 setter 的方法(给对象属性设置值时调用的函数),如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined

创建/修改/获取属性的方法

Object.defineProperty()

功能:
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,
并返回这个对象。如果不指定configurable, writable, enumerable
,则这些属性默认值为false,如果不指定value, get, set,则这些属性默认值为undefined

语法: Object.defineProperty(obj, prop, descriptor)

obj: 需要被操作的目标对象
prop: 目标对象需要定义或修改的属性的名称
descriptor: 将被定义或修改的属性的描述符

var obj = new Object();

Object.defineProperty(obj, 'name', {
configurable: false,
writable: true,
enumerable: true,
value: '张三'
}) console.log(obj.name) //张三

Object.defineProperties()

功能:
方法直接在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象。

语法: Object.defineProperties(obj, props)

obj: 将要被添加属性或修改属性的对象
props: 该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置

var obj = new Object();
Object.defineProperties(obj, {
name: {
value: '张三',
configurable: false,
writable: true,
enumerable: true
},
age: {
value: 18,
configurable: true
}
}) console.log(obj.name, obj.age) // 张三, 18

Object.getOwnPropertyDescriptor()

功能:
该方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

语法: Object.getOwnPropertyDescriptor(obj, prop)

obj: 需要查找的目标对象
prop: 目标对象内属性名称

var person = {
name: '张三',
age: 18
} var desc = Object.getOwnPropertyDescriptor(person, 'name');
console.log(desc) 结果如下
// {
// configurable: true,
// enumerable: true,
// writable: true,
// value: "张三"
// }

Object. getOwnPropertyDescriptors()

功能:
所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

语法: Object.getOwnPropertyDescriptors(obj)

obj: 需要查找的目标对象

var person = {
name: '张三',
age: 18
}
var desc = Object.getOwnPropertyDescriptors(person, 'name');
console.log(desc) // 结果如下图

各种场景下描述符属性的的扩展示例讲解

configrubale

如果设置configrubale属性为false,则不可使用delete操作符(在严格模式下抛出错误), 修改所有内部属性值会抛出错误,在《javaScript高级教程中》说只可以改变writable的值,现在改变writable的值也会抛出错误

在对象中添加一个数据描述符属性

var person = {};

Object.defineProperty(person, 'name', {
configurable: false,
value: 'John'
}) ; delete person.name // 严格模式下抛出错误 console.log(person.name) // 'John' 没有删除 Object.defineProperty(person, 'name', {
configurable: true //报错
}); Object.defineProperty(person, 'name', {
enumerable: 2 //报错
}); Object.defineProperty(person, 'name', {
writable: true //报错
}); Object.defineProperty(person, 'name', {
value: 2 //报错
});

注意:
以上是最开始定义属性描述符时,writabl默认为false,才会出现上述效果,如果writable定义为true, 则可以修改[[writable]]和[[value]]属性值,修改另外两个属性值报错

var obj = {};

Object.defineProperty(obj, 'a', {
configurable: false,
writable: true,
value: 1
}); Object.defineProperty(obj, 'a', {
// configurable: true, //报错
// enumerable: true, //报错
writable: false,
value: 2
});
var d = Object.getOwnPropertyDescriptor(obj, 'a')
console.log(d);
// {
// value: 2,
// writable: false,
// }

在对象中添加存取描述符属性

var obj = {};
var aValue; //如果不初始化变量, 不给下面的a属性设置值,直接读取会报错aValue is not defined
var b;
Object.defineProperty(obj, 'a', {
configurable : true,
enumerable : true,
get: function() {
return aValue
},
set: function(newValue) {
aValue = newValue;
b = newValue + 1
}
})
console.log(b) // undefined
console.log(obj.a) // undefined, 当读取属性值时,调用get方法,返回undefined
obj.a = 2; // 当设置属性值时,调用set方法,aValue为2 console.log(obj.a) // 2 读取属性值,调用get方法,此时aValue为2
console.log(b) // 3 再给obj.a赋值时,执行set方法,b的值被修改为2,额外说一句,vue中的计算属性就是利用setter来实现的

注意:
1.getter和setter可以不同时使用,但在严格模式下只其中一个,会抛出错误
2.数据描述符与存取描述符不可混用,会抛出错误

var obj = {};
Object.defineProperty(obj, 'a', {
value: 'a1',
get: function() {
return 'a2'
}
});

3.全局环境下:

var a = 1;  // a属于window, 相当于window.a

让我们来看看a的描述符属性

var d = Object.getOwnPropertyDescriptor(window, 'a');
console.log(d)
// {
// configurable: false,
// value: 1,
// writable: true,
// enumerable: true
// }

在来看一下另一种不适用var声明的方式初始化a变量

a = 1; //a相当于window的一个属性, window.a

再来看看此时a的描述符属性

var d = Object.getOwnPropertyDescriptor(window, 'a');
console.log(d)
// {
// configurable: true, // 此时configurable属性值为true
// value: 1,
// writable: true,
// enumerable: true
// }

注意:

只有使用var, let等操作符才是定义变量,而不使用var,直接a=1;,这样a的含义为window的一个属性,并不是我们所说的变量的概念。使用 var定义的任何变量,其configurable属性值都为false,定义对象也是一样

var b = {
name: 'bbb'
}
var d = Object.getOwnPropertyDescriptor(window, 'b');
console.log(d)
// {
// configurable: false
// value: 1,
// writable: true,
// enumerable: true
// }

但是这里需要说明的一点是,使用字面量定义的对象,该对象内部的属性的数据描述符属性都为true

var b = {
name: 'bbb'
}
var d = Object.getOwnPropertyDescriptor(b, 'name');
console.log(d)
// {
// configurable: true
// writable: true,
// enumerable: true
// value: 'bbb'
// }

Writable

当writable为false(并且configrubale为true),[[value]]可以通过defineeProperty修改, 但不能直接赋值修改

var obj = {};

Object.defineProperty(obj, 'a', {
configurable: true,
enumerable: false,
writable: false,
value: 1
}); Object.defineProperty(obj, 'a', {
configurable: false,
enumerable: true,
writable: false ,
value: 2
});
var d = Object.getOwnPropertyDescriptor(obj, 'a') console.log(d); // 结果如下
// {
// value: 2,
// writable: false,
// enumerable: true,
// configurable: false
// } 但是如果直接复制修改
var obj = {} Object.defineProperty(obj, 'a', {
configurable: true,
enumerable: false,
writable: false,
value: 1
});
obj.a=2;
var d = Object.getOwnPropertyDescriptor(obj, 'a') console.log(d); // 结果如下 // {
// value: 1, // 没有做出修改
// writable: false,
// enumerable: true,
// configurable: false
// }

Enumerable

直接上例子

var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
enumerable: false
},
b: {
value: 2,
enumerable: true
},
c: {
value: 3,
enumerable: false
}
}) obj.d = 4; //等同于 //Object.defineProperty(obj, 'd', {
// configurable: true,
// enumerable: true,
// writable: true,
// value: 4
//}) for(var key in obj) {
console.log(key);
// 打印一次b, 一次d, a和c属性enumerable为false,不可被枚举
} var arr = Object.keys(obj);
console.log(arr); // ['b', 'd']

get和set

简易的数据双向绑定

html代码:

<body>
<p>
input1=><input type="text" id="input1">
</p>
<p>
input2=>
<input type="text" id="input2">
</p>
<div>
我每次比input1的值加1=>
<span id="span"></span>
</div>
</body>

js代码:

var oInput1 = document.getElementById('input1');
var oInput2 = document.getElementById('input2');
var oSpan = document.getElementById('span');
var obj = {};
Object.defineProperties(obj, {
val1: {
configurable: true,
get: function() {
oInput1.value = 0;
oInput2.value = 0;
oSpan.innerHTML = 0;
return 0
},
set: function(newValue) {
oInput2.value = newValue;
oSpan.innerHTML = Number(newValue) ? Number(newValue) : 0
}
},
val2: {
configurable: true,
get: function() {
oInput1.value = 0;
oInput2.value = 0;
oSpan.innerHTML = 0;
return 0
},
set: function(newValue) {
oInput1.value = newValue;
oSpan.innerHTML = Number(newValue)+1;
}
}
})
oInput1.value = obj.val1;
oInput1.addEventListener('keyup', function() {
obj.val1 = oInput1.value;
}, false)
oInput2.addEventListener('keyup', function() {
obj.val2 = oInput2.value;
}, false)

总结

虽然我们在开过过程中不怎么使用几种方法,但理解之后对于我们理解js中对象有很大帮助,对后续进步也很有帮助,比如vue的实现原理等。个人能力有限,还希望大家发现问题后能多多指点,共同进步。

js中的Object.defineProperty()和defineProperties()详解的更多相关文章

  1. Angular.js中处理页面闪烁的方法详解

    Angular.js中处理页面闪烁的方法详解 前言 大家在使用{{}}绑定数据的时候,页面加载会出现满屏尽是{{xxx}}的情况.数据还没响应,但页面已经渲染了.这是因为浏览器和angularjs渲染 ...

  2. Js中的Object.defineProperty

    通过Object.defineProperty为对象设置属性,并同时规定属性的属性(可见性,可配置性,可枚举性等) 备注:如果通过var obj = {} obj.age = 18这种方式设置的属性, ...

  3. Js中this用法及注意点详解

          我们在写js时,特别是用到回调函数时,经常会发现this指代的对象总是可能脱离自己的思路而发生改变.面向对象语言的特性告诉我们this始终指代它的调用者,而在js中回调函数中内部的this ...

  4. node.js 中模块的循环调用问题详解

    首先,我们看一下图示代码,每一个注释其实代表一个 js 文件.所以下面其实是三个 js 文件 .第一个是我们要运行的 main 文件,后面两个是 a, b 文件.   从上面可以看书 a ,b 两个模 ...

  5. js中toggle()及toggleClass()的使用详解

    在javascript中toggle()为连续点击事件,当里面含有多个function(){}函数时,每次点击依次执行里面的function的函数,直至最后一个.随后每次点击都重复对这几个函数的轮番调 ...

  6. JS中的事件委托/事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  7. js中prototype与__proto__的关系详解

    一.构造函数: 构造函数:通过new关键字可以用来创建特定类型的对象的函数.比如像Object和Array,两者属于内置的原生的构造函数,在运行时会自动的出现在执行环境中,可以直接使用.如下: var ...

  8. js中事件冒泡,事件捕获详解

    一.事件流 事件是js与HTML交互的基础,事件流描述的是页面接受事件的顺序,而事件流又分为三个阶段:捕获阶段.目标阶段和冒泡阶段. 如果单纯的事件处理,事件捕获和事件冒泡二选一即可,导致两者并存的原 ...

  9. js中Object.defineProperty()和defineProperties()

    在介绍js中Object.defineProperty()和defineProperties()之前,我们了解下js中对象两种属性的类型:数据属性和访问器属性. 数据属性 数据属性包含一个数据的位置, ...

随机推荐

  1. 【转】利用matlab生成随机数函数

    原文地址:利用matlab生成随机数函数 rand(n):生成0到1之间的n阶随机数方阵  rand(m,n):生成0到1之间的m×n的随机数矩阵 (现成的函数) betarnd:贝塔分布的随机数生成 ...

  2. jenkins插件安装与升级[三]

    标签(linux): jenkins 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 默认的插件 Folders Plugin OWASP Markup Form ...

  3. HTML学习——表单标签

    1.type: 当 type="radio" 时,控件为单选框 当 type="checkbox" 时,控件为复选框 2.value:提交数据到服务器的值(后台 ...

  4. 树莓派小车By 树莓派爱好者ITJoker(通过python socket通信实现树莓派视频小车)(一)

    本文由树莓派爱好者ITJoker 编辑,转载请注明出处.本人也有新浪博客同样是树莓派爱好者ITJoker 所需材料:树莓派2B或者2B以上,L2985n驱动板,若干排线,电池及电池盒,usb无线网卡( ...

  5. 小白入门Docker基础篇

    docker是什么 百科上的解释是这样的: Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化 ...

  6. mysql将查询出来的一列数据拼装成一个字符串

    使用GROUP_CONCAT函数. SELECT GROUP_CONCAT(查询的字段 separator ',') FROM table

  7. Ubuntu14.04 安装vmware虚拟机

    下载VMware 链接:VMware 14  密码:5okh 移动VMware14 到 /opt #mv VMware-Workstation-Full-14.0.0-6661328.x86_64.b ...

  8. 分组密码的工作模式--wiki

    密码学中,块密码的工作模式允许使用同一个块密码密钥对多于一块的数据进行加密,并保证其安全性.[1][2] 块密码自身只能加密长度等于密码块长度的单块数据,若要加密变长数据,则数据必须先被划分为一些单独 ...

  9. JDK,JRE,JVM的区别与联系

    JDK : Java Development ToolKit(Java开发工具包).JDK是整个JAVA的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工 ...

  10. HDU D Tree [点分治]

    传送门 题意:找路径积$\mod 1e6+3 = k$的字典序最小点对 作为一个点分治蒟蒻,写这道题花了两节课.... 显然只要开一个桶$c[i]$记录当前路径积为$i$的最小点 然后处理一个子树时一 ...