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

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

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

JavaScript中的对象

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

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

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

创建对象

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

  1. var person = new Object();
  2. person.name = 'zhang';
  3. person.age = 123;
  4. person.job = "SoftWare Engineer";
  5. person.sayName = function () {
  6. alert(this.name);
  7. };
  8.  
  9. person.sayName();

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

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

  1. var person = {
  2. name: 'zhang',
  3. age: 18,
  4. job: 'SoftWare Engineer',
  5. sayName: function () {
  6. alert(this.name);
  7. }
  8. };
  9.  
  10. person.sayName();

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

  1. person.home = 'hebei';
  2. person.showHome = function () {
  3. alert(this.home);
  4. };

这个样子去拓展它。

属性类型

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

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

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

数据属性

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

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

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

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

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

例子

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

重点来了

Object.defineProperty()

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

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

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

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

writable的情况演示

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

  

enumerable的情况演示

  1. var person = {
  2. name: 'zhang',
  3. baba: '1',
  4. job: 'Software Engineer'
  5. };
  6.  
  7. for(var key in person){
  8. console.log(key); // name, baba, job
  9. }
  10. // 三个属性都可以正常的打印出来
    // 配置一下 enumerable
  11. Object.defineProperty(person,'baba',{
  12. enumerable: false
  13. });
  14.  
  15. for(var key1 in person){
  16. console.log(key1); // name job
  17. }
  18. // ??? baba去哪了? 我告诉你去哪了 因为配置了 enumerable: false
  19. console.log(person.baba); // 1 不会影响输出

  

Configurable 的演示

  1. var person = {
  2. name: 'zhang',
  3. age: 12
  4. };
  5.  
  6. Object.defineProperty(person,'name',{
  7. configurable: false
  8. });
  9.  
  10. console.log(person.name); // zhang
  11. console.log(person.age); // 12
  12.  
  13. delete person.name;
  14. console.log(person.name); // zhang
  15. // 刚才删除不是undefined吗 configurable: false就不允许删除了
  16. // 如果声明是严格模式的话 那一句 delete person.name 还会报错
  17. person.name = 'wang'
  18. console.log(person.name); // wang

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

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

Value的演示

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

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

  1. var person = {
  2. name: 'zhang'
  3. };
  4. console.log(person.name); // zhang
  5. Object.defineProperty(person,'name',{
  6. writable: false,
  7. value: 'wang'
  8. });
  9.  
  10. console.log(person.name); // wang
  11. // 卧槽你个小兔崽子又改姓了 给我改回来
  12. person.name = 'zhang';
  13. console.log(person.name); // wang
  14. // 你会发现 有些事情发生了就是发生了,永远也回不来了
  15. person.name = 'zhang1';
  16. 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的演示,

  这个重要

  1. var book = {
  2. _year: 2018,
  3. edition: 1
  4. };
  5.  
  6. Object.defineProperty(book,'year',{
  7. get: function () {
  8. console.log(`获取的时候触发函数`); // 每次获取的时候都会触发这个函数
  9. console.log(this === book); // true 这里面的this就是book的引用 也就是book对象
  10. return this._year;
  11. },
  12. set: function (newValue) {
  13. console.log('修改值的时候触发函数');
  14. // newValue ----- 这里的newValue就是你设置的修改后的值
  15. if(newValue> 2018){
  16. this._year = newValue;
  17. this.edition = newValue - 2018;
  18. }
  19. }
  20. });
  21.  
  22. console.log(book.year);
  23. // 2018 他的值也就是我们返回的值 book._year
  24. // 如果get函数不写返回值就是undefined
  25.  
  26. book.year = 2020; // 修改值的时候触发函数
  27. 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()方法利用这个方法可以通过描述符一次定义多个属性。

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

同时定义多个属性的写法

  1. var book = {};
  2.  
  3. Object.defineProperties(book,{
  4. _year: {
  5. value: 2018
  6. },
  7. edition: {
  8. value: 1
  9. },
  10. year: {
  11. get: function () {
  12. return this._year;
  13. },
  14. set: function (newValue) {
  15. console.log('修改函数触发触发了吗');
  16. if(newValue>2018){
  17. this._year = newValue;
  18. this.edition += newValue - 2018;
  19. }
  20. }
  21. }
  22. });
  23.  
  24. console.log(book.year);
  25.  
  26. book.year = 2020;
  27. console.log(book); //{_year: 2018, edition: 1}
  28. // ???? 我前面不是触发了修改的函数了吗为什么没有改呢 上一个分开写的都修改成功了

  

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

Object.getOwnPropertyDescriptor()

  读取属性的特性

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

  1. var descriptor1 = Object.getOwnPropertyDescriptor(book,"_year");
  2. var descriptor2 = Object.getOwnPropertyDescriptor(book,"edition");
  3. console.log(descriptor1);
  4. /*
  5. {configurable: false,
  6. enumerable: false,
  7. value: 2018,
  8. writable: false}
  9. */
  10. console.log(descriptor2);
  11. /*
  12. {
  13. configurable: false,
  14. enumerable: false,
  15. value: 1,
  16. writable: false}
  17. */
  18. */
  19. // 前面分开写可以成功的原因可以用这个东西去获取一下,可以看到他们是true的,这个是false,所以
  20. // 虽然你修改了,但是他是不生效的

  在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. [Swift]LeetCode926. 将字符串翻转到单调递增 | Flip String to Monotone Increasing

    A string of '0's and '1's is monotone increasing if it consists of some number of '0's (possibly 0), ...

  2. 必须知道的Java八大排序算法

    冒泡排序.简单选择.直接插入.快速排序.堆排序.希尔排序.归并排序.基数排序. 将其按排序方式分类如下图所示: 1.冒泡排序: 基本思想——在要排序的一组数中,对当前还未排好序的范围内的全部数据,自上 ...

  3. iOS学习——页面的传值方式

    一.简述 在iOS开发过程中,页面跳转时在页面之间进行数据传递是很常见的事情,我们称这个过程为页面传值.页面跳转过程中,从主页面跳转到子页面的数据传递称之为正向传值:反之,从子页面返回主页面时的数据传 ...

  4. (转)浅谈Session与Cookie的区别与联系

    一.Session的概念 Session 是存放在服务器端的,类似于Session结构来存放用户数据,当浏览器 第一次发送请求时,服务器自动生成了一个Session和一个Session ID用来唯一标 ...

  5. 使用vue+ivew做2048小游戏

    首先先弄页面 废话不多说 上代码 静态页面代码 <template> <div class="main"> <div class="top& ...

  6. 谷歌浏览器的各种插件网址Chrome插件(谷歌浏览器)-超级详细

    各种各样的插件,可以查找对自己有用的,自行下载安装 http://chromecj.com/

  7. asp.net core 系列 15 中间件

    一.概述 中间件(也叫中间件组件)是一种装配到应用管道以处理请求和响应的软件. 每个组件:(1)选择是否将请求传递到管道中的下一个组件;(2)可以在管道中的下一个组件之前和之后执行工作. 请求委托用于 ...

  8. exec族函数详解及循环创建子进程

    前言:之前也知道exec族函数,但没有完全掌握,昨天又重新学习了一遍,基本完全掌握了,还有一些父子进程和循环创建子进程的问题,还要介绍一下环境变量,今天分享一下. 一.环境变量 先介绍下环境的概念和特 ...

  9. 初探WebAssembly

    1.前言 参加完2018年上海的QCon大会,想到了会议中来自Microsoft的朱力旻大佬讲的WebAssembly,感触颇深. 我之前完全没有了解过WebAssembly,之前没有了解的原因也很简 ...

  10. Spring Boot分布式系统实践【1】-架构设计

    前言 [第一次尝试去写一个系列,肯定会有想不到的地方,欢迎大家留言指正] 本系列将介绍如果从零构建一套分布式系统.同时也是对自己过去工作的一个梳理过程. 本文先整理出构建系统的主要技术选型,以及技术框 ...