属性描述符

52 阅读4分钟

一、属性描述符

1.获取对象的属性的描述

  • 获取对象的某个属性描述符:Object.getOwnPropertyDescriptor(对象, "对象的某个属性");

image.png


const obj = {
  uerId: "1001",
  name: "张三",
  age: 10,
  sex: "男",
};

//获取属性描述,参数(对象,对象的某个属性)
const nameDesc = Object.getOwnPropertyDescriptor(obj, "name");
console.log(nameDesc)
//输出:{ value: '张三', writable: true, enumerable: true, configurable: true }
  • 获取对象的所有属性描述符Object.getOwnPropertyDescriptors(对象名);
const obj = {
  uerId: "1001",
  name: "张三",
  age: 10,
  sex: "男",
};
const allDesc = Object.getOwnPropertyDescriptors(obj);
console.log(allDesc);
//输出所有属性描述符
{
  uerId: {
    value: '1001',
    writable: true,
    enumerable: true,
    configurable: true
  },
  name: {
    value: '王五',
    writable: false,
    enumerable: false,
    configurable: true
  },
  age: { value: 10, writable: true, enumerable: true, configurable: true },
  sex: { value: '男', writable: true, enumerable: true, configurable: true }        
}

2.设置属性描述符

(1) value --- 属性值

const obj = {
  uerId: "1001",
  name: "张三",
  age: 10,
  sex: "男",
};

//可以通value属性修改值
Object.defineProperty(obj, "name", {
  value: "李四",
});

console.log(obj);
//输出 { uerId: '1001', name: '李四', age: 10, sex: '男' }

(2) writable --- 是否可以修改value的值

  • writable 为true --->obj对象的值改为了李四

Object.defineProperty(obj, "name", {
  writable: true,//属性值可修改
});

console.log(obj);//{ uerId: '1001', name: '张三', age: 10, sex: '男' }
obj.name = "李四";
console.log(obj); //{ uerId: '1001', name: '李四', age: 10, sex: '男' }


//可以通过修改属性配置从而修改属性值

Object.defineProperty(obj, "name", {
  writable: true,
});

obj.name = "李四";

console.log(obj); //{ uerId: '1001', name: '李四', age: 10, sex: '男' }

  • writable 为false --->obj对象的值仍然为张三
const obj = {
  uerId: "1001",
  name: "张三",
  age: 10,
  sex: "男",
};

Object.defineProperty(obj, "name", {
  writable: false,//属性值不可修改
});

console.log(obj); //{ uerId: '1001', name: '张三', age: 10, sex: '男' }
obj.name = "李四";
console.log(obj); //{ uerId: '1001', name: '张三', age: 10, sex: '男' }

(3)enumerable ---该属性是否可被遍历

  • enumerable 为true --->obj属性可以被遍历
 const obj = {
  uerId: "1001",
  name: "张三",
  age: 10,
  sex: "男",
};
//得到一个由对象键名形成的数组
Object.keys(obj) //[ 'uerId', 'name', 'age', 'sex' ]

//依次输出对象键名:uerId,name,age,sex
for (var key in obj) {
   console.log(key);
 }

  • enumerable 为false --->obj属性不可以被遍历

Object.defineProperty(obj, "name", {
  enumerable: false,
});

const objKeys = Object.keys(obj);
//没有属性name
console.log(objKeys); //["uerId", "age", "sex"];

////依次输出对象键名:uerId,age,sex
for (var key in obj) {
  console.log(key);
}

(4)configurable ---是否可配置

  • configurable为true,属性描述符可配置,默认为true

const obj = {
  uerId: "1001",
  name: "张三",
  age: 10,
  sex: "男",
};

const nameDesc = Object.getOwnPropertyDescriptor(obj, "name");
console.log(nameDesc);
//输出结果:{ value: '张三', writable: true, enumerable: true, configurable: true }
//设置属性描述符
Object.defineProperty(obj, "name", {
  value: "王五",
  writable: false,
  enumerable: false,
  configurable: false,
});

const nameDescChange = Object.getOwnPropertyDescriptor(obj, "name");
console.log(nameDescChange);
//输出结果:{
  value: '王五',
  writable: false,
  enumerable: false,
  configurable: false
}

  • configurable为false,属性描述符不可配置

const obj = {
  uerId: "1001",
  name: "张三",
  age: 10,
  sex: "男",
};

const nameDesc = Object.getOwnPropertyDescriptor(obj, "name");
console.log(nameDesc);
//设置属性描述符
Object.defineProperty(obj, "name", {
  configurable: false,
});

Object.defineProperty(obj, "name", {
  value: "王五",
  writable: true,
  enumerable: true,
  configurable: true,
});

const nameDescChange = Object.getOwnPropertyDescriptor(obj, "name");
console.log(nameDescChange);


结果会报错

image.png

(5)get,set--访问器

var obj = {};

Object.defineProperty(obj, "a", {
  //getter读取器
  get: function () {
    return 123;
  },
  //setter设置器
  set: function (val) {
    console.log("hello");
  },
});

obj.a = 3 + 2; // 相当于在执行set(3+2)
console.log(obj.a); // 相当于在执行get()

// 输出:先输出hello,在输出123

const g = {
  pic: "./assets/g1.png",
  title: "椰云拿铁",
  desc: `1人份【年度重磅,一口吞云】

    √原创椰云topping,绵密轻盈到飞起!
    原创瑞幸椰云™工艺,使用椰浆代替常规奶盖
    打造丰盈、绵密,如云朵般细腻奶沫体验
    椰香清甜饱满,一口滑入口腔
    
    【饮用建议】请注意不要用吸管,不要搅拌哦~`,
  sellNumber: 200,
  favorRate: 95,
  price: 32,
};
class UIData {
  constructor(g) {
    Object.defineProperty(this, "data", {
      get: function () {
        return g;
      },
      set: function () {
        throw new Error("只读属性不能赋值");
      },
      configurable: false,
    });
    let internalChooseValue = 0;
    Object.defineProperty(this, "choose", {
      get: function () {
        return internalChooseValue;
      },
      set: function (val) {
        if (typeof val !== "number") {
          throw new Error("choose类型必须是数字");
        } else if (parseInt(val) !== val) {
          throw new Error("choose类型必须整数");
        }
      },
      configurable: false,
    });

    //获取总价
    // Object.defineProperty(this, "totalPrice", {
    //   get: function () {
    //     return this.choose * this.data.price;
    //   },
    // });
  }
  //获取总价第二种方式,更简洁
  get totalPrice() {
    return this.choose * this.data.price;
  }
  get isChoose() {
    return this.choose > 0;
  }
}

const ui = new UIData(g);

// ui.data = 123;
// console.log(ui.data);
ui.choose = 3;
console.log(ui.isChoose);

3.限制对象修改的方法

(1) 放扩展:Object.preventExtensions()---> 防添加

(2)密封: Object.seal() --->防添加,防删除

(3) 冻结:Object.freeze() --->防添加,防删除,防修改


const g = {
  pic: "./assets/g1.png",
  title: "椰云拿铁",
  desc: `1人份【年度重磅,一口吞云】

    √原创椰云topping,绵密轻盈到飞起!
    原创瑞幸椰云™工艺,使用椰浆代替常规奶盖
    打造丰盈、绵密,如云朵般细腻奶沫体验
    椰香清甜饱满,一口滑入口腔
    
    【饮用建议】请注意不要用吸管,不要搅拌哦~`,
  sellNumber: 200,
  favorRate: 95,
  price: 32,
};
class UIData {
  constructor(g) {
    // Object.seal(g); //密封后,不能添加属性,但能修改属性
    // Object.freeze(g);//冻结后不能添加和修改属性
    g = { ...g }; //克隆对象
    Object.defineProperty(this, "data", {
      get: function () {
        return g;
      },
      set: function () {
        throw new Error("只读属性不能赋值");
      },
      configurable: false,
    });
    let internalChooseValue = 0;
    Object.defineProperty(this, "choose", {
      get: function () {
        return internalChooseValue;
      },
      set: function (val) {
        if (typeof val !== "number") {
          throw new Error("choose类型必须是数字");
        } else if (parseInt(val) !== val) {
          throw new Error("choose类型必须整数");
        }
      },
      configurable: false,
    });
    Object.freeze(this);
    //获取总价
    // Object.defineProperty(this, "totalPrice", {
    //   get: function () {
    //     return this.choose * this.data.price;
    //   },
    // });
  }
  //获取总价第二种方式,更简洁
  get totalPrice() {
    return this.choose * this.data.price;
  }
  get isChoose() {
    return this.choose > 0;
  }
}

const ui = new UIData(g);
// ui.data.name = "abc";  //使用seal或freeze后,不能添加属性
ui.data.price = 900; //使用seal可以修改值,使用freeze不能修改值
console.log(ui.data);
                      

image.png