第三章:对象
语法
-声明(字面)形式
var myObj = {
key: value
//一次性可以添加多个键/值对,常用
};
-构造形式
var myObj =newObject();
myObj.key = value;//必须一个个添加属性
类型
-对象是大多数JS程序依赖的基本构建块
-JS六种主要类型(语言规范中的”语言类型”)
1.string
2.number
3.boolean
4.null
5.undefined
6.object
-简单基本类型(12345)自身不是object
-null实际上是它自己的基本类型,但typeofnull会错误返回”object”(bug)
-错误论断:JavaScript中的一切都是对象
-复杂基本类型
1.function对象子类型(技术上称”可调用对象”),“头等(first class)”类型
2.数组内容组织上更加结构化
内建对象
-其他的对象子类型称为内建对象
1.String
2.Number
3.Boolean
4.Object
5.Function
6.Array
7.Date
8.RegExp
9.Error
-仅仅是内建的函数,都可以被用作构造器(new),结果是一个新构建的相应子类型的对象
-必要时语言会自动将“string”基本类型强制转换为String对象类型
-JS社区绝大部分人强烈推荐尽可能使用字面形式的值
-Date值仅可以由它们的构造对象形式创建(没有对应的字面形式)
-仅仅在你需要使用额外的选项时使用构建形式
内容
-对象的内容由存储在特定命名的位置上的(任意类型的)值组成
-称这些值为属性
-值通常不存储在容器对象内部,存的是属性名称
-像指针一样(reference引用)指向值存储的地方
var myObject = {
a:2
};
myObject.a; // 2
myObject["a"]; // 2
-.a:“属性(property)”访问
1.a需要是一个标识符(Identifier)兼容的属性名
2.不合法只能用键访问
-[“a”]:“键(key)”访问
1.对象中属性名总是字符串
2.对象和数组使用的数字别搞混,都会使字符串
计算型属性名
-将计算表达式作为键名称要用键访问
-ES6加入了计算型属性名,指定表达式为名称
var prefix ="foo";
var myObject = {
[prefix +"bar"]:"hello",
[prefix +"baz"]:"world"
};
myObject["foobar"]; // hello
myObject["foobaz"]; // world
-最常见用法用于ES6的Symbol
1.新的基本类型
2.强烈不鼓励使用实际值(理论上因JS引擎不同而不同)的情况
属性(Property)vs.方法(Method)
-访问对象的属性都是一个属性访问(函数也是属性访问,不会在访问时成为方法)
-函数不会属于对象,只是函数的一个引用
-ES6加入了super引用(静态绑定通常和class一起使用)
数组
-组织更加结构化,数字索引(下标)
-可以添加命名属性,不会改变length的值
-命名为数字会成为数字索引,改变数组内容(length改)
复制对象
-浅(shallow copy引用)拷贝深(deep copy)拷贝
-不同JS框架标准不同
-一个解决方案,JSON安全的对象
1.被序列化为一个JSON字符串,之后还可以被重新解析为相同结构和值的对象
2.ES6为浅拷贝定义了Object.assign(...)
-接收目标对象作为第一个参数,一个或多个源对象作为后续参数
-源对象上迭代所有的可枚举enumerable,own keys(直接拥有的键)
-拷贝到目标对象(仅通过=赋值)
-任何在源对象属性的特殊性质在目标对象都不会保留
属性描述符(Property Descriptors)
-ES5中所有的属性都用属性描述符来描述
-创建一个普通属性,可以有加上各种性质
-可以用Object.defineProperty()来添加新属性,或用期望的性质修改既存的属性
var myObject = {};
Object.defineProperty( myObject, "a", {
value:2,
writable:true,
configurable:true,
enumerable:true
} );
myObject.a; // 2
-可写性(Writable)
1.控制你改变属性值的能力
var myObject = {};
Object.defineProperty( myObject, "a", {
value:2,
writable:false, // 不可写!
configurable:true,
enumerable:true
} );
myObject.a =3;
myObject.a; // 2 use strict会得到一个错误TypeError
-可配置性(Configurable)
1.可配置可以使用相同的defineProperty()工具修改描述符定义
2.false单向不可撤销,再调用defineProperty()会发生TypeError
3.false可写性可以从true到false,反向不行
4.false阻止使用delete操作符移除既存属性的能力
5.delete移除引用,仅仅是一个对象属性移除操作,用于释放内存不恰当
-可枚举性(Enumerable)
1.控制一个属性是否能在特定的对象-属性枚举操作中出现
不可变性(Immutability)
-有时我们希望将属性或对象设置为不可改变的
-ES5有几种不同的微妙方式
1.创建的都是浅不可变性
2.仅影响对象和它的直属属性的性质
3.拥有其他对象的引用不会受影响,仍然可变
-对象常量(ObjectConstant)
1.writable:false和configurable:false组合
2.创建一个作为对象属性的常量(不能被改变,重定义或删除)
-防止扩展(PreventExtensions)
1.防止被添加新属性,并保留其他既存的对象属性
2.调用Object.preventExtensions(..)
3.非strict mode无声失败,strict mode抛出TypeError
-封印(Seal)
1.调用Object.preventExtensions(..)并configurable:false
2.不能添加,配置,删除属性,可以修改值
-冻结(Freeze)
1.调用Object.seal(..)并writable:false
2.从自身获得的最高级别的不可变性
3.深度冻结:递归全部冻结
[[Get]]
1.属性访问细节
var myObject = {
a:2
};
myObject.a; // 2
2.先执行一个[[Get]]操作,对象中找到属性返回相应的值
3.(遍历[[Prototype]]链)如果任何方法都找不到,返回undefined
4.引用在可用词法作用域无法解析的变量会抛出ReferenceError
[[Put]]
1.给一个对象的属性赋值
2.属性存在,[[Put]]算法会大致检查:
1.属性时访问器描述符吗,是且是setter,调用setter
2.writable为false吗,是在非strict下无声失败,strict下抛出TypeError
3.否则像平常一样设置既存属性的值
Getters与Setters
-ES5引入的方法覆盖put,get这些默认操作的一部分,针对属性
-Getter调用一个隐藏函数来取得值的属性
-Setter调用一个隐藏函数来设置值的属性
-一个属性定义为拥有getter/setter,定义就成为了”访问器描述符”
-value和writable性质没有意义而被忽略,而是set,get(configurable,enumerable)性质
var myObject = {
// 为 `a` 定义 getter
geta() {
returnthis._a_;
},
// 为 `a` 定义 setter
seta(val) {
this._a_ = val *2;
}
};
myObject.a =2;
myObject.a; // 4
存在性(Existing)
-in操作符会检查属性是否存在于对象中或[[Prototype]]链对象遍历的更高层中
-hasOwnProperty(..)仅仅检查是否拥有属性,不检查[[Prototype]]链
-枚举(Enumeration)
1.如果对象的属性被迭代时会被包含在内
2.propertyIsEnumerable(..)测试一个给定属性名是否直接存于对象上且可枚举
3.Object.keys(..)返回一个所有可枚举属性的数组
4.Object.getOwnPropertyNames(..)返回一个所有属性的数组
迭代(Iteration)
-数字索引的数组中,典型的迭代办法是使用标准的for循环
var myArray = [1, 2, 3];
for (var i =0; i < myArray.length; i++) {
console.log( myArray[i] );
}
// 1 2 3
-ES5加入了几个迭代帮助方法
1.forEach(..)迭代所有值,忽略回调的返回值
2.every(..)迭代到最后或当回调返回一个false(falsy)值
3.some(..)迭代到最后或回调返回一个true(truthy)值
-ES6加入了for..of循环语法用来迭代数组和有定义迭代器的对象
var myArray = [ 1, 2, 3 ];
for (var v of myArray) {
console.log( v );
}
// 1
// 2
// 3
1.要被迭代的东西提供一个迭代器对象
2.每次循环调用一次迭代器对象的next()对象
3.@@iterator本身不是迭代器对象,是一个返回迭代器对象的方法