zoukankan      html  css  js  c++  java
  • 红宝书4-第八章对象、类与面向对象编程(1)

    @


    1.理解对象

    这里作为理解对象的开篇,介绍了两种方式来创建对象

    第一种:通过基类 Object 来创建

    let person = new Object(); 
    person.name = "Nicholas"; 
    person.age = 29; 
    person.job = "Software Engineer"; 
    person.sayName = function() { 
     console.log(this.name); 
    };
    

    第二种:通过字面量来创建

    let person = { 
     name: "Nicholas", 
     age: 29, 
     job: "Software Engineer", 
     sayName() { 
     console.log(this.name); 
     } 
    };
    

    2. 属性的类型

    属性的类型在这里是指我们定义的属性,其实内部有一些特性来来描述规范这个属性,把这些特性分成两种:数据属性访问器属性。为了区分和特定表示这些特性,我们用中括号来表示他们,比如 [[Enumerable]]

    1.数据属性

    数据属性有 4个特性描述它们的行为。

    • [[Configurable]] :表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true,如前面的例子所示。
    • [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
    • [[Writable]]:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
    • [[Value]]:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为 undefined。

    像咱们前面那样显式的添加属性,前三个特征都会被设置成true,最后的[[value]]会被设置成特定的值。
    所以数据属性的特性包含三个判断特性,以及一个保存实际数据的储存特性。

    改变数据属性特性的两种方法

    第一种: Object.defineProperty()
    其接受三个参数,要给其添加属性的对象、属性的名称和一个描述符对象。描述符对象上的属性就是上文中的4个特性,一一对应。

    let person = {}; 
    Object.defineProperty(person, "name", { 
     writable: false, 
     value: "Nicholas" 
    }); 
    console.log(person.name); // "Nicholas" 
    person.name = "Greg"; 
    console.log(person.name); // "Nicholas"
    

    非严格模式下,给这个属性赋值会没有效果。严格模式下,则会报错。
    注意:如果我们把[[configurable]]这个特性赋值为false,这样的操作是不可逆的。再次用Object.defineProperty()修改除了writable特性以外的属性都会报错。

    因此,虽然可以对同一个属性多次调用 Object.defineProperty(),但在把 configurable 设置为 false 之后就会受限制了。
    在调用 Object.defineProperty()时,configurable、enumerable 和 writable 的值如果不指定,则都默认为 false。这里也提到了特性默认值的问题,与上面直接给对象添加属性正好是相反的。

    第二种:Object.defineProperties()

    let book = {}; 
    Object.defineProperties(book, { 
     year_: { 
     value: 2017 
     }, 
     edition: { 
     value: 1 
     }, 
     year: { 
     get() { 
     return this.year_; 
     set(newValue) { 
     if (newValue > 2017) { 
     this.year_ = newValue; 
     this.edition += newValue - 2017; 
     } } } });},```
    
    #### 2. 访问器属性
    它们包含一个**获取(getter)函数**和一个**设置(setter)函数**,不过这两个函数不是必需的。在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效的值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。访问器属性有 4 个特性描述它们的行为。
    
     
    
     - [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。 
     -  [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。 
     - [[Get]]:获取函数,在读取属性时调用。默认值为 undefined。 
     - [[Set]]:设置函数,在写入属性时调用。默认值为 undefined。
    
    **访问器属性是不能直接定义的**,必须使用 **Object.defineProperty()**。下面是一个例子:
    
    ```javascript
    // 定义一个对象,包含伪私有成员 year_和公共成员 edition 
    let book = { 
     year_: 2017, 
     edition: 1
     }; 
    Object.defineProperty(book, "year", { 
     get() { 
     return this.year_; 
     }, 
     set(newValue) { 
     if (newValue > 2017) { 
     this.year_ = newValue; 
     this.edition += newValue - 2017; 
     } 
     } 
    }); 
    book.year = 2018; 
    console.log(book.edition); // 2
    

    上面的例子就是我们定义了一个访问器属性,内部可以定义 get ,set来 自定义函数的获取值,以及设置修改属性的规则。这是访问器属性的典型使用场景,即设置一个属性值会导致一些其他变化发生。

    获取函数和设置函数不一定都要定义。只定义获取函数意味着属性是只读的,尝试修改属性会被忽略。在严格模式下,尝试写入只定义了获取函数的属性会抛出错误。类似地,只有一个设置函数的属性是不能读取的,非严格模式下读取会返回 undefined,严格模式下会抛出错误。

    在不支持 Object.defineProperty()的浏览器中没有办法修改[[Configurable]]或[[Enumerable]]。

    3.读取属性的特性

    Object.getOwnPropertyDescriptor()方法可以取得指定属性的属性描述符。这个方法接收两个参数:属性所在的对象和要取得其描述符的属性名。

    let book = {}; 
    Object.defineProperties(book, { 
     year_: { 
     value: 2017 
     }, 
     edition: { 
     value: 1 
     }, 
     year: { 
     get: function() { 
     return this.year_; 
     }, 
     set: function(newValue){ 
     if (newValue > 2017) { 
     this.year_ = newValue; 
     this.edition += newValue - 2017; 
     } 
     } 
     } 
    }); 
    let descriptor = Object.getOwnPropertyDescriptor(book, "year_"); 
    console.log(descriptor.value); // 2017 
    console.log(descriptor.configurable); // false 
    console.log(typeof descriptor.get); // "undefined" 
    let descriptor = Object.getOwnPropertyDescriptor(book, "year"); 
    console.log(descriptor.value); // undefined 
    console.log(descriptor.enumerable); // false 
    console.log(typeof descriptor.get); // "function"
    

    Object.getOwnPropertyDescriptors()静态方法,来获取所有属性的特征值。

    let book = {}; 
    Object.defineProperties(book, { 
     year_: { 
     value: 2017 
     }, 
     edition: { 
     value: 1 
     }, 
     year: { 
     get: function() { 
     return this.year_; 
     }, 
     set: function(newValue){ 
     if (newValue > 2017) { 
     this.year_ = newValue; 
     this.edition += newValue - 2017; 
     } 
     } 
     } 
    }); 
    console.log(Object.getOwnPropertyDescriptors(book)); 
    // { 
    // edition: { 
    // configurable: false, 
    // enumerable: false, 
    // value: 1, 
    // writable: false 
    // }, 
    // year: { 
    // configurable: false, 
    // enumerable: false, 
    // get: f(), 
    // set: f(newValue), 
    // }, 
    // year_: { 
    // configurable: false, 
    // enumerable: false, 
    // value: 2017, 
    // writable: false 
    // } 
    // }
    

    4.合并对象。

    Object.assign()方法。这个方法接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚(Object.propertyIsEnumerable()返回 true)自有(Object.hasOwnProperty()返回 true)属性复制到目标对象。以字符串和符号为键的属性会被复制。对每个符合条件的属性,这个方法会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值

    let dest, src, result; 
    /** 
     * 简单复制
     */ 
    dest = {}; 
    src = { id: 'src' }; 
    result = Object.assign(dest, src); 
    // Object.assign 修改目标对象
    // 也会返回修改后的目标对象
    console.log(dest === result); // true 
    console.log(dest !== src); // true 
    console.log(result); // { id: src } 
    console.log(dest); // { id: src } 
    /** 
     * 多个源对象
     */ 
    dest = {}; 
    result = Object.assign(dest, { a: 'foo' }, { b: 'bar' }); 
    console.log(result); // { a: foo, b: bar } 
    /** 
     * 获取函数与设置函数
     */ 
    dest = { 
     set a(val) { 
     console.log(`Invoked dest setter with param ${val}`); 
     } 
    }; 
    src = { 
     get a() { 
     console.log('Invoked src getter'); 
     return 'foo'; 
     } 
    }; 
    Object.assign(dest, src); 
    // 调用 src 的获取方法
    // 调用 dest 的设置方法并传入参数"foo" 
    // 因为这里的设置函数不执行赋值操作
    // 所以实际上并没有把值转移过来
    console.log(dest); // { set a(val) {...} }
    

    Object.assign()实际上对每个源对象执行的是浅复制。多个对象后面的会覆盖前面的相同的属性。此外,从源对象访问属性取得的值,比如获取函数,会作为一个静态值赋给目标对象。换句话说,不能在两个对象间转移获取函数和设置函数。
    就是如果我们设置了获取或者设置函数,如果需要触发他们就走我们自己设置的方法,没有设置就走默认的获取设置方法。

    let dest, src, result; 
    /** 
     * 覆盖属性
     */ 
    dest = { id: 'dest' }; 
    result = Object.assign(dest, { id: 'src1', a: 'foo' }, { id: 'src2', b: 'bar' }); 
    // Object.assign 会覆盖重复的属性
    console.log(result); // { id: src2, a: foo, b: bar } 
    // 可以通过目标对象上的设置函数观察到覆盖的过程:
    dest = { 
     set id(x) { 
     console.log(x); 
     } 
    }; 
    Object.assign(dest, { id: 'first' }, { id: 'second' }, { id: 'third' }); 
    // first 
    // second 
    // third 
    /** 
     * 对象引用
     */ 
    dest = {}; 
    src = { a: {} }; 
    Object.assign(dest, src); 
    // 浅复制意味着只会复制对象的引用
    console.log(dest); // { a :{} } 
    console.log(dest.a === src.a); // true
    

    如果赋值期间出错,则操作会中止并退出,同时抛出错误。Object.assign()没有“回滚”之前赋值的概念,因此它是一个尽力而为、可能只会完成部分复制的方法。

    let dest, src, result; 
    /** 
     * 错误处理
     */ 
    dest = {}; 
    src = { 
     a: 'foo', 
     get b() { 
     // Object.assign()在调用这个获取函数时会抛出错误
     throw new Error(); 
     },
      c: 'bar' 
    }; 
    try { 
     Object.assign(dest, src); 
    } catch(e) {} 
    // Object.assign()没办法回滚已经完成的修改
    // 因此在抛出错误之前,目标对象上已经完成的修改会继续存在:
    console.log(dest); // { a: foo }
    

        感谢您花时间阅读此篇文章,如果您觉得看了这篇文章之后心情还比较高兴,可以打赏一下,请博主喝上一杯咖啡,让博主继续码字……
        本文版权归作者和博客园共有,来源网址:https://blog.csdn.net/weixin_46498102 欢迎各位转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接
  • 相关阅读:
    Learning R笔记(一)
    聚类效果评测-Fmeasure和Accuracy及其Matlab实现
    R—读取数据(导入csv,txt,excel文件)
    R-模式(mode)和类(class)
    SQL Saturday 北京将于7月25日举办线下活动,欢迎参加
    开启微软机器学习之旅(1)--如何从不同数据源将样本数据导入Azure Machine Learning Studio
    微软机器学习Azure Machine Learning入门概览
    JS常用方法
    搭建django环境
    接口测试基础
  • 原文地址:https://www.cnblogs.com/jackson1/p/13778058.html
Copyright © 2011-2022 走看看