原型

原型是一个对象 其他对象可以通过它实现属性继承 对象分为两种:普通对象 函数对象
prototype是函数对象才拥有的属性

普通对象:
var o={};
typeof o ======= "object" 


函数对象:
function f(){}
typeof f ========"function"
var f1=new f();
typeof f1======= "object" ;//实例也是普通对象
具体原型对象:
function Person(){};  //定义一个函数对象
 Person.prototype.name="abc";  //原型对象中添加属性
 Person.prototype.age = 18;
 var p1 = new Person();  //实例化
 var p2 = new Person();

总结:1、每一个函数对象都有一个prototype属性,但是普通对象没有,prototype下面又有constructor,指向这个函数;
2、每一个对象都有一个__proto__内部属性,指向它所对应的构造函数的原型对象

关于为什么要使用原型的思考过程:

//初始化的想法
var o={
    name:"1",
    age:18
}
var o1={
    name:"1",
    age:20
}
//缺点:不灵活麻烦  对象没有关联


function person(name,age){
    return {
        name:name,
        age:age,
        sex:"男"
    }
}
//封装成一个方法 不同的项通过传值  相同的项写死
var o3=person("ceshi",18);
var o4=person("test",19);
//缺点:不能反映出它们是同一个原型对象的实例



//构造函数
function Person(name,age){
    this.name=name;
    thia.age=age;
}
Person.prototype.type="test";
Person.prototype.eat=function(){
    console.log("吃饭")
};
var p1=new Person('zhangsan',18);
var p2=new Person('lisi',20);
p1.type
p2.eat();


//p1.__proto__===Person.prototype                     true
//Person.prototype.constructor===Person               true

//在实例去调用的时候如何知道是否是本身的属性还是原型的属性
//使用in  hasOwnProperty
// in:只要存在于本身和原型上面都是为true
// hasOwnProperty:只能判断是否存在于本身
console.log('name' in p1);//true
console.log(p1.hasOwnProperty("name"));//true
console.log('type' in p1);//true
console.log(p1.hasOwnProperty("type"));//false
继承
// ---------------------------------方式一------------------------------------
function Animate(){
    this.type="动物";
    this.eat=function(){
         console.log("啃骨头");
    }
}

function Dog(name,age){
    this.name=name;
    this.age=age;
    
}
Dog.prototype=new Animate();
var dog=new Dog("Tony",3);
console.log(dog.type);
// ---------------------------------方式二------------------------------------
function Animate(){
    this.type="动物";
     this.eat=function(){
        console.log("啃骨头");
    }
}

function Dog(name,age){
    Animate.apply(this);//使用apply获取继承属性
    console.log("this",this);
    this.name=name;
    this.age=age;
}
var dog=new Dog("Tony",3);
console.log(dog.type,dog.eat());
apply call bind
// call和apply都是对函数的直接调用,不同的是传的第二个参数不同
function fn(name,age,text) {
    this.name=name;
    this.age=age;
    this.text=text;
    console.log(this.name+"今年"+this.age+","+this.text);
}
var o={};
// fn.call(o,"zhangsan",18,"喜欢打篮球");//call  调用fn函数并且将函数内部的this指向o对象,是的o对象也具备fn的属性和方法
// fn.apply(o,["zhangsan",18,"喜欢打篮球"]);//apply  和call的区别就是传参不一样
let bindFn=fn.bind(o,"zhangsan",18,"喜欢打篮球");//bind 同样改变this指向,不一样的是不是对函数的直接调用,而是返回一个函数,在需要的时候调用
bindFn();
o.name;
寄生式继承(作为了解)

缺点:没有使用到原型 无法复用

// ---------------------------------方式三------------------------------------
function Cat(o){
    var obj=Object.create(o);
    console.log("obj",obj);
    o.type="动物";
    return obj;
}
var obj={name:"小猫咪",age:1};
var cat=Cat(obj);
console.log("cat",cat);
组合继承
// ---------------------------------方式四------------------------------------
function Animate(name,age){
    this.type="动物";
    this.name=name;
    this.age=age;
    this.eat=["骨头","肉肉"]
}
// name和age是父级也有的属性,可以使用apply的方式去继承父级的相同属性,不一样的属性值选择传值
function Dog(name,age){
    Animate.apply(this,[name,age]);
}
Dog.prototype=new Animate();
var dog1=new Dog("Tony",3);
var dog2=new Dog("Tom",3);
dog1.eat.push("小鱼仔");//组合继承  当其中一个实例修改继承的属性时不会影响到其他实例
dog2.eat//["骨头","肉肉"]
// 修改原型对象(进一步理解为什么使用组合继承)
function Animate(name,age){
    this.type="动物";
    this.name=name;
    this.age=age;
    this.eat=["骨头","肉肉"]
}
// name和age是父级也有的属性,可以使用apply的方式去继承父级的相同属性,不一样的属性值选择传值
function Dog(name,age){
    this.name=name;
    this.age=age;
    this.eat=["草","青菜"];//如果当前没有eat,那么dog1.eat.push的时候修改的就是原型中的属性,即Animate中的属性
    
    //使用apply或者call方法,就是为了在当前方法中复制一份Animate中的属性,让当前方法调用的时候具有一个自己的属性,那么在修改的时候就不会修改原型中的属性了
}
Dog.prototype=new Animate();//修改原型对象 使用当前原型继承Animate中的方法
var dog1=new Dog("Tony",3);
var dog2=new Dog("Tom",3);
dog1.eat.push("小鱼仔");//修改属性的时候 当自身有该属性的时候就先从自身中修改,当自身没有的时候就会从原型中修改  
Dog.prototype//指向的是Animate
console.log(dog1.__proto__===Dog.prototype);//true
dog1.__proto__.eat.push("肉骨头");//此时修改的是Animate中的eat
function Animate(name,age){
    this.type="动物";
    this.name=name;
    this.age=age;
    this.eat=["骨头","肉肉"]
}
//加入定义了Animate的原型上的一个属性
Animate.prototype.type2="2222";

function Dog(name,age){
    this.name=name;
    this.age=age;
    this.eat=["草","青菜"];
}
Dog.prototype=new Animate();//使用当前原型继承Animate中的方法
var dog1=new Dog("Tony",3);
var dog2=new Dog("Tom",3);
//修改Animate的原型上的属性type2
dog1.__proto__.type2="555";//这样会在Animate上面添加一个type2属性而非把其原型上的属性修改掉
//正确的方式:
dog1.__proto__.__proto__.type2="666";