javascript-红宝书第8章Object部分笔记

loading 2022年10月23日 111次浏览

1. Object.defineProperty()

1.1 数据属性

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

  • [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。
  • [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。
  • [[Writable]]:表示属性的值是否可以被修改。
  • [[Value]]:包含属性实际的值。

如果是通过对象字面量将属性显式添加到对象,以上属性都会被设置为true。

而通过defineProperty()来定义的话,以上属性默认为false。

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

        console.log(person.name); // "Nicholas" 
        console.log(person.age); //15

        person.name = "Greg";
        person.age = 25;

        console.log(person.name); //25
        console.log(person.age);// "Nicholas"

重写某个函数的prototype时,会造成constructor的丢失,可以通过defineProperty来重写构造函数。
比如这样,会导致friend.constructor指向Object而非本该指向的Person()

        function Person() { }
        Person.prototype = {
            name: "Nicholas",
            age: 29,
            job: "Software Engineer",
            sayName() {
                console.log(this.name);
            }
        };
	
	let friend = new Person();

因为原生constructor是不可枚举的,所以要通过defineProperty来设置enumerable为false,否则在对象字面量中直接创建constructor会默认可枚举。

        Object.defineProperty(Person.prototype, "constructor", {
            enumerable: false,
            value: Person
        }); 

1.2 访问器属性

访问器属性不包含数据值。相反,它们包含一个getter函数和一个setter函数。

访问器属性必须通过Object.defineProperty()来定义,下面这个例子中,book中的year_属性表示不希望被外部访问,则可通过定义year访问器属性解决:

        let book = {
            year_ : 2020,
            edition : 1
        }
        
        Object.defineProperty(book , "year" , {
            get(){
                return this.year_;
            },
            set(val){
                if(val > 2020){
                    this.year_ = val;
                    this.edition += val-2020;
                }
            }
        });

        book.year = 2022;
        console.log(book.edition) //3

也可以通过Object.defineproperties()来定义所有属性,区别就是数据属性的默认值为false。

        let book = {}

        Object.defineProperties(book, {
            year_: {
                value: 2020
            },
            edition: {
                value: 1
            },
            year: {
                get() {
                    return this.year_;
                },
                set(newValue) {
                    if (newValue > 2020) {
                        this.year_ = newValue;
                        this.edition += newValue - 2020;
                    }
                }
            }
        });

        book.year = 2022;
        console.log(book.edition) //1,因为edition的writable默认为false

2.Object.assign()

深拷贝浅拷贝

        let obj = {
            a:0,
            b:{
                c:1
            }
        }
        let obj1 = JSON.parse(JSON.stringify(obj))
        let obj2 = Object.assign({} , obj)
        obj.a = 123;
        obj.b.c = 456;
        console.log(obj , obj1 , obj2)

3.Object.create()

可以通过 Object.create()来创建一个新对象,同时为其指定原型,避免使用Object.setPrototypeOf()。

let biped = { 
 numLegs: 2 
}; 
let person = Object.create(biped); 
person.name = 'Matt'; 

console.log(person.name); // Matt 
console.log(person.numLegs); // 2 
console.log(Object.getPrototypeOf(person) === biped); // true 

4.Object属性的遍历顺序

1. for-in循环和Object.keys()

枚举顺序是不确定的,取决于JavaScript引擎,可能因浏览器而异。

2. Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()和Object.assign()

枚举顺序是确定的。先以升序枚举数值键,然后以插入顺序枚举字符串和符号键。在对象字面量中定义的键以它们逗号分隔的顺序插入。

        let k1 = Symbol('k1'),
            k2 = Symbol('k2');
        let o = {
            1: 1,
            first: 'first',
            [k1]: 'sym2',
            second: 'second',
            0: 0
        };
        o[k2] = 'sym2';
        o[3] = 3;
        o.third = 'third';
        o[2] = 2;

        console.log(Object.getOwnPropertyNames(o));
        // ["0", "1", "2", "3", "first", "second", "third"]

        console.log(Object.getOwnPropertySymbols(o));
        // [Symbol(k1), Symbol(k2)]