JavaScript权威指南(个人笔记):(二)对象

对象

对象也可看作是属性的无序集合,每个属性都是一个名/值对。

 

属性访问查询(以下两种方式)

expression . identifier  一个表达式后跟随一个句点和标识符。表达式指定对象,标识符指定需要访问属性的名称。

expression [ expression ]   方括号内是另一个表达式(会计算方括号内的表达式的值并将它转换为字符串。)

对象的属性名称可以是字符串而不是标识符,可以通过点(.)或方括号([]) 来获取属性的值。

 

对于  .  来说,右侧是一个以属性名称命名的简单标识符。

对于  [ ]  来说,方括号内必须是一个计算结果为字符串的表达式(严格来说,或者返回一个可以转换为字符串的值),这个字符串就是属性的名字

 

例子: 

let book = { name: "the little prince" };

book ["name"]  // => the little prince

通过方括号来访问对象属性,就像访问关联数组一样!JavaScript对象都是关联数组!

这种写法在函数传参以及遍历的时候比较灵活,因为里面是字符串(字符串值时动态的,可以在运行时更改),而不是标识符作为索引对属性进行访问(标识符是静态的,必须写死在程序中) 

 

属性访问错误

如果对象不存在,那么试图查询这个不存在的对象的属性就会报错

例子:(接上一个例子)

let len = book.subtitle.length;  // 抛出一个类型错误异常,对象book的subtitle属性是undefined,undefined没有length属性

 

除非确定book和book.subtitle都是(或在行为上)对象,否则不能这样写表达式book.subtitle.length,因为这样会报错,下面提供了两种避免出错的方法:

// 一种沉余但很易懂的方法

let len = undefined;

if (book) {

  if (book.subtitle) len = book.subtitle.length;

}

 

// 一种更简练的常用方法,获取subtitle的length属性或undefined

let len = book && book.subtitle && book.subtitle.length;

 

创建对象

可以通过下面三种方法创建对象:

1.对象直接量

2.关键字new

3.Object.create()

 

对象直接量

属性名可以是JavaScript标识符也可以是字符串直接量,如:

let empty = {}  // 创建一个空对象

let p = { "name": "joy" };  // 属性名用字符串表示

 

对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新对象。

 

通过new创建对象

let o = new Object();  // 创建一个空对象,和{}一样

 

Object.create(obj)

创建一个新对象,其中第一个参数就是这个对象的原型。(暂不说第二个参数)

let o1 = Object.create({ x:1, y:2 });  // 创建新对象o1,o1继承了参数对象的属性x和y(即o1的原型是传入的参数对象)

 

原型 & 继承 & 原型链

JavaScript对象可以从一个称为原型的对象继承属性。这种“原型式继承”是JavaScript的核心特征。

原型:

基本每个对象都和另一个对象相关联,“另一个” 对象就是原型,每个对象都从原型继承属性

通过对象直接量创建的对象使用Object.prototype作为他们的原型。

通过关键字new创建的对象使用构造函数的prototype属性作为他们的原型。

例子:

let date = new Date()  // date的原型就是Date.prototype

 

所有内置构造函数(以及大部分自定义的构造函数),都具有一个继承自Object.prototype的原型。

例子:

Date.prototype的属性继承自Object.prototype

因此对象date的属性同时继承Date.prototype和Object.prototype(原型链)

 

继承:

JavaScript对象具有“自有属性”(own property), 也有一些属性是从原型对象继承而来的。

使用下面的函数,用原型创建一个新对象,新对象将从原型继承属性

例子:inherit()通过原型继承创建一个新对象,返回了一个继承自原型对象p的属性的新对象。

function inherit(p) {

  if (p == null) throw TypeError();         // p是一个对象,但不能是null

  if (Object.create) return Object.create(p);      // 如果Object.create()存在,则直接使用它

  let t = typeof p;                // 否则进一步检测

  if ( t !== 'object' && t !== 'function' ) throw TypeError();

  function Foo() { }                  // 定义一个空构造函数

  Foo.prototype = p;                  // 将其原型属性设置为p

  return new Foo();                 // 使用f()创建p的继承对象

}

 

let o = { x:1 };  // 定义一个对象o,具有属性x(因为是使用对象直接量创建的,所以对象o从Object.prototype继承对象的方法)

let p = inherit(o)  // 对象p继承对象o

p.x = 1;  // 则对象p继承对象o的属性x (并且继承Object.prototype)

 

原型链:

要查询对象p的属性x,如果p中不存在属性x,那么将会继续在p的原型对象(即对象o)中查询属性x。如果原型对象也没有属性x,那么继续在这个原型对象的原型上查询,直到找到x或者查找到一个原型是null对象为止。

可以看到对象的原型属性构成了一个“链”,可以通过这个“链”实现属性的继承。这一系列的原型对象就是所谓的“原型链”(prototype chain)。

 

假设给对象p的属性x赋值会如何呢?

1.已存在属性x(不是继承来的)  那么只改变这个已有属性x的值

2.不存在属性x  添加一个新属性x

3.继承自属性x  那么这个继承的属性就会被新创建的同名属性覆盖了

如果p继承自一个只读属性x,那么赋值操作是不允许的。

如果允许属性赋值操作,它也总是在原对象上创建属性或对已有的属性赋值,而不会去修改原型链。

 

getter和setter

对象属性是由名字,值和一组特性(attribute)构成的。在ES5中,属性的值可以用一个或两个方法替代,这两个方法就是getter和setter(和数据属性一样可以继承)

由getter和setter定义的属性叫“存取器属性”(accessor property),它不同于“数据属性”(data property),数据属性只有一个简单的值

getter方法  // 查询存取器属性的值(无参数)

setter方法  // 设置存取器属性的值

当程序设置一个存取器属性的值时,JavaScript调用setter方法,将赋值表达式右侧的值当作参数传入setter。从某种意义上讲,这个方法负责“设置”属性值。可以忽略setter方法的返回值。

存取器属性定义为一个或者两个和属性名同名的函数,没有function关键字,而是使用get或set

例子:

let number = {
  num: 0,
  get next() { return this.num++; },  // next是属性名
  set next(n) {
    if (n >= this.num) this.num = n;
    else throw '值不能小于等于当前值';
  }
};


number.next;  // => 0  num => 1  调用get
number.next;  // => 1  num => 2  调用get
number.next;  // => 2  num => 3  调用get
number.next = 2  // 抛出异常  调用set

 

属性的特性

查询和设置属性特性的API,对于库的开发者来说非常重要。因为:

1.可以通过这些API给原型对象添加方法,并将他们设置成不可枚举的,这让他们看起来更像内置方法。

2.可以通过这些API给对象定义不能修改或删除的属性,借此“锁定”这个对象。

 

为了实现属性特性的查询和设置操作,ES5中定义了一个名为“属性描述符”(property descriptor)的对象

描述符对象的属性和他们所描述的属性特性是同名的。因此:

数据属性的描述符对象的属性有:value,writable,enumerable,configurable(其中writable,enumerable,configurable都是布尔值)

存取器属性的描述符对象的属性有:get,set,enumerable,configurable(get属性和set属性是函数值)

 

序列化对象

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

JSON.stringify(obj)  对象序列化  数组也可以

JSON.parse(str)  字符串转对象

(两个一起用就是深拷贝)

 

对象的方法 

obj.hasOwnProperty(prop)   检测某个属性是否为该对象的自有属性

obj.propertyIsEnumerable(prop)  检测某个属性是否为该对象的自有属性,且这个属性的可枚举性为true时他才返回true(上面一个方法的增强版)

p.isPrototypeOf(o)   检测对象p是否是对象o的原型(或处于原型链中)(功能和instanceof运算符类似)

Object.getPrototypeOf(obj)  可以查询对象原型

Object.keys()  返回一个数组,这个数组由对象中可枚举的自有的属性名称组成(而不是继承属性)

Object.getOwnPropertyNames()  返回一个数组,数组由对象中所有自有属性的名称组成,包括不可枚举属性

Object.getOwnPropertyDescriptor(obj, prop)  查询对象自有属性中某个属性的特征

Object.defineProperty(obj, prop, 描述对象)  修改或新建属性的特性(只能修改自有属性)(参数描述对象不必包含所有4个特性)

Object.isExtensible(obj)  判断某个对象上是否可以添加新属性

Object.preventExtensions(obj)  禁止在一个对象上添加新的属性

Object.seal(obj)  阻止添加或删除对象的属性

Object.isSealed(obj)  判断对象的属性是可添加或删除,如果是封闭的状态返回true(即检测是否为封闭对象)

Object.isFrozen(obj)  判断对象是否不可改变,如果已冻结返回true

obj.toString()  没有参数,返回一个表示调用这个方法的对象值的字符串

obj.valueOf()  当需要将对象转换为某种原始值而非字符串的时候才会调用它,尤其是转换为数字的时候

posted @ 2020-08-31 15:02  或许从前  阅读(121)  评论(0)    收藏  举报