原来写文章都是一次写两三个小时写完,偶尔看到一个人的博客了解到还有草稿箱这个功能,所以以后写文章的时候就舒服多了哈哈,可以存起来再发,不需要一口气写完了

  最近一直在看JavaScript高级程序设计,看到defineProperty的时候感受挺深的,因为大名鼎鼎的Vue的双向数据绑定的原理就是根据这个东西来的,所以看到这里的时候长了见识

  要说起Object.defineProperty的话,需要先来介绍一下JavaScript中的对象。

JavaScript中的对象

  面向对象的语言有一个标志,那就是他们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。ECMAScript中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。

  JavaScript中的对象定义: 无序属性的集合,其属性可以包含基本值、对象或者函数。严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。正因为这样,可以把对象想象成散列表,就是一个键值对,其中的值可以是任意的(数据,函数,对象,数组~~)

  前面balabala一堆概念,真心感觉如果可以理解挺重要的 

创建对象

  可以使用构造函数,创建一个Object的实例,然后为其添加属性和方法

var person = new Object();
person.name = 'zhang';
person.age = 123;
person.job = "SoftWare Engineer";
person.sayName = function () {
alert(this.name);
}; person.sayName();

  这个例子创建了一个名为person的对象,并为其添加了三个属性(name、age、和job)和一个方法(sayName),其中sayName()方法用于显示this.name 这个会被解析为person.name和值。

  不过现在创建这种对象大都是通过字面量的方式来创建了

var person = {
name: 'zhang',
age: 18,
job: 'SoftWare Engineer',
sayName: function () {
alert(this.name);
}
}; person.sayName();

 这种写法的结果和上面的那一种是一样的,这种写法如果你要拓展功能的话就不能再赋值了,也是用到了上面的写法

person.home = 'hebei';
person.showHome = function () {
alert(this.home);
};

这个样子去拓展它。

属性类型

   ES5定义只有内部采用的特性时,描述了属性的各种特征。定义这些特性时为了实现JavaScript引擎用的,因此在JavaScript中不能直接访问它们。为了表示特征是内部值,该规范把它们放在了两对儿方括号中,例如[[Enumerable]]

  前面例子中的name,age,job,home,showHome都是属性,引号后面的东西是值,说一下属性的一些东西。

  ECMAScript中有两种属性: 数据属性和访问器属性

数据属性

  数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。

  [[Configurable]] 表示能否通过delete删除属性从而重新定义属性,能够通过属性的特性,或者能否把属性修改为访问器属性,像前面的例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。

  [[Enumerable]] 表示能否通过for-in循环返回属性。像前面例子中那样直接在对象上定义的属性,他们的这个特性默认值为true

  [[Writable]] 表示能够修改属性的值。像前面例子中那样直接在对象上定义的属性,他们的这个特性默认值为true

  [[Value]] 包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined

例子

var person = {
name: 'zhang'
}; for(var key in person){
console.log(key);
}
// 他这个属性的value值就是他在上面所定义的那一些,对这个值的任何修改都反映在这个位置
console.log(person.name); person.name = 'goudan';
// 在这里也可以修改,因为[[Writable]]属性默认为true
console.log(person.name); // [[Configurable]] 这个东西为true 表示也可以删除它
delete person.name;
console.log(person.name); // undefined 因为删除之后这个属性就不存在了 [[Value]]特性的默认值为undefined

重点来了

Object.defineProperty()

  如果要修改 属性默认的特性 就必要要使用ES5的Object。defineProperty()方法。这个方法接收三个参数。

  属性所在的对象、 属性的名字、 一个描述符对象 (其中描述符对象的属性必须是: configurable、enumerable、writeable、和value)设置其中一个或多个值,可以修改对应的特性值

  下面去演示这些例子了,希望你也可以试着去尝试一遍,我举得例子写法可能比较啰嗦,希望没有尝试过的可以试着练习一下

  提示:我后面 // 注释后面的东西 是输出打印的东西,如有错误可以自己具体情况分析一下

writable的情况演示

var person = {
name: 'zhang',
age: 12
};
Object.defineProperty(person,"name",{
writable: false
});
// 读取person.name
console.log(person.name); // zhang
console.log(person.age); //12
// 修改person.name
person.name = 'wang';
person.age = 14;
console.log(person.name); // zhang ?????? 咋没变
console.log(person.age); // 14
// 原因: 那个配置里面也说了的[[Writable]]默认是true,但是改为false之后 就是这个属性不可写,没有写进去的也可以看到是可以正常修改的

  

enumerable的情况演示

var person = {
name: 'zhang',
baba: '1',
job: 'Software Engineer'
}; for(var key in person){
console.log(key); // name, baba, job
}
// 三个属性都可以正常的打印出来
// 配置一下 enumerable
Object.defineProperty(person,'baba',{
enumerable: false
}); for(var key1 in person){
console.log(key1); // name job
}
// ??? baba去哪了? 我告诉你去哪了 因为配置了 enumerable: false
console.log(person.baba); // 1 不会影响输出

  

Configurable 的演示

var person = {
name: 'zhang',
age: 12
}; Object.defineProperty(person,'name',{
configurable: false
}); console.log(person.name); // zhang
console.log(person.age); // 12 delete person.name;
console.log(person.name); // zhang
// 刚才删除不是undefined吗 configurable: false就不允许删除了
// 如果声明是严格模式的话 那一句 delete person.name 还会报错
person.name = 'wang'
console.log(person.name); // wang

注意:这个配置有一点需要注意一下

var person = {
name: 'zhang'
};
Object.defineProperty(person,'name',{
configurable: false
});
// 如果以后你有需求你还想删除他的话 你会想到
Object.defineProperty(person,'name',{
configurable: true
});
// 控制台打印错误消息: Uncaught TypeError: Cannot redefine property: name at Function.defineProperty (<anonymous>)
// 这个东西一旦被配置为 false,以后在也不可以把他变为true了 会报错

Value的演示

var person = {
name: 'zhang'
};
console.log(person.name); // zhang
Object.defineProperty(person,'name',{
value: 'wang'
});
console.log(person.name); // wang
// 你小子怎么改姓了
person.name = 'zhang'; // 给我改回来
console.log(person.name); //zhang 又是我老张家的孩子了,这个是可以修改回来的

当然了上面的属性只是单独演示的, 这四个属性可以结合起来使用,比如我要定义一个永远都无法改变它的值

var person = {
name: 'zhang'
};
console.log(person.name); // zhang
Object.defineProperty(person,'name',{
writable: false,
value: 'wang'
}); console.log(person.name); // wang
// 卧槽你个小兔崽子又改姓了 给我改回来
person.name = 'zhang';
console.log(person.name); // wang
// 你会发现 有些事情发生了就是发生了,永远也回不来了
person.name = 'zhang1';
console.log(person.name); // wang

  注意 在调用 Object.defineProperty()方法时,如果不指定,configurable、enumerable和writable特性的默认值都是true。

  你给我演示这一堆东西到底有啥意思,能干啥。我想告诉你多数情况下可能都没有必要利用Object.defineProperty()方法提供的这些高级功能。不过,理解这些概念对理解JavaScript对象却非常有用

访问器属性

  访问器属性不包含数据值;他们包含一对儿getter和setter函数(不过这两个函数都不是必须的)在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下4个特性  

[[Configurable]] : 能否修改属性的特性,或者能否把属性修改为数据属性。像前面的例子中那样直接在对象上定义的属性,它们的这个特性默认值为true

[[Enumerable]] : 能否通过for-in循环 *********和上面的一样
[[Get]]: 在读取属性的时候调用。 默认值 undefined
[[Set]]: 在写入属性的时候调用 默认值 undefined

前面两个属性大同小异,几乎是一样的,同样的访问器属性不能直接定义,必须使用Object.defineProperty()来定义.

get和set的演示,

  这个重要

var book = {
_year: 2018,
edition: 1
}; Object.defineProperty(book,'year',{
get: function () {
console.log(`获取的时候触发函数`); // 每次获取的时候都会触发这个函数
console.log(this === book); // true 这里面的this就是book的引用 也就是book对象
return this._year;
},
set: function (newValue) {
console.log('修改值的时候触发函数');
// newValue ----- 这里的newValue就是你设置的修改后的值
if(newValue> 2018){
this._year = newValue;
this.edition = newValue - 2018;
}
}
}); console.log(book.year);
// 2018 他的值也就是我们返回的值 book._year
// 如果get函数不写返回值就是undefined book.year = 2020; // 修改值的时候触发函数
console.log(book); // {_year: 2019, edition: 2};

  vue双向数据绑定就是这个,当我们在修改数据的时候,会触发set那个函数,就在set函数中执行我们的逻辑,去修改数据,以后我会写一个大概的双向数据绑定的那个代码,会发出来,网上也有好多类似的代码。

上面的代码创建了一个book对象,并给他定义两个默认的属性: _year和edition。

  而访问器属性year则包含一个getter函数和一个setter函数。 getter函数返回_year的值,setter函数修改了_year和edition。这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。

  不一定非要同时制定getter和setter。只指定getter意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入至指定了getter函数的属性会抛出错误。类似的,只指定setter函数的属性也不能读,否则在非严格模式下回返回undefined,而在严格模式下会抛出错误。

  同时定义多个属性的写法由于未对象定义多个属性的可能性很大,ES5又定义了一个Object.defineProperties()方法利用这个方法可以通过描述符一次定义多个属性。

  这个方法接收两个对象参数:第一个对象是哟啊添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。

同时定义多个属性的写法

var book = {};

Object.defineProperties(book,{
_year: {
value: 2018
},
edition: {
value: 1
},
year: {
get: function () {
return this._year;
},
set: function (newValue) {
console.log('修改函数触发触发了吗');
if(newValue>2018){
this._year = newValue;
this.edition += newValue - 2018;
}
}
}
}); console.log(book.year); book.year = 2020;
console.log(book); //{_year: 2018, edition: 1}
// ???? 我前面不是触发了修改的函数了吗为什么没有改呢 上一个分开写的都修改成功了

  

别着急,下面来看一个新的API

Object.getOwnPropertyDescriptor()

  读取属性的特性

使用Es5的Object.getOwnPropertyDescription() 方法,可以取得给定属性描述符。这个方法接收两个参数: 属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable enumerable writable 和value

var descriptor1 = Object.getOwnPropertyDescriptor(book,"_year");
var descriptor2 = Object.getOwnPropertyDescriptor(book,"edition");
console.log(descriptor1);
/*
{configurable: false,
enumerable: false,
value: 2018,
writable: false}
*/
console.log(descriptor2);
/*
{
configurable: false,
enumerable: false,
value: 1,
writable: false}
*/
*/
// 前面分开写可以成功的原因可以用这个东西去获取一下,可以看到他们是true的,这个是false,所以
// 虽然你修改了,但是他是不生效的

  在JavaScript中,可以针对任何对象------包括DOM和BOM对象,使用Object.getOwnPropertyDescriptor()方法。

  如果你阅读了本文章有了一些收获,我会感到非常开心,由于能力有限,文章有的部分解释的不到位,希望在以后的日子里能慢慢提高自己能力,如果不足之处,还望指正。

深入了解Object.defineProperty的更多相关文章

  1. javascript之Object.defineProperty的奥妙

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

  2. Object.defineproperty实现数据和视图的联动

    Object.defineproperty语法 var o = {}; // 创建一个新对象 // Example of an object property added with definePro ...

  3. Vue 双向数据绑定原理分析 以及 Object.defineproperty语法

    第三方精简版实现 https://github.com/luobotang/simply-vue Object.defineProperty 学习,打开控制台分别输入以下内容调试结果 userInfo ...

  4. Object.defineProperty vs __defineGetter__ vs normal

    Testing in Chrome 31.0.1650.63 32-bit on Windows Server 2008 R2 / 7 64-bit Test Ops/sec Object.defin ...

  5. Object.defineProperty

    属性类型ECMA-262第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征.ECMA-262定义这些特性是为了实现JavaScript引擎用的,因此在Ja ...

  6. Object.defineproperty实现数据和视图的联动 ------是不是就是 Angular 模型和视图的同步的实现方式???

    参考:http://www.cnblogs.com/oceanxing/p/3938443.html https://developer.mozilla.org/zh-CN/docs/Web/Java ...

  7. 20+行代码使用es5 Object.defineProperty 实现简单的watch功能

    /** * 一个简单的demo 帮助理解defineProperty,只对Object类型参数有效 */ $watch=function(myObject,callback){ function in ...

  8. Object.defineProperty()方法的用法详解

    Object.defineProperty()函数是给对象设置属性的. Object.defineProperty(object, propertyname, descriptor); 一共有三个参数 ...

  9. 理解Object.defineProperty()

    理解Object.defineProperty() Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象. 基本语法:Obj ...

  10. 利用object.defineProperty实现数据与视图绑定

    如今比较火的mvvm框架,例如vue就是利用es5的defineProperty来实现数据与视图绑定的,下面我来介绍一下defineProperty的用法. var people= {} Object ...

随机推荐

  1. CSS3禁止用户选中文字——user-select: none;

    需求:现在有一个需求是在移动端让你禁止用户选中文字,你会怎么做呢?如下图    解决方法:使用CSS3新增属性 user-select: none;   -webkit-user-select:non ...

  2. Array.find()和Array.findIndex()

    ES6新增的两个方法,根据回调函数返回作为判断依据,按照数组顺序进行遍历,符合条件(为真)时find()返回该值.findIndex()返回下标. 1.语法 arr.find(callback[, t ...

  3. [Swift]LeetCode402. 移掉K位数字 | Remove K Digits

    Given a non-negative integer num represented as a string, remove k digits from the number so that th ...

  4. [Swift]LeetCode768. 最多能完成排序的块 II | Max Chunks To Make Sorted II

    This question is the same as "Max Chunks to Make Sorted" except the integers of the given ...

  5. MySQL 规范及优化

    一.建库建表优化 1.核心规范(推荐) 表字符集选择UTF8 (“表情”字段单独设置为其他字符集) 存储引擎使用INNODB 不在库中存储图片.文件等 使用可变长字符串(varchar) 每张表数据量 ...

  6. 关于video标签移动端开发遇到的问题,获取视频第一帧,全屏,自动播放,自适应等问题

    最近一直在处理video标签在IOS和Android端的兼容问题,其中遇到不少坑,绝大多数问题已经解决,下面是处理问题经验的总结: 1.获取视频的第一帧作为背景图: 技术:canvas绘图 windo ...

  7. 强如 Disruptor 也发生内存溢出?

    前言 OutOfMemoryError 问题相信很多朋友都遇到过,相对于常见的业务异常(数组越界.空指针等)来说这类问题是很难定位和解决的. 本文以最近碰到的一次线上内存溢出的定位.解决问题的方式展开 ...

  8. JDK1.8源码(三)——java.util.HashMap

      什么是哈希表? 在讨论哈希表之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能 数组:采用一段连续的存储单元来存储数据.对于指定下标的查找,时间复杂度为O(1):通过给定值进行查找, ...

  9. Android软键盘事件imeOptions响应

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 在android发开过程中,有时候需要对EditText的软键盘进行监听. 当点击软键盘回车位置按键的时候,需要实现 完成.前进.下 ...

  10. C#2.0 委托

    委托 委托是一个非常不错的设计,允许我们把方法做为参数传递,实现了开放閉放原则.在方法中我们只要有一个委托占位,调用者就可以传入符合签名的方法来做不同的操作,这也面向对象开发中多态的魅力. 但是在C# ...