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() 当需要将对象转换为某种原始值而非字符串的时候才会调用它,尤其是转换为数字的时候
浙公网安备 33010602011771号