美文网首页
11.JavaScript的面向对象1-属性描述符

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

作者: 静昕妈妈芦培培 | 来源:发表于2021-09-22 14:57 被阅读0次

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

  • JavaScript中的对象被设计成一组属性的无序集合,像是一个哈希表,有key和value组成;
  • key是一个标识符名称,value可以是任意类型,也可以是其他对象或者函数类型;
  • 如果值是一个函数,那么我们可以称之为是对象的方法;
如何创建一个对象呢?
早期使用创建对象的方式最多的是使用Object类,并且使用new关键字来创建一个对象:
  • 这是因为早期很多JavaScript开发者是从Java过来的,它们也更习惯于Java中通过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

对属性操作的控制

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

  • 但是这样来做的时候我们就不能对这个属性进行一些限制:比如这个属性是否是可以通过delete删除的?这个属性是否在for-in遍历的时候被遍历出来呢?
如果我们想要对一个属性进行比较精准的操作控制,那么我们就可以使用属性描述符。
  • 通过属性描述符可以精准的添加或修改对象的属性;
  • 属性描述符需要使用 Object.defineProperty 来对属性进行添加或者修改

Object.defineProperty

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


image.png
可接收三个参数:
  • obj要定义属性的对象;
  • prop要定义或修改的属性的名称或 Symbol;
  • descriptor要定义或修改的属性描述符;
返回值:
  • 被传递给函数的对象。
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

属性描述符分类

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

  • 数据属性(Data Properties)描述符(Descriptor);
  • 存取属性(Accessor访问器 Properties)描述符(Descriptor);
image.png

数据属性描述符

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

[[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符;
  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false;
[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;
  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false;
[[Writable]]:表示是否可以修改属性的值;
  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Writable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Writable]]默认为false;
[[value]]:属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改;
  • 默认情况下这个值是undefined;

数据属性描述符-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删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符;
  • 和数据属性描述符是一致的;
  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false;
[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;
  • 和数据属性描述符是一致的;
  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false;
[[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.即所有的属性都不能被delete删除,所有的属性描述符不能被修改
  • 2.不能给对象添加新属性
用法: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()

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

  • 1.一个被冻结的对象再也不能被修改;
  • 2.冻结了一个对象则不能向这个对象添加新的属性,
  • 3.不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,
  • 4.不能修改已有属性的值。
  • 5.冻结一个对象后该对象的原型也不能被修改。
    +6. 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()

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

  • 1.对象不可以添加新属性
    1. 对象的所有属性都是不可配置的,即所有属性描述符的configurable为false
语法:
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 的知识

相关文章

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

    JavaScript其实支持多种编程范式的,包括函数式编程和面向对象编程: JavaScript中的对象被设计成一...

  • JS基础(二)

    2018.05.13 11.JavaScript 对象 JavaScript对象是拥有属性和方法的数据,几乎所有的...

  • 定义一个变量

    属性描述符(Property Descriptors) 我们普通的对象属性a的属性描述符(称为“数据描述符”,因为...

  • Object.defineProperty方法

    对象的属性在创建的时候,都会带有属性描述符,分为数据描述符和存取描述符 数据描述符是具有值的属性,改属性值可能可写...

  • Object.defineProperty()

    作用 直接在对象上定义或者修改属性 通过描述符的设置可以精准的控制对象的属性 为对象定义属性有两种方式:属性描述符...

  • JS 面向对象补充 构造函数 原型 对象原型 函数原型

    1.面向对象 1.1.定义多个属性描述符 1.1.1 set和get的新型写法 这种的configurable为t...

  • python中描述符的学习

    什么是描述符 描述符是Python新式类的关键点之一,它为对象属性提供强大的API,你可以认为描述符是表示对象属性...

  • js 对象属性描述符

    ECMAScript对象中⽬前存在的属性描述符主要有两种,数据描述符(数据属性)和存取描述符(访问器属性),数据描...

  • 2019-09-08 理解 数据描述符和存取描述符

    对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。 我们可以理解 属性描述符 为用来描述一个属性的...

  • 如何让对象属性不可配置或枚举

    一、什么是属性描述符? MDN: 对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。 数据描述符是...

网友评论

      本文标题:11.JavaScript的面向对象1-属性描述符

      本文链接:https://www.haomeiwen.com/subject/wjmegltx.html