红宝书系列之 对象(一)

目录

1. 定义

1.1 创建对象

2.属性的类型

2.1 数据属性

2.2 修改默认属性

2.3 访问器属性

3.对象的操作

3.1 定义多个属性 Object.defineProperties( )

3.2 读取属性的特性

Object.getOwnPropertyDescriptor( )

Object.getOwnPropertyDescriptors( )

3.3 合并对象 Object.assign( )

4.增强的对象语法

4.1 属性值简写

4.2 可计算属性

5. 对象解构


1. 定义

ECMA-262 将对象定义为一组属性的无序集合,可以把对象想象成一张散列表。对象的每个属性或方法都由一个名称来标识,这个名称映射到一个值。(也就是键值对)

1.1 创建对象

  • 通常方法

先创建一个 Object 实例,然后给它添加属性和方法(有点麻烦)

let person = new Object();
person.name = 'mike';
person.age = 18;
person.sayName = function() {
    console.log(this.name);
};
  • 对象字面量方法
let person = {
    name: 'mike',
    age: 18,
    sayName() {
        console.log(this.name);
    }
    // 也可以写成这样
    // sayName: function() {
    //     console.log(this.name);
    // }
};

2.属性的类型

属性包括两种

  • 数据属性
  • 访问器属性
特性的定义:ECMA-262 使用一些内部特性来描述属性的特征。这些特性是由为 JavaScript 实现引擎的规范定义 的。因此,开发者不能在 JavaScript 中直接访问这些特性。为了将某个特性标识为内部特性,规范会用两个中括号把特性的名称括起来,比如 [[ Enumerable ]]  

2.1 数据属性

数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。数据属性有4个特性描述它们的行为。

  1. [[ configurable ]] : 表示属性是否可以通过 delete 删除并重新定义,是否可以修改他的特性。以及是否可以把它变成访问器属性。默认是 true
  2. [[ Enumerable]] : 表示属性是否可以通过 for-in 循环返回。默认是 true
  3. [[ Writable ]] : 表示属性是否可以被修改。默认是 true
  4. [[ Value ]] : 表示属性的实际值,默认未赋值的情况下是 undefined

例如:

let person = {
    name: 'mike'
}

这里创建了一个名为 name 的属性,且赋值为 mike ,那么前面三个特性 [[ configurable ]] [[ Enumerable]] [[ Writable ]] 都会是默认值 true , [[ Value ]] 为 ' mike '

2.2 修改默认属性

使用 Object.defineProperty() 方法,接收三个参数:属性对象、属性名称、描述符对象

let person = {
    name: 'mike'
}
Object.defineProperty(person,"name", {
    writable: false
})
person.name = 'alice'
console.log(person.name);           // mike

上面的例子,在非严格模式下尝试重新赋值会被忽略,在严格模式下会报错

Tips:

  • 如果把 [[ configurable ]] 设置为 false,该属性变成不可配置后,就不能再变回可配置了。再次调用 Object.defineProperty() 修改除 [[ writable ]] 之外的所有属性都会报错
  • 在调用 Object.defineProperty() 时如果没有给 configurable enumberable writable 指定值,就会默认是 false (例如 3.1 的例子)
  • 多数情况下,可能不需要 Object.defineProperty() 提供这些强大的设置,但要知道这些概念

2.3 访问器属性

访问器属性不包含数值。它们包含一个获取函数(getter)和一个设置函数(setter),不过这两个函数不是必需的。在读取时会调用 getter ,在写入时调用 setter

访问器属性不是直接定义的,必须通过 Object.defineProperty()

访问器属性有4个特性

  1. [[ configurable ]] : 表示属性是否可以通过 delete 删除并重新定义,是否可以修改他的特性。以及是否可以把它变成访问器属性。默认是 true
  2. [[ Enumerable]] : 表示属性是否可以通过 for-in 循环返回。默认是 true
  3. [[ Get ]] : 获取函数,在读取属性时使用,默认为 undefined
  4. [[ Set ]] : 设置函数,在写入属性时使用,默认为 undefined

例如:

let book = {
    year_: 2023,    // 数据属性,通常情况下下划线表示该属性并不希望在对象方法的外部使用
    edition: 1      // 数据属性
};

// 定义访问器属性
Object.defineProperty(book,"year", {
    get() {
        return this.year_;
    },
    set(value) {
        if(value > 2023) {
            this.year_ = value;
            this.edition += value - 2023;
        }
    }
});
// 使用访问器属性
book.year = 2025;
console.log(book.edition);    // 3

在上面的例子中,修改 year 属性会导致 year_ 和 edition 都会进行更新。这个就是访问器属性的典型使用场景,设置一个属性会导致其他属性发生一些变化。 

Tips:

  • 虽然获取函数和设置函数并不是一定需要定义的。但是如果你只定义了获取函数,也就意味着该属性是只读的,当你想要尝试修改属性时,非严格模式会忽略,严格模式会报错。反之也是如此

3.对象的操作

3.1 定义多个属性 Object.defineProperties( )

Object.defineProperties( ) 接收两个参数,第一个是添加或修改属性的对象,第二个是描述符对象

let book = {};
Object.defineProperties(book,{
    year_: {                // 数据属性
        value: 2023
    }, 
    edition: {             // 数据属性
        value: 1    
    },
    year: {                // 访问器属性
        get() {
            return this.year_;
        },
        set(value) {
            if(value > 2023) {
                this.year_ = value;
                this.edition += value - 2023;
            }
        }
    }
})

这个例子中定义的 book 对象跟 2.3 例子中的 book 对象是一样的。区别就是数据属性 year_ 和edition 由于没有指定 configurable enumerable 和 writable 特性值,所以全都设置为 false

3.2 读取属性的特性

  • Object.getOwnPropertyDescriptor( )

Object.getOwnPropertyDescriptor( ) 接收两个参数,第一个是要访问的对象名,另一个是属性名(要以字符串的形式)。返回值是一个对象,返回数据属性和访问器属性的四个特性

let book = {};
Object.defineProperties(book,{
    year_: {                // 数据属性
        value: 2023
    }, 
    edition: {             // 数据属性
        value: 1    
    },
    year: {
        get() {
            return this.year_;
        },
        set(value) {
            if(value > 2023) {
                this.year_ = value;
                this.edition += value - 2023;
            }
        }
    }
})

let descriptor = Object.getOwnPropertyDescriptor(book,"year_");
console.log(descriptor);
// {
//   value: 2023,
//   writable: false,
//   enumerable: false,
//   configurable: false
// }

let descriptor1 = Object.getOwnPropertyDescriptor(book,"year");
console.log(descriptor1);
// {
//     get: [Function: get],
//     set: [Function: set],
//     enumerable: false,
//     configurable: false
// }
  • Object.getOwnPropertyDescriptors( )

该函数接收一个参数为要访问的对象,返回值是一个对象。该函数实际上会在对象的每个自有属性上调用 Object.getOwnPropertyDescriptor( ) ,合并在一个新对象后返回。

let book = {};
Object.defineProperties(book, {
    year_: { // 数据属性
        value: 2023
    },
    edition: { // 数据属性
        value: 1
    },
    year: {
        get() {
            return this.year_;
        },
        set(value) {
            if (value > 2023) {
                this.year_ = value;
                this.edition += value - 2023;
            }
        }
    }
})

let descriptors = Object.getOwnPropertyDescriptors(book);
console.log(descriptors); 
// {
//     year_: {
//         value: 2023,
//         writable: false,
//         enumerable: false,
//         configurable: false
//     },
//     edition: {
//         value: 1,
//         writable: false,
//         enumerable: false,
//         configurable: false
//     },
//     year: {
//         get: [Function: get],
//         set: [Function: set],
//         enumerable: false,
//         configurable: false
//     }
// }

3.3 合并对象 Object.assign( )

  • Object.assign( ) 这个方法接收一个目标对象和一个或多个源对象作为参数。返回值是修改后的目标对象。
  • 将源对象中可枚举和自有的属性复制到目标对象中。(使用源对象的 [[ Get ]] 特性方法获取值,使用目标对象的 [[ Set ]] 特性方法设置属性值

简单复制

let dest = {
    a: 'aaa'
}
let src = {
    b: 'bbb'
}

let k = Object.assign(dest,src, { c: 'ccc'} );

console.log(dest);
// { a: 'aaa', b: 'bbb', c: 'ccc' }

console.log(k);
// { a: 'aaa', b: 'bbb', c: 'ccc' }

获取函数和设置函数

let dest = {
    set (val) {
        console.log(`This is ${val}`);
    }
}

let src = {
    get () {
        console.log('src getter');
        return 'foo';
    }
}

Object.assign(dest,src);

dest.get();       // src getter

覆盖属性,重复的属性会进行覆盖

let dest = {
    a: 'aaa',
    b: 'bbb'
}

let src = {
    a: 'zzz'
}

Object.assign(dest, src, { a: 'kkk' });

console.log(dest);
// { a: 'kkk', b: 'bbb' }

4.增强的对象语法

4.1 属性值简写

当属性名和变量名相等时,可以直接简写属性名即可。当简写时,如果没有找到同名变量,则会抛出 ReferenceError 

let name = 'mike';
function sayName() {
    console.log(this.name);
}

let dest = {
    name,        // 等价于 name: name
    age: 18,
    sayName     // 函数也适用
}

console.log(dest);
// { name: 'mike', age: 18, sayName: [Function: sayName] }

dest.sayName();  // mike

4.2 可计算属性

  • 如果想要用一个变量来定义属性名的话,要使用中括号语法,不然会直接识别成字符串。

Tips

  • 变量中途发生改变,要重新定义 person 才会在 person 上生效
let key = 'job';

let person = {
    [key]: 'good'
}

console.log(person.job);   // good

key = 'class';

console.log(person.class);    // undefined  要重新定义对象才有用
console.log(person.job);      // good

// 重新定义对象 person 变量改动后的值才生效
person = {
    [key]: 'bad'
}

console.log(person.class);   // bad
console.log(person.job);     // undefined
  • 函数返回值也可以充当属性名
let nameKey = 'name';

function getKey(count) {
    return `${nameKey}_${count}`;
}

let person = {
    [getKey(1)] : 'aaa'
}

console.log(person);
// { name_1: 'aaa' }

5. 对象解构

ES6 新增了对象结构语法,可以在一条语句中使用嵌套数据实现一个或多个赋值操作。

  • 不解构时
let person = {
    name: 'mike',
    age: 18
}
let personName = person.name;
let personAge = person.age;

console.log(personName);   // mike
console.log(personAge);    // 18
  • 解构时,简单轻松
let person = {
    name: 'mike',
    age: 18
}
let { name: personName, age: personAge } = person;

console.log(personName);   // mike
console.log(personAge);    // 18
  • 如果属性名和要赋值的变量名相等,甚至可以简写
let person = {
    name: 'mike',
    age: 18
}
let { name, age, job } = person;

console.log(name);   // mike
console.log(age);    // 18
console.log(job);    // undefined 当属性不存在时 undefined

Tips:

  • 解构时会自动帮你生成变量,如果要给事先声明过的变量赋值,则表达式必须包含在一对括号中。
let personName,personAge;

let person = {
    name: 'mike',
    age: 18
};

({ name: personName, age: personAge } = person);

console.log(personName);    // mike
console.log(personAge);     // 18

终于写完了,不过未完待续......

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值