# 对象
# 在变量中存放对象
通过变量读取对象中的某个属性
- 当读取的属性不存在时,会得到 undefined
- 当读取属性的对象不存在(undefined 或 null)时,程序报错
删除一个属性:
delete 变量名.属性名
属性表达式
- 给属性赋值时,或读取属性时,可以使用下面的格式:
对象变量["属性名"]
- 给属性赋值时,或读取属性时,可以使用下面的格式:
# 属性访问错误
查询一个不存在的属性并不会报错,而是会返回 undefined,但是如果对象不存在就会报错,因为 null 和 undefined 没有属性
在下列场景下给对象 o 设置属性 p 会失败:
- o 中的属性 p 是只读的:不能给只读属性重新赋值
- o 中的属性 p 是只读继承属性:不同覆盖只读的继承属性
- o 中不存在自有属性 p:如果 o 中不存在 p,并且没有 setter 方法可供调用,则 p 一定会添加到 o 中;如果 o 不是可扩展的,那么 o 中就不能定义新属性
# 创建对象
创建对象有三种方式:
- 对象字面量
- new 操作符
- 通过
Object.create()
函数来创建
# 工厂模式
function createPerson(name, age, sex) {
var o = new Object();
o.name = name;
o.age = age;
o.sex = sex;
return o;
}
var person = createPerson("a", 18, "男");
# 构造函数模式
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person("a", 18, "男");
以这种方式调用构造函数会经历 4 个步骤:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(因此 this 就指向了新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
# 原型模式
每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含属性和方法,这些属性和方法可以让所有的对象实例共享。
function Person() {}
Person.prototype.name = "张三";
var person1 = new Person();
var person2 = new Person();
person1.name // "张三"
person2.name // "张三"
&
利用原型可以提取函数的公有属性
简单的原型语法:
Person.prototype = {
// 显式创建 constructor
constructor: Person,
name: "张三",
age: 18
}
通过这种方式创建的原型本质上是重写了默认的 prototype 属性,因此这个原型的 constructor 属性不指向 Person 了,而是指向了 Object。如果需要用到 constructor 属性,就必须显式创建出来。
注意
通过此方法重设 constructor 属性会导致它的 [[Enumerable]] 特性为 true,默认情况下,原生的 constructor 是不可枚举的。
如果要使用兼容 ES5 的 JavaScript 引擎,可以试试 Object.defineProperty()
// 重设构造函数,只适用于兼容 ES5 的浏览器
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
})
# 组合使用构造函数模式和原型模式
在构造函数中定义实例属性,原型里面定义共享的属性和方法
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype = {
job: "Web 开发工程师",
boss: "LQ",
play: function() {
console.log("打球");
}
}
# 动态原型模式
把所有信息都封装到构造函数里面,通过构造函数来初始化原型(仅在必要的情况下)
function Person(name, age, sex) {
// 属性
this.name = name;
this.age = age;
this.sex = sex;
// 方法
// if 只用检查其中一个必要的方法或属性即可
if (typeof this.play != "function") {
Person.property.play = function() {
console.log("打球");
};
}
}
# 对象的特性
除了包含属性之外,每个对象还包含三个相关的对象特性:
- 对象的原型:指向另一个对象,本对象的属性继承自它的原型对象
- 对象的类:是一个标识对象类型的字符串,通过调用对象的
toString()
方法,返回[object class]
- 对象的可扩展性:标明是否可以向该对象添加新属性
# 对象的拷贝
浅拷贝:以赋值的形式拷贝对象,仍指向同一个地址,修改时原对象也会收到影响。
深拷贝:完全拷贝一个新对象,修改时不会影响原对象,有以下方法:
var newObj = JSON.parse(JSON.stringify(obj))
性能最快- 当值为函数、undefined 或 symbol 时无法拷贝
- 递归进行逐一赋值
# 原型
每一个 JavaScript 对象都和另一个对象(原型)相关联,这个原型是实例对象创建之初就设计好的:
- 所有通过对象字面量都具有同一个原型对象(Object.prototype)
- 通过
new
和构造函数调用创建的对象的原型就是构造函数的prototype
属性的值 Object.prototype
不继承任何属性- 所有的内置构造函数(以及大部分自定义函数)都具有一个继承自
Object.prototype
的原型 - 通过
Object.create()
来创建的对象使用第一个参数作为它们的原型
在 ES5 中可以将对象当作参数传入 Object.getPrototypeOf()
查询它的原型
# 理解原型对象
无论什么时候,只要创建了一个新函数,就会为该函数创建一个 prototype
属性,这个属性指向函数的原型对象。默认情况下,prototype
是一个 Object 对象,有一个属性是 constructor
,它指向构造函数本身
Person.prototype
=> 原型对象 => constructor
=> Person
最终 Person.prototype.constructor
=> Person
- 隐式原型
__proto__
- 所有的对象都有一个属性
__proto__
,称之为隐式原型 - 默认情况下,隐式原型指向创建该对象的函数的原型(prototype)
- 当访问一个对象的成员时:
- 先看对象自身是否拥有该成员,如果有直接使用
- 再看对象的隐式原型是否有该成员,如果有直接使用
- 直到原型链的末端才会停下来
- 不能重写隐式原型中的值
- 如果在实例中添加了一个属性,该属性与隐式原型中的一个属性同名,那我们就在实例中创建该属性,自动屏蔽隐式原型中的属性,但是不会修改隐式原型中的值
- 通过
hasOwnProperty
方法可以检测一个属性是存在与实例中(返回true
),还是存在与隐式原型中(返回false
)
- 所有的对象都有一个属性
# 对象混合(mixin)
// 把 obj2 混合到 obj1,产生一个新的对象
function mixin(obj1, obj2) {
var newObj = {};
// 复制 obj2 属性
for (var prop in obj2) {
newObj[prop] = obj2[prop];
}
// 找到 obj1 中有,obj2 中没有的属性
for (var prop in obj1) {
if (!(prop in obj1)) {
newObj[prop] = obj1[prop];
}
}
return newObj;
}
JS 中提供了一个方法可以混合对象:var newObj = Object.assign({}, obj1, obj2);
把 obj2 赋值给 obj1,再把 obj1 赋值给一个空对象,这个空对象现在就是 obj2 和 obj1 混合后的内容,并把它返回。
# 对象的静态方法
Object.create()
- 创建一个新的对象,使用现有对象来提供新创建的对象的
__proto__
- 语法:
Object.create(proto[, propertiesObject])
- proto:新创建对象的原型对象
- propertiesObject:可选,如果没有指定为
undefined
,则是要添加到新创建对象的不可枚举属性(即其自身的属性,而不是原型对象的属性)
- 返回一个新对象,带着指定的原型对象和属性
Object.defineProperty()
在对象上直接定义一个属性,或者修改已有属性
语法:
Object.defineProperty(obj, prop, descriptor)
- obj:要定义属性的对象
- prop:属性名
- descriptor:属性描述符
- configurable
- enumerable
- 数据描述符:一种具有值的属性
- value
- writable
- 存取描述符:由 getter-setter 函数对描述的属性
- get
- set
返回这个修改过的对象
默认情况下,使用
Object.defineProperty()
定义的属性值是不可修改的
Object.keys()
- 遍历对象,返回一个数组,包含对象中可枚举的自有属性
- 实现:
function keys (obj) {
if (typeof obj !== 'object') throw TypeError()
var result = []
for (var prop in obj) {
if (obj.hasOwnProperty) result.push(prop)
}
return result
}