对象属性
一、属性类型
ECMAScript 中有数据属性
和访问器属性
两种属性,各有 4 个描述其行为的特征,其中两种属性都有的特性是:
- [[configurable]] 默认为 true,能否删除属性、修改属性特征、修改属性类型
- [[enumerable]] 默认为 true,是否可枚举,即被 for-in 循环、Object.keys 等遍历到
数据属性
同时可具有:
- [[value]] 默认为 undefined,数据值
- [[writable]] 默认为 true,能否修改属性的值
访问器属性
同时可具有:
- [[get]] 默认为 undefined,读取属性时调用的 getter 函数
- [[set]] 默认为 undefined,读取属性时调用的 setter 函数
需要注意的是,数据属性
和访问器属性
不能同时存在
可以是用 Object.defineProperty
或者Object.defineProperties
来修改对象的属性,在使用这两个函数的时候,如果不指定,configurable
、enumerable
、writable
的默认值都为 false
原型
实例.__proto__ === 原型
原型.constructor === 构造函数
构造函数.prototype === 原型
原型链
每个对象都有 __proto__
属性,指向了创建该对象的构造函数的原型,__proto__
将对象连接起来组成了原型链,原型链可以用于实现继承
和共享属性
继承
一、原型链继承
- 优点:简单
- 缺点:多个实例共享引用类型的属性
function Parent(age) {
this.names = []
this.age = age
}
Parent.prototype.foo = () => {}
function Child() {}
Child.prototype = new Parent()
var child1 = new Child(5)
var child2 = new Child(6)
child1.names.push(1)
child2.names.push(2)
二、构造函数
- 优点:
1.不共享引用类型的属性
2.可传参 - 缺点:
1.每次创建实例都会重新创建一遍方法,函数没有复用
2.不属于原型链的一环,子类实例不能使用父类的方法
function Parent(age) {
this.names = []
this.age = age
this.foo = function () {}
}
function Child(age) {
Parent.call(this, age)
}
var child = new Child(11)
三、组合继承
- 优点:
1.原型链确保了函数复用
2.构造函数实现了对实例属性的继承 - 缺点:
1.调用了两次父类构造函数
function Parent(age) {
this.name = 'Bill'
this.age = age
}
Parent.prototype.foo = function () {}
function Child(age) {
Parent.call(this, age) //第二次调用构造函数
}
Child.prototype = new Parent() //第一次调用构造函数
Child.prototype.constructor = Child
var child = new Child(11)
四、原型式继承
与原型链类似
- 优点:简单
- 缺点:多个实例共享引用类型的属性
const objectCreate = (o) => {
// 就是Object.create 静态方法的实现
// 实际上只是对 o 进行了浅拷贝
function F() {}
F.prototype = o
return new F()
}
五、寄生式继承
与借用构造函数类似
- 缺点:每次创建实例都会重新创建一遍方法,函数没有复用
function create(o) {
var f = Object.create(o)
f.foo = function() {}
return f
}
六、寄生组合式继承
主要是改进了组合继承需要调用两次构造函数的缺点
function object(o) {
function F() {}
F.prototype = o
return new F()
}
// 这里代替了用来 组合式继承 第一次调用构造函数
// 将父类的原型进行一次浅拷贝
// 并将这个浅拷贝的对像作为原型链接到子类
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
function SuperType(name) {
this.name = name
this.colors = ['red', 'blue']
}
SuperType.prototype.sayName = function() {
alert(this.name)
}
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
SubType.prototype.sayAge = function() {
alert(this.age)
}
inheritPrototype(SubType, SuperType)