11.JavaScript的面向对象1-属性描述符

2021-09-22  本文已影响0人  静昕妈妈芦培培

JavaScript其实支持多种编程范式的,包括函数式编程面向对象编程

如何创建一个对象呢?
早期使用创建对象的方式最多的是使用Object类,并且使用new关键字来创建一个对象:
后来很多开发者为了方便起见,都是直接通过字面量的形式来创建对象:

创建对象的两种方式

/**
 * 对象的创建方式一
 */

var person = new Object();
person.name = "coderwhy";
person.age = 18;
/**
 * 对象的创建方式二:字面量方式
 */
var obj = {
  name: "kobe",
  age: 40,
};

对对象的属性进行操作

var obj = {
  name: "kobe",
  age: 40,
};


//访问对象的属性
console.log(obj.name)

//给对象添加属性
obj.height = 1.88;

//修改对象属性
obj.name = "curry"

//删除对象的属性
delete obj.name

对属性操作的控制

在前面我们的属性都是直接定义在对象内部,或者直接添加到对象内部的:

如果我们想要对一个属性进行比较精准的操作控制,那么我们就可以使用属性描述符。

Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此
对象。


image.png
可接收三个参数:
返回值:
var obj = {
  name: "kobe",
  age: 40,
};


/**
 * Object.defineProperty(obj, property, descritor)
 * obj为操作的目标对象
 * property为操作的目标对象的属性
 * descritor为属性描述符
 */

//使用下面方式给obj添加的height属性是不可枚举的,
//即console.log的时候,不会显示这个属性,
//for in的时候,不会遍历这个属性,
//Object.keys时也没用这个属性,
//但是可以通过obj.height获取到
Object.defineProperty(obj, "height", {
  value: 1.88,
});

console.log(obj); //{name: "kobe", age: 40}
console.log(obj.height); //1.88

属性描述符分类

属性描述符的类型有两种:

image.png

数据属性描述符

数据数据描述符有如下四个特性:

[[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符;
[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;
[[Writable]]:表示是否可以修改属性的值;
[[value]]:属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改;

数据属性描述符-configurable

var obj = {
  name: "kobe",
  age: 40,
};

Object.defineProperty(obj, "address", {
  value: "北京",
  //该特性为false,则对象的此属性不能被delete删除,不能被修改,不能被重新定义为存取属性描述符
  configurable: false,
})

//测试configurable
delete obj.address
console.log(obj.address) //北京  address属性没有被从obj对象上删除

obj.address = "上海"
console.log(obj.address) //北京  address属性值没有被修改


Object.defineProperty(obj, "address", {
  value: "上海",
  configurable: true
}) //报错,Cannot redefine property: address  address不能被重新定义



数据属性描述符-enumerable

var obj = {
  name: "kobe",
  age: 40,
};

Object.defineProperty(obj, "address", {
  value: "北京",
  //该特性为false,则对象的此属性不能被delete删除,不能被修改,不能被重新定义为存取属性描述符
  configurable: false,
  //该特性为false,则对象的此属性不能被for in遍历,不能被Object.keys(obj)获取到,使用console.log(obj)输出是不能被输出
  enumerable: false,
})



//测试enumerable
for(var key in obj) {
  console.log(key)  
}

//name  age          address没有被遍历出来

console.log(Object.keys(obj))  //[name, age]  不包含address

console.log(obj)  //{name: "kobe", age: 40}  不包含address
 

image.png

数据属性描述符-writable

var obj = {
  name: "kobe",
  age: 40,
};

Object.defineProperty(obj, "address", {
  value: "北京",
  //该特性为false,则对象的此属性不能被delete删除,不能被修改,不能被重新定义为存取属性描述符
  configurable: false,
  //该特性为false,则对象的此属性不能被for in遍历,不能被Object.keys(obj)获取到,使用console.log(obj)输出是不能被输出
  enumerable: false,
  //该特性为false,则对象的此属性的值不可以被修改
  writable: false,
})



//测试writable
obj.address = "上海"
console.log(obj.address) //北京

 

image.png

定义对象属性的不同方法,数据属性描述符配置的默认值

var obj = {
  name: "kobe",
  age: 40,
};

obj.address = "北京";
//上面定义对象属性的方法相当于下面的写法
Object.defineProperty(obj, "address", {
  value: "北京",
  configurable: true,
  enumerable: true,
  writable: true,
});

Object.defineProperty(obj, "height", {
  value: 1.88,
});

//上面定义对象属性的方法相当于下面写法
Object.defineProperty(obj, "height", {
  value: 1.88,
  configurable: false,
  enumerable: false,
  writable: false,
});

存取属性描述符

数据数据描述符有如下四个特性:

[[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符;
[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;
[[get]]:获取属性时会执行的函数。默认为undefined
[[set]]:设置属性时会执行的函数。默认为undefined
var obj = {
  name: "kobe",
  age: 40,
};


Object.defineProperty(obj, "_address", {
  value: "北京",
  configurable: true,
  enumerable: false,
  writable: true,
})


//存取属性描述符的用法一:隐藏一个私有属性,不希望被外界直接使用和赋值
Object.defineProperty(obj, "address", {
  configurable: true,
  enumerable: true,
  get: function() {
    return this._address
  },
  set: function(val) {
    this._address = val
  }
})
console.log(obj) //{ name: 'kobe', age: 40, address: [Getter/Setter] }
console.log(obj.address) //北京
obj.address = "上海"
console.log(obj.address) //上海

//存取属性描述符的用法二:截获某一个属性的访问和设置值的过程,
Object.defineProperty(obj, "address", {
  configurable: true,
  enumerable: true,
  get: function() {
    console.log("获取了address的值")
    return this._address
  },
  set: function(val) {
    console.log("设置了address的值")
    this._address = val
  }
})

console.log(obj.address)
obj.address = 50
image.png
在字面量对象中直接设置存取属性描述符,configurable,enumerable默认值为true
var obj = {
  name: "why",
  _address: "北京市",
  //在字面量对象中直接设置存取属性描述符,configurable,enumerable默认值为true
  get address() {
    return this._address;
  },
  set address(val) {
    this._address = val;
  },
};
console.log(obj);
相当于下面写法
var obj = {
  name: "why",
  _address: "北京市",
};
Object.defineProperty(obj, "address", {
  configurable: true,
  enumerable: true,
  get: function () {
    return this._address;
  },
  set: function (val) {
    this._address = val;
  },
});

属性描述符相关api

1. Object.defineProperties()

直接在一个对象上一次性定义多个新的属性或修改多个现有属性,并返回该对象。

语法:

Object.defineProperties(obj, props)

用法:
var obj = {
  name: "why",
  _age: 18,
  eatting: function () {},
};

//一次性给目标对象定义多个属性描述符
Object.defineProperties(obj, {
  address: {
    configurable: false,
    enumerable: false,
    writable: false,
    value: "北京市",
  },
  height: {
    configurable: true,
    enumerable: true,
    writable: true,
    value: 1.88
  },
  age: {
    configurable: true,
    enumerable: true,
    get: function () {
      return this._age;
    },
    set: function (val) {
      this._age = val;
    },
  },
});

console.log(obj);
//因为address是不可枚举的,所有没有输出address属性,但是可以通过obj.address获取值
console.log(obj.address)

image.png

2. Object.defineProperty()

在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

语法:

Object.defineProperty(obj, prop, descriptor)

用法:
var obj = {
  name: "why",
  _address: "北京市",
  running: function () {},
};

Object.defineProperty(obj, "age", {
  value: 18,
  configurable: true,
  enumerable: true,
  writable: true,
});

Object.defineProperty(obj, "address", {
  configurable: true,
  enumerable: true,
  get: function () {
    return this._address;
  },
  set: function (val) {
    this._address = val;
  },
});

console.log(obj);
console.log(obj.address);

image.png

3. Object.getOwnPropertyDescriptor()

返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

语法:

Object.getOwnPropertyDescriptor(obj, prop)

用法:
var obj = {
  name: "why",
  _address: "北京市",
  get address() {
    return this._address;
  },
  set address(val) {
    this._address = val;
  },
  running: function () {},
};

const descriptor1 = Object.getOwnPropertyDescriptor(obj, "name");
const descriptor2 = Object.getOwnPropertyDescriptor(obj, "address");
const descriptor3 = Object.getOwnPropertyDescriptor(obj, "running");
console.log(descriptor1);
console.log( descriptor2);
console.log(descriptor3);

image.png

4. Object.getOwnPropertyDescriptors()

获取所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

用法:
var obj = {
  name: "why",
  _address: "北京市",
  get address() {
    return this._address;
  },
  set address(val) {
    this._address = val;
  },
  running: function () {},
};

const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);

image.png

5. Object.preventExtensions()

让一个对象变的不可扩展,也就是永远不能再添加新的属性

语法:

Object.preventExtensions(obj)

用法:
var obj = {
  name: "why",
  running: function () {},
};
//让obj变得不可扩展,也就是永远不能给obj添加新的属性
Object.preventExtensions(obj);

obj.age = 18;
console.log(obj) 
//{ name: 'why', running: [Function: running] }   age属性并没有被添加到obj上
image.png

6. Object.seal()

封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置

用法:1.让对象的所有现有属性变为不可配置
var obj = {
  name: "why",
  _age: 18,
  get age() {
    return this._age;
  },
  set age(val) {
    this._age = val;
  },
  running: function () {},
};
console.log(Object.getOwnPropertyDescriptors(obj))

//让对象所有属性变得不可配置,即所有的属性都不能被delete删除,所有的属性描述符不能被修改
Object.seal(obj);
console.log(Object.getOwnPropertyDescriptors(obj))
image.png

相当于对对象做了如下操作:

for (var key in obj) {
  Object.defineProperty(obj, key, {
    ...Object.getOwnPropertyDescriptor(obj, key),
    configurable: false,
  });
}
用法二:让对象不能添加新属性
/**
 * Object.seal(obj)
 * 封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置
 *  1.即所有的属性都不能被delete删除,所有的属性描述符不能被修改
 *  2.不能给对象添加新属性
 */
var obj = {
  name: "why",
  _age: 18,
  get age() {
    return this._age;
  },
  set age(val) {
    this._age = val;
  },
  running: function () {},
};

//让对象所有属性变得不可配置,即所有的属性都不能被delete删除,所有的属性描述符不能被修改
Object.seal(obj);

//height属性不会被添加到obj上
obj.height = 18;
console.log(Object.getOwnPropertyDescriptors(obj));
image.png

7. Object.freeze()

冻结一个对象,阻止添加新属性并将所有现有属性标记为不可配置,属性值不可以被修改

也就是,如果一个对象被冻结,这个对象现有的所有属性的的属性描述符的configurable, writable被设置为false

用法:1.把对象所有属性的属性描述符的configurable,writable设置为false,则对象所有的属性不能被删除,属性描述符不能被修改,属性的值不能改变
/**
 * Object.freeze(obj)
 * 冻结一个对象,阻止添加新属性并将所有现有属性标记为不可配置,属性值不可以被修改
 *  1.即所有的属性都不能被delete删除,所有的属性描述符不能被修改
 *  2.对象所有的属性的值不可以被修改
 *  3.不能给对象添加新属性
 *  也就是说,对象的所有属性的属性描述符的configurable,writable被设置为false
 */
var obj = {
  name: "why",
  _age: 18,
  get age() {
    return this._age;
  },
  set age(val) {
    this._age = val;
  },
  running: function () {},
};
console.log(Object.getOwnPropertyDescriptors(obj));

Object.freeze(obj);
console.log(Object.getOwnPropertyDescriptors(obj));
//对象所有属性的属性描述符的configurable,writable被设置为false
image.png
用法:2.对冻结的对象,不能添加新属性
var obj = {
  name: "why",
  _age: 18,
  get age() {
    return this._age;
  },
  set age(val) {
    this._age = val;
  },
  running: function () {},
};

Object.freeze(obj);
//height属性不会被添加到obj上
obj.height = 18;
console.log(Object.getOwnPropertyDescriptors(obj));
image.png

8. Object.isExtensible()

判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。

语法:

Object.isExtensible(obj)

返回值:Boolean,true表示检测的对象是可扩展的
用法:
/**
 * Object.isExtensible(obj)
 * 检测目标对象是否可以扩展
 * 返回一个布尔值,表示其是否可扩展
 */
var obj = {
  name: "why",
  running: function () {},
};
//让obj变得不可扩展,也就是永远不能给obj添加新的属性
Object.preventExtensions(obj);
console.log(Object.isExtensible(obj)) //false

8. Object.isFrozen()

判断一个对象是否被[冻结]

语法:

Object.isFrozen(obj)
返回值:布尔值,true代表被检测的对象时冻结的

用法:
/**
 * Object.isFrozen(obj)
 * 判断一个对象是否被冻结
 *  1.对象不可以扩展,即不添加新属性
 *  2. 对象的所有属性都是不可配置的,即所有属性描述符的configurable为false
 *  3.对象的所有属性值都是不可以修改的,即所有属性描述符的writable为false
 */

//一个空对象被Object.preventExtensions()处理后,就变成是冻结的
var emptyObj = {};
Object.preventExtensions(emptyObj);
console.log(Object.isFrozen(emptyObj)); //true

//非空空对象被Object.seal()处理后,不是被冻结状态
var obj1 = { name: true };
Object.seal(obj1);
console.log(Object.isFrozen(obj1)); //false

var obj2 = {
  name: "why",
  _age: 18,
  get age() {
    return this._age;
  },
  set age(val) {
    this._age = val;
  },
  running: function () {},
};
Object.preventExtensions(obj2);
for (var key in obj2) {
  //in操作符可以用来判断一个属性是否属于一个对象
  if ("writable" in Object.getOwnPropertyDescriptor(obj2, key)) {
    Object.defineProperty(obj2, key, {
      ...Object.getOwnPropertyDescriptors(obj2),
      configurable: false,
      writable: false,
    });
  } else {
    Object.defineProperty(obj2, key, {
      ...Object.getOwnPropertyDescriptors(obj2),
      configurable: false,
    });
  }
}
console.log(Object.isFrozen(obj2)); //true

//被Object.freeze()处理的对象时冻结的
var obj3 = { name: "why" };
Object.freeze(obj3);
console.log(Object.isFrozen(obj3));

image.png

9. Object.isSealed()

判断一个对象是否被密封。

语法:
Object.isSealed(obj)
用法:
/**
 * Object.isSealed(obj)
 * 判断一个对象是否被密封
 *  1.对象不可以添加新属性
 *  2. 对象的所有属性都是不可配置的,即所有属性描述符的configurable为false
 */

//一个空对象被Object.preventExtensions()处理后,就变成是密封的
var emptyObj = {};
Object.preventExtensions(emptyObj);
console.log(Object.isSealed(emptyObj)); //true

//非空空对象被Object.seal()处理后变成密封的
var obj1 = { name: true };
Object.seal(obj1);
console.log(Object.isSealed(obj1)); //true

var obj2 = {name: true}
Object.preventExtensions(obj2);
for (var key in obj2) {
  Object.defineProperty(obj2, key, {
    ...Object.getOwnPropertyDescriptors(obj2),
    configurable: false,
  });
}
console.log(Object.isSealed(obj2)); //true

//一个密封对象同时也是不可扩展的
console.log(Object.isExtensible(obj2)); //false

//一个冻结的对象同时也是密封的
var obj3 = { name: "kobe", age: 40 };
Object.freeze(obj3);
console.log(Object.isSealed(obj3)); //true
image.png

11. Object.keys()

会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。

语法:

Object.keys(obj)

用法:
/**
 * Object.keys 返回一个所有元素为字符串的数组,]
 * 其元素来自于从给定的object上面可直接枚举的属性。
 * 这些属性的顺序与手动遍历该对象属性时的一致。
 */

 var obj = {
  name: "why",
  _age: 18,
  eatting: function () {},
};

//一次性给目标对象定义多个属性描述符
Object.defineProperties(obj, {
  address: {
    configurable: false,
    enumerable: false,
    writable: false,
    value: "北京市",
  },
  height: {
    configurable: true,
    enumerable: true,
    writable: true,
    value: 1.88
  },
  age: {
    configurable: true,
    enumerable: true,
    get: function () {
      return this._age;
    },
    set: function (val) {
      this._age = val;
    },
  },
});
console.log(obj)
console.log(Object.keys(obj));
//obj不可以枚举的属性是不会出现在Object.keys(obj)返回的对象中的


image.png

非常感谢王红元老师的深入JavaScript高级语法让我学习到很多 JavaScript 的知识

上一篇 下一篇

猜你喜欢

热点阅读