常用设计模式、继承

设计模式

工厂模式

var cake1 = {
    name: '草莓味面包',
    sayName: function() {
        console.log('我是' + this.name);
    }
};
var cake2 = {
    name: '巧克力面包',
    sayName: function() {
        console.log('我是' + this.name);
    }
};

缺点:虽然对象内部的拥有相同的属性,但还是每个都要单独定义。重复造轮子。

有没有一个面包机,根据材料不同生成不同的面包?

function createCake(name, age){
    var obj = {
        name: name,
        sayName: function(){
            console.log('我是' + this.name);
        }
    }
    return obj;
}
var cake1 = createCake('草莓味面包');
var cake2 = createCake('巧克力面包');

这种创建对象的方式叫工厂模式

解决上面的缺点,但是不完美。无法得到对象的类型。

有什么办法既能解决重复创造,又有自己的类型呢?

构造函数模式

function Cake(name) {
    this.name = name;
    this.sayName = function(){
        console.log('我是' + this.name);
    }
}
var cake1 = new Cake('草莓味面包'),
    cake2 = new Cake('巧克力面包');
console.log( cake1 instanceof Cake ); //true
console.log( cake2 instanceof Cake ); //true

instanceof的作用是判断一个对象是不是某个类型的实例

所有生产的面包不够是草莓味还是巧克力味,都是Cake牌面包。

  1. Cake是个函数, 当 new Cake()的时候就会把Cake做为构造函数,构造对象。
  2. 用Cake创建对象后,Cake函数里的this代表 用Cake面包机生成的面包
  3. 【扩展知识】Cake是个函数,同时函数也是个对象,它是Function创建的实例(Cake instanceof Function === true)。可以这样理解,面包机也是个对象,它是生成面包机的机器创建的实例。

上面的已经比较完美了,但是还有点小小的遗憾。每个面包都有sayName这个函数,而函数的功能是一样的。如果有1万面包就有1万个函数,太浪费了。 这些面包能共用一个sayName函数吗?

可以用原型方式创建。

原型方式

几个概念需要知道:

1.函数也是对象,对象是有属性和值的,任何一个函数都有prototype这个属性,这个属性的值也是对象,叫原型对象。 可以这样理解,面包机有个附加装置存储奶油,奶油就放在面包机的prototype空间里。每次生产的面包都会抹上同一块奶油。

function Cake(name){
    this.name = name;
}
console.log(Cake.prototype);
Cake.prototype = { other: '奶油' };

2.当创建一个对象后,该对象拥有创建者赋予的所有功能。同时该对象还有个额外的内部属性,指向构造函数的原型对象。可以这样理解,草莓面包拥有面包机创建的属性,同时还有一个额外的属性__proto__,指向面包机赠送的奶油。

var cake1 = new Cake('草莓');
console.log(cake1.__proto__); //这里是演示,最好不要直接访问
//console.log( Object.getPrototypeOf(cake1) );  //这是正确的写法
consoele.log(cake1.other);

var cake2 = new Cake('巧克力');
console.log(cake2.__proto__);
console.log(cake2.other);

Cake.prototype.other = "蜡烛";
console.log(cake1.other);
console.log(cake2.other);

就把原型对象理解为构造器为对象赠送的公共区就行了。

再看看原型方式创建对象

function Cake(name){
    this.name = name;
}
Cake.prototype = {
    other: '蜡烛',
    sayName: function(){
        console.log('我是' + this.name);
        console.log('我还有' + this.other);
    }
}

var cake1 = new Cake('草莓面包'),
    cake2 = new Cake('巧克力面包');
cake1.sayName();
cake2.sayName();
cake1.other = "奶油";
console.log( cake2.other );

原型方式创建对象的原则是,把所有对象使用的公共资源放在原型对象。

上面的写法有点问题: prototype原型对象里面还有其他信息,上面的方式把原型对象覆盖了。 可以写成:

Cake.prototype.sayName = function() {};

回顾一下我们之前写的轮播组件

function Carousel(opts){
    this.init(opts);
}
Carousel.prototype={
  init: function(opts){
  },
  ...
};

拓展知识

instanceof

怎样知道一个对象obj是不是某个构造函数Fun创建的呢?

看这个对象的原型是不是在原型链上,简单来看就是:看这个对象obj的原型obj.__proto__ 是不是等于 Fun的原型对象 Fun.prototype

function Dog(){}
function Cake(){}

var c1 = new Cake();
console.log( c1.__proto__ === Cake.prototype ); //因为二者相等,所以
console.log( c1 instanceof Cake );  //true

Cake.prototype = {};
var c2 = new Cake();
console.log( c2 instanceof Cake );  //true
consoel.log( c1.__proto === Cake.prototype ); //不相等了,所以
console.log( c1 instanceof Cake );   //false

constructor

任何函数都有个原型属性prototype,指向原型对象。

原型对象里默认有个constructor属性,指向原函数。

console.log(People.prototype.constructor);  // People
People.prototype.constructor === People;  //true

所有我们在覆盖原型对象时要修改过constructor

function Cake(name){
    this.name = name;
}
Cake.prototype = {
    constructor: Cake,
    other: '蜡烛',
    sayName: function(){
        console.log('我是' + this.name);
        console.log('我还有' + this.other);
    }
};

原型图

重要参考1

重要参考instance

重要参考constructor

继承

原型链

每个够着函数都有一个原型对象,原型对象包含一个指向该构造函数的指针。构造函数生成的实例包含一个指向该原型对象的指针。假如我们让原型对象等于另一个类型的实例,此时原型对象将包含两一个原型对象的指针,层层递进,就够成了原型链

对象继承

一个对象有了基本的属性,如果需要扩展这些属性,可以使用继承

function Cake(name){
    this.name = name;
}
Cake.prototype.sayName = function(){
    console.log( '我是' + this.name );
}

function MilkCake( name, size ){
    Cake.call(this, name); //相当于 this.name = name;
    this.size = size;
} 
MilkCake.prototype = new Cake();
MilkCake.prototype.contructior = MilkCake;
MilkCake.prototype.saySize = function(){
    console.log('我的size是:'+ this.size);
}

通过构造函数继承属性,通过原型链继承方法。

数据继承

var obj1 = {name: '小灰', age: 1},
    obj2 = {name: '小呆', sex: 'male'};
var newObj1 = $.extend(obj1, obj2);   //会把obj2合并覆盖到obj1,obj1被改掉
var newObj2 = $.extend({}, obj1, obj2);  //不会改掉obj1
  console.log(newObj1);
  console.log(newObj2);