面向对象
2022年10月14日大约 7 分钟
理解对象
属性类型
ECMAScript-262 第五版在定义只有内才用的特性时,描述了属性的各种特征。ECMAScript-262 定义这些特性是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问和他们。为了表示特性是内部值,该规范把他们放在了两对儿方括号中,例如 [[Enumerable]]
数据属性
[[Configurable]]
:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。- 一旦此属性定义为
false
,则不能再把它变回可配置,此时在调用Object.defineProperty()
只能修改writable
特性
- 一旦此属性定义为
[[Enumerable]]
:表示能否通过 for-in 循环返回属性。[[Writable]]
:表示能否修改属性的值。[[Value]]
:包含这个属性的数据值。读取属性值的时候从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。Object.defineProperty()
:要修改属性默认的特性,必须使用此方法- 接受三个参数:属性所在对象、属性的名字、和一个描述符对象
- 描述符对象的属性必须是:configurable、enumerable、writable、value。设置其中的一个或多个值可以修改对应的特性值。
- 接受三个参数:属性所在对象、属性的名字、和一个描述符对象
访问器属性
- 访问器属性不包括数据值;他们包含一对儿 getter 和 setter 函数(饭不是必须的)
- 在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;
- 在写入访问器属性时,会调用 setter 函数,这个函数负责决定如何处理;数据
- 访问器属性特性
[[Configurable]]
:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。[[Enumerable]]
:表示能否通过 for-in 循环返回属性。[[Get]]
:在读取属性时调用的函数,默认值为 undefined。[[Set]]
:在写入属性时调用的函数,默认值为 undefined。
Object.defineProperty()
:要定义访问器属性,必须使用此方法- 接受三个参数:属性所在对象、属性的名字、和一个描述符对象
- 描述符对象的属性必须是:configurable、enumerable、writable、value。设置其中的一个或多个值可以修改对应的特性值。
- 接受三个参数:属性所在对象、属性的名字、和一个描述符对象
<script>
var book = {
_year: 2004, // 前面加 '_' 表示只能通过对象方法访问的属性
edition: 1
}
Object.defineProperty(book, 'year', {
get: function () {
return this._year
},
set: function (newValue) {
if (newValue > 2004) {
this._year = newValue
this.edition += newValue - 2004
}
}
})
book.year = 2005
console.log(book.year) // 2005
console.log(book.edition) // 2
</script>
定义多个属性
Object.defineProperties()
:为对象定义多个属性- 接收两个对象参数
- 第一个对象是要添加和修改其属性的对象
- 第二个对象的属性与第一个对象中要添加或修改的属性一一对应。
- 接收两个对象参数
读取属性的特性
Object.getOwnPropertyDescriptor()
:获取给定属性的描述符- 接收两个参数
- 属性所在的对象
- 要读取其描述符的属性名称
- 返回一个对象
- 如果是数据属性,这个对象的属性有 configurable、enumerable、writable、value
- 如果是访问器属性,这个对象的属性有 configurable、enumerable、get、set
- 接收两个参数
- 在 JavaScript 中,可以针对任何对象使用
Object.getOwnPropertyDescriptor()
,包括 BOM 和 DOM
创建对象
工厂模式
<script>
// 工厂模式
// 优点:解决了创建多个相似对象的问题
// 缺点:没有解决对象识别的问题( 即怎样知道一个对象的类型 )
function creatPerson(name, age, job) {
var obj = new Object()
obj.name = name
obj.age = age
obj.job = job
return obj
}
var person1 = creatPerson('sunzhenyang', 25, 'web')
console.log(person1)
</script>
构造函数模式
<script>
// 构造函数模式
// 优点:可以将它的实例标识为一种特点定的类型
// 缺点:每个方法都要在每个实例上重新创建一遍
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
}
/* new 操作符操作步骤
* 1. 创建一个新对象
* 2. 将构造函数的作用域赋给新对象( 因此 this 就指向了这个新对象 )
* 3. 执行构造函数中的代码( 为这个对象添加属性 )
* 4. 返回新对象
*/
var person1 = new Person('gaoyue', 26, 'kefu')
var person2 = new Person('gaomeili', 25, 'contact')
console.log(person1)
console.log(person2)
console.log(person1.constructor == Person)
console.log(person2.constructor == Person)
</script>
- 对象的 constructor 最初是用来表示对象类型的,但是,还是使用 instanceof 操作符更可靠一些
- 创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,这也正是构造函数模式胜过工厂模式的地方
- 以这种方式定义的构造函数是定义在 Global 对象(在浏览器中是 window 对象)中的。
将构造函数当做函数
- 构造函数与其他函数唯一的区别,就在于调用他们的方式不同。
- 构造函数依然势函数,不存在定义构造函数的特殊语法
- 任何函数吗,只要通过 new 操作符来调用,那他就可以作为构造函数
- 任何函数,如果不通过构造函数来调用,那它和普通函数也没有区别
原型模式
- 每个被创建的函数都有
prototype
(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。按字面意思理解,那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。 - 使用原型对象的好处就是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
理解原型对象
- 只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象
- 默认情况下,所有原型对象都会自动获取一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。通过这个构造函数,我们还可以继续为原型对象添加其他属性和方法。
- 创建了自定义构造函数之后,其原型对象默认只会取得 constructor 属性,至于其他方法,则都是从 Object 继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262 第五版管这个指针叫 [[Prototype]]
isProtptypeOf()
:从本质上讲没如果 [[Prototype]] 指向调用 isPrototypeOf() 方法的对象(Proson.prototype),那么这个方法就返回 true- ECMAScript 5 增加了一个新方法,叫
Object.getPrototypeOf()
,返回 [[Prototype]] 的值 - 可以通过对象实例访问保存在原型中的值,却不能通过对象实例重写原型中的值。
- 如果在实例中有与实例原型相同的属性名,则该属性会屏蔽原型中的属性
- 使用 delete 操作符可以完全删除实例属性,从而让我们能够重新访问原型中的属性
hasOwnProperty()
:检测一个属性是存在于实例中还是存在于原型中,这个方法(不要忘了它是从 Object 继承来的)只在给定属性存在于对象实例中时,才会返回 truein
:操作符只要通过对象能够访问到属性就返回 trueObject.keys(obj)
:取的对象上所有可枚举的实例属性,接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。Object.getOwnPropertyDescriptor(obj, 'name')
:返回实例属性描述符,要取得原型属性的描述符,必须在原型对象上调用 Object.getOwnPropertyDescriptor() 方法Object.keys(obj)
:得到对象上的所有可枚举的实例属性Object.getOwnPropertyNames(obj)
:得到对象上的所有实例属性,无论是否可枚举