ES6知识点总结(七)

对象进阶

JavaScript对象

JavaScript的基本数据类型,一种复合值,可看做是属性的无序集合。

特征

  • 每个属性都是一个名/值对 (名称是唯一的)
  • 属性名是字符串,可以把对象看成是从字符串到值得映射。
  • 可以从一个称为原型的对象继承属性
  • 对象是动态的,可以增加或删除属性
  • 除了字符串、数值、true、false、null和undefined,其他值都是对象。
  • 属性值是任意JavaScript值,或者是一个getter或setter函数。
  • 对象最常见的用法是对其属性进行创建、设置、查找、删除、检测和枚举等操作。

属性特征(property abttribute)

  • 可写(writable attribute),表明是否可以设置属性的值。
  • 可枚举(enumerable attribute),表明是否可以通过for/in结构返回该属性。
  • 可配置(configurable attribute),表明是否可以删除或修改该属性。

对象特性

  • 对象的原型(prototype),指向另一个对象,该对象的属性会被当前对象继承
  • 对象的类(class),一个标识对象类型的字符串
  • 对象的扩展标记(extensible flag),指明了是否可以向该对象添加新属性

对象的分类

  • 内置对象,native object
    由ECMAScript实现提供的对象,独立于宿主环境,在一个脚本程序执行的开始处。
    包括:String(字符串对象)、RegExp(正则表达式对象)、Number(数字对象)、Math(数学对象)、Function(函数对象)、Error(异常对象)、Date(日期/时间对象)、Boolean(布尔对象)、Array(数组对象)、Object(对象超类)
  • 宿主对象,host object
    由 ECMAScript 实现的宿主环境提供的对象
  • 自定义对象,user-defined object

属性的分类

  • 自有属性,own property
  • 继承属性,inherited property

创建对象

  • 对象字面量
    对象字面量是一个表达式,每次运算都会创建一个新的对象,其中的属性值也会重新计算。
let empty = {}; //没有任何属性的对象
let point = { x: 0, y: 0 }; //具有属性的对象
let book = {
  //属性名中有空格,必须用字符串表示。
  "main title": "JavaScript",
  //属性名中有特殊字符,必须用字符串表示
  "sub-title": "The Definitive Gruide", 
  //属性名可以是保留字,但尽量避免。
  for: "all audiences", 
  //属性值可以是一个对象。  
  author: {
    firstname: "David",
    lastname: "Flanagan",
  },
};
  • 通过new创建对象
    通过new调用构造函数(constructor)来创建并初始化一个新对象。
    JavaScript语言核心中的原始类型都包含内置构造函数,如Object()、Array()、Date()等
function Person(name , age){
         this.name = name;
         this.age = age;
         return this;
     }
var p1 = new Object(); 
p1 = Person('neo','23');
console.log(p1.name);       //neo

原型

  • 每一个JavaScript对象(null除外)都和原型对象相关联。每一个对象都从原型继承属性
    所有通过对象字面量创建的对象都具有同一个原型对象
let obj = {value:100};  
Object.prototype.flag = "head";   //最顶层的原型增加了属性
console.log(obj.flag);       //head
let arr = [1,2,3,4];
console.log(arr.flag);       //head
let str= "adw";
console.log(str.flag);       //head
  • 通过Object.prototype获得原型对象的引用。
    (Object.prototype的原型是null,它是对象继承的顶端)
    对象.__ proto__ == Object.prototype
  • 通过new和构造函数创建的对象的原型就是构造函数的prototype属性引用的对象
    如:new Array()对象的原型就是Array.prototype
  • 极少对象没有原型。
    如:Object.prototype,它不继承任何属性。
  • 所有内置构造函数都具有一个继承自Object.prototype的原型
    Array.prototype的属性继承自Object.prototype。

通过层级的原型继承形成的链接,称为“原型链”(prototype chain)

es6对象是否存在某个属性删除_es6对象是否存在某个属性删除

  • Object.create( )
    创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
// obj1继承了属性x和y。
let obj1 = Object.create({ x: 1, y: 2 });
//obj2不继承任何属性和方法。
let obj2 = Object.create(null);
//obj3是一个普通的空对象。
let obj3 = Object.create(Object.prototype);

原型对象保护机制
当一个对象要改变属性对应的值,会给对象自动创建一个新的属性,而不是改变原型对象中的对应属性。

属性相关

访问属性
使用“ . ”和“ [ ] ”来访问对象的属性

let author = {
  "first name": "Tonny",
  "last-name": "Michael",
  age: 40,
};
console.log(author["first name"], author["last-name"]);     //Tonny Michael
let { "first name": fname, "last-name": lname } = author;
console.log(fname, lname, author.age);     //Tonny Michael 40

作为关联数组的对象

let result = {
  data1: {
    name: "Language",
    value: "Chinese",
  },
  data2: {
    name: "Country",
    value: "China",
  },
  data3: {
    name: "Gender",
    value: "Male",
  },
};
for (let i = 1; i < 4; i++) {
  let data = result["data" + i];
  console.log(`${data.name}-->${data.value}`);
  //Language-->Chinese
  //Country-->China
  //Gender-->Male
}

继承
属性赋值操作首先检查原型链,以此判断是否允许赋值操作。

let a = {};
a.x = 1;
let b = Object.create(a);
b.y = 2;
b.x = 100;
let c = Object.create(b);
c.z = 3;
console.log(c.toString());       //[object Object]
console.log(c.x + c.y + c.z);    //105

属性访问错误
查询一个不存在的属性并不会报错,如果在对象o自身的属性或继承的属性中均未找到属性x,属性访问表达式o.x返回undefined
如果对象不存在,那么试图查询这个不存在的对象的属性就会报错

对象o设置属性p会失败的场景

  1. o中的属性p是只读的:不能给只读属性重新赋值(defineProperty()方法中有一个例外,可以对可配置的只读属性重新赋值
  2. o中的属性p是继承属性,且它是只读的:不能通过同名自有属性覆盖只读的继承属性
  3. o中不存在自有属性p:o没有使用setter方法继承属性p,并且o的可扩展性(extensible attribute)是false

删除属性
delete运算符可以删除对象的属性,只能删除自有属性,不能删除继承属性

let o = { x: 1 };
delete o.x;
delete o.toString;
console.log(o, o.toString());    //{} "[object Object]"

delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性。

let book= { "author":"may"};
let auth = book.author;
delete book.author;
console.log(book, auth);    //{} "may"

delete不能删除那些可配置性为false的属性

检测属性
判断某个属性是否存在于某个对象中,可以通过in运算符、hasOwnPreperty()和propertyIsEnumerable()方法

let o = { x: 1 };
console.log("x" in o);       //true
console.log("y" in o);      //false
console.log("toString" in o);  //true

console.log(o.hasOwnProperty("x"));    //true
console.log(o.hasOwnProperty("y"));    //true
console.log(o.hasOwnProperty("toString"));   //false

console.log(o.propertyIsEnumerable("x"));    //true
console.log(o.propertyIsEnumerable("y"));    //false
console.log(o.propertyIsEnumerable("toString"));   //false
console.log(Object.prototype.propertyIsEnumerable("toString"));  //false

枚举属性
for/in循环可以在循环体中遍历对象中所有可枚举的属性(包括自有属性和继承的属性),把属性名称赋值给循环变量

let o =Object.create({m:10,n:20});
o.x=1; o.y=2; o.z=3;
for (let p in o) {
  console.log(p, o[p]);
  //x 1
  //y 2
  //z 3
  //m 10
  //n 20
  }

Object.keys(),它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成
Object.getOwnPropertyNames(),返回对象的所有自有属性的名称,不仅仅是可枚举的属性。

let o = Object.create({ m: 10, n: 20 });
o.x = 1; o.y = 2; o.z = 3;
console.log(Object.keys(o));    //["x", "y", "z"]

let o = Object.create({ m: 10, n: 20 });
o.x = 1; o.y = 2; o.z = 3;
console.log(Object.getOwnPropertyNames(o));   //["x", "y", "z"]

存取器属性
由getter和setter定义的属性称做“存取器属性”

  • 当程序查询存取器属性的值时,JavaScript调用getter方法(无参数)。这个方法的返回值就是属性存取表达式的值。
  • 当程序设置一个存取器属性的值时,JavaScript调用setter方法,将赋值表达式右侧的值当做参数传入setter
let circle = {
  r: 10,
  get round() {
    return 2 * this.r * Math.PI;
  },
  set round(v) {
    this.r = v / 2 / Math.PI;
  },
  get area() {
    return Math.PI * this.r ** 2;
  },
};
console.log(circle.round, circle.area);   //62.83185307179586 314.1592653589793
circle.round = 60;
console.log(circle.r, circle.area);  //9.549296585513721 286.47889756541167

数据属性的4个特性
值(value)、可写性(writable)、可枚举性(enumerable)、可配置性(configurable)
存取器属性的4个特性
读取(get)、写入(set)、可枚举性(enumerable)、可配置性(configurable)

通过一个名为“属性描述符”(property descriptor)的对象实现属性特性的查询和设置操作

  • Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符
  • 调用Object.definePeoperty()设置属性的特性
let o = {};
Object.defineProperty(o, "x", {
  value: 10,
  writable: true,
  enumerable: false,
  configurable: true,
});
console.log(o.x, Object.keys(o));  //10 []
console.log(Object.getOwnPropertyDescriptor(o, "x"));
//{value: 10, writable: true, enumerable: false, configurable: true}

Object.defineProperty(o, "x", { enumerable: true });
console.log(Object.getOwnPropertyDescriptor(o, "x"));
//{value: 10, writable: true, enumerable: true, configurable: true}

如果要同时修改或创建多个属性,使用Object.defineProperties()。
第一个参数是要修改的对象。
第二个参数是一个映射表,它包含要新建或修改的属性的名称,以及它们的属性描述符。

let p = Object.defineProperties(
  {},
  {
    x: { value: 1, writable: true, enumerable: true, configurable: true },
    y: { value: 1, writable: true, enumerable: true, configurable: true },
    r: {
      get: function () {
        return Math.hypot(this.x, this.y);
      },
      enumerable: true,
      configurable: true,
    },
  }
);

原型属性

  • 通过对象直接量创建的对象使用Object.prototype作为它们的原型
  • 通过new创建的对象使用构造函数( constructor属性)的prototype属性作为它们的原型
  • 通过Object.create()创建的对象使用第一个参数(也可以是null)作为它们的原型
  • 将对象作为参数传入Object.getPrototypeOf()可以查询它的原型

isPrototypeOf()方法,检测一个对象是否是另一个对象的原型(或处于原型链中)

let o = { x: 1 };
let p = Object.create(o);
p.y = 2;
console.log(Object.getPrototypeOf(p));    //{x: 1}
console.log(o.isPrototypeOf(p));     //true

可扩展性

  • 可以给对象添加新属性
  • 所有内置对象和自定义对象都是显式可扩展的
  • 宿主对象的可扩展性是由JavaScript引擎定义的

对象序列化(serialization)是指将对象的状态转换为字符串,也可将字符串还原为对象

  • 内置函数JSON.stringify()和JSON.parse()用来序列化和反序列化JavaScript对象
  • JSON.stringify()只能序列化对象可枚举的自有属性
  • 函数、RegExp、Error对象和undefined值不能序列化和还原

Object 构造函数的方法

Object.assign()
 通过复制一个或多个对象来创建一个新的对象。Object.create()
 使用指定的原型对象和属性创建一个新对象。Object.defineProperty()
 给对象添加一个属性并指定该属性的配置。Object.defineProperties()
 给对象添加多个属性并分别指定它们的配置。Object.entries()
 返回给定对象自身可枚举属性的 [key, value] 数组。Object.freeze()
 冻结对象:其他代码不能删除或更改任何属性。Object.getOwnPropertyDescriptor()
 返回对象指定的属性配置。Object.getOwnPropertyNames()
 返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名。Object.getOwnPropertySymbols()
 返回一个数组,它包含了指定对象自身所有的符号属性。Object.getPrototypeOf()
 返回指定对象的原型对象。Object.is()
 比较两个值是否相同。所有 NaN 值都相等(这与== 和 ===不同)Object.isExtensible()
 判断对象是否可扩展。Object.isFrozen()
 判断对象是否已经冻结。Object.isSealed()
 判断对象是否已经密封。Object.keys()
 返回一个包含所有给定对象自身可枚举属性名称的数组Object.preventExtensions()
 防止对象的任何扩展。Object.seal()
 防止其他代码删除对象的属性。Object.setPrototypeOf()
 设置对象的原型(即内部 [[Prototype]] 属性)。Object.values()
 返回给定对象自身可枚举值的数组。