原型
原型是一个对象 其他对象可以通过它实现属性继承
对象分为两种:普通对象 函数对象
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";