创建对象
工厂模式
缺点:无法解决对象识别,不知道创建对象的类型(Cat or Dog)
function createObj(name, age, like)
{
let o = {}
o.name = name
o.age = age
o.like = like
o.say = function () {
return this.name
}
return o
}
let per1 = createObj('aa', 23, 'ccc');
let per2 = createObj('bb', 18, 'ddd');
构造函数模式
function Person(name, age)
{
this.name = name
this.age = age
this.say = function () {
return this.name
}
}
let pers1 = new Person('aa', 12)
let pers2 = new Person('bb', 13)
new的过程
- 创建一个新对象
- 构造函数的作用域赋给这个新对象(this指向新对象)
- 执行构造函数的代码(为这个对象添加属性和方法)
- 返回新对象
缺点:构造函数里面的方法反复重新定义
// 构造函数改进
function Person(name, age)
{
this.name = name
this.age = age
}
Person.prototype.say = function () {
return this.name
}
let pers3 = new Person('cc', 14)
pers3.constructor // Person
// 实例的原型指向构造函数的原型对象
pers3.__proto__ === Person.prototype // true
原型模式
function Person() {}
Person.prototype.name = 'Grid';
Person.prototype.age = 23;
Person.prototype.sayName = function () {
return this.name;
}
var per1 = new Person();
var per2 = new Person();
console.log(per1.name) // Grid
console.log(per2.name) // Grid
console.log(per1.sayName()) // Grid
console.log(per2.sayName()) // Grid
理解原型对象
- 无论什么时候只要我们创建了一个新函数,JS就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向该函数的原型对象!
- 所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针
function Person() {}
Person.prototype.constructor // ƒ Person() {}
- 原型对象默认只有
constructor
属性,其他属性都是从Object对象继承而来
原型的一些函数
1. isPrototypeOf() // 检测一个对象的原型指向
Person.prototype.isPrototypeOf(per1) // true
2. Object.getPrototypeOf() // 获取一个对象的原型对象
Object.getPrototypeOf(per1) // Person.prototype
3. hasOwnProperty() // 检测一个实例对象是否有某个属性,只有在实例对象中才返回true,原型中返回false
function Person1() {}
Person1.prototype.name = 'proName'
var per11 = new Person1()
per11.hasOwnProperty('name') // false
per11.name = "objName"
per11.hasOwnProperty('name') // true
原型与in操作符
对象的原型是可以遍历的,通过for-in
遍历
in也是可以单独使用,如下
function Person() {}
Person.prototype.name = 'aa'
var per = new Person()
console.log('name' in per) // true
- Object.getOwnPropertyNames()的使用
function Person() {}
Person.prototype.name = 'aa'
Person.prototype.age = 12
Person.prototype.sex = 'boy'
Person.prototype.say = function () {
return this.name
}
var per = new Person()
per.like = 'gril'
console.log(Object.getOwnPropertyNames(per)) // ['like']
console.log(Object.keys(per)) // ['like']
console.log(Object.getOwnPropertyNames(Person.prototype)) // ["constructor", "name", "age", "sex", "say"]
console.log(Object.keys(Person.prototype)) // ["name", "age", "sex", "say"]
- in操作符号和hasOwnProperty()结合可以检测一个属性是在原型还是对象
function hasPrototypeOwnProperty(obj, name)
{
return (name in obj) && !obj.hasOwnProperty(name)
}
// true: 在原型,false:在实例对象
组合使用构造函数模式和原型模式
通过构造函数和原型模式结合,实例对象可以访问单独的属性和共享在原型对象的属性和方法
这种也是目前最为常用的一种定义引用类型的方法
function Person(name, age)
{
this.name = name
this.age = age
this.friends = ['red', 'blue']
}
Person.prototype = {
constructor: Person,
say: function () {
console.log(this.name)
}
}
var per1 = new Person('zhy', 23)
var per2 = new Person('yhz', 12)
per1.friends.push('green')
console.log(per1.friends) // ["red", "blue", "green"]
console.log(per2.friends) // ["red", "blue"]
动态原型模式
在构造函数利用if语句初始化原型
function Person(name, age) {
this.name = name
this.age = age
// 动态原型开始
if(typeof this.say != 'function') {
Person.prototype.say = function () {
console.log(this.name)
}
}
// 动态原型结束
}
注意:不必使用if检查每个属性和方法,只需要检查一个即可。这里修改的原型只在第一次实例化的时候调用,同时会快速生效到每个实例化对象上。
寄生构造函数模式
根据字面意思很好理解,好比寄生蟹不是真的螃蟹?因此寄生构造函数也算不得是真正的构造函数,就是构造函数套壳实例对象
function Person(name, age, like)
{
var o = new Object()
o.name = name
o.age = age
o.like = like
return o
}
var per = new Person('zz', 12, 'cat')
console.log(per) // {name: "zz", age: 12, like: "cat"}
可以看到里面返回的是内部实例化的对象,返回对象和构造函数没关系了,原型不在指向构造函数的原型对象,并且instanceof
也不再是Person
了,因此不建议和其他的创建类一起使用
虽然寄生构造函数有上述的缺点,但是它可以为对象创建构造函数,如下:
function SpecialArray()
{
var values = new Array()
values.push.apply(values, arguments)
values.toPipedString = function () {
console.log(this.join('|'))
}
return values
}
var arr = new SpecialArray(1,2,3)
arr.toPipedString() // 1|2|3
如上,我们为Array增加了一个拼接的方法,类似的我们也可以为String、Date等甚至是我们自定义的类添加一些额外的属性和方法。
稳妥构造函数模式
稳妥对象
:没有公共属性,而且其方法也不引用this的对象
function Person(name, age)
{
var o = new Object()
o.name = name
o.age = age
o.sayName = function () {
console.log(name)
}
return o
}
var per = Person('aa', 12)
console.log(per.sayName()) // aa
缺点和寄生构造函数一样
继承
原型链
借用构造函数
function SuperType(name)
{
this.name = name
}
function SubType(age)
{
SuperType.call(this, 'xxx') // 此处使用apply也是可以
this.age = age
}
var sub = new SubType(12)
console.log(sub) // SubType {name: "xxx", age: 12}
- 可以解决参数传递问题了,并且在子类自定义了单独的属性,防止构造函数重写子类的属性
- 构造函数模式把方法都写在构造函数里面了,因此方法不能复用并且子类访问不到超类的方法
组合继承
利用原型链和构造函数实现对超类原型对象上属性和方法继承,利用构造函数实现对实例属性的继承。
function SuperType(name)
{
this.name = name
this.like = ['杨幂', '刘亦菲', '唐嫣']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age)
{
SuperType.call(this, name) // 此处使用apply也是可以
this.age = age
}
SubType.prototype = new SuperType()
SubType.prototype.sayAge = function () {
console.log(this.age)
}
var sub = new SubType('xingchen', 26)
console.dir(sub)
sub.sayName() // xingchen
sub.sayAge() // 26
sub.like.push('张韶涵')
var sub1 = new SubType('chenxing', 25)
sub1.sayName() // chenxing
sub1.sayAge() // 25
SuperType.prototype.isPrototypeOf(sub) // true
SuperType.prototype.isPrototypeOf(sub1) // true
SubType.prototype.isPrototypeOf(sub1) // true
SubType.prototype.isPrototypeOf(sub) // true
原型式继承
function object(o)
{
function F() {}
F.prototype = o
return new F()
}
寄生式继承
和原型式继承是差不多的形式,最终都是返回一个对象,但是内部实现却有点不一样,寄生式继承不会再去new一个原型函数,但是原型式继承需要new一个内部的原型函数,然后重写它的原型对象
寄生式继承则直接浅复制一个对象,然后在根据需要设置属性和方法,在进行返回
function createAnthor(obj)
{
var clone = Object.create(obj)
clone.sayName = function () {
console.log(this.name)
}
return clone
}
var obj = {
name: 'aa',
age: 13
}
var objAnthor = createAnthor(obj)
console.log(objAnthor)
objAnthor.sayName() // aa
寄生组合式继承
目前使用最多的方式,解决了组合继承2次实例化超类的问题,使用寄生继承的方式解决了2次new
的问题
function inhertPrototype(SubType, SuperType)
{
var prototype = Object.create(SuperType.prototype)
prototype.constructor = SubType
SubType.prototype = prototype
}
function SuperType(name)
{
this.name = name
this.like = ['杨幂', '刘亦菲', '唐嫣']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age)
{
SuperType.call(this, name) // 此处使用apply也是可以
this.age = age
}
// 这行是要被替换的
// SubType.prototype = new SuperType()
inhertPrototype(SubType, SuperType)
SubType.prototype.sayAge = function () {
console.log(this.age)
}
var sub = new SubType('aaa', 23)
console.log(sub)