1.js中的构造函数的写法
function Person(){}; //定义一个构造函数
Person.prototype.name="abc"; //原型对象中添加属性
Person.prototype.age = 18;
var p1 = new Person(); //实例化
var p2 = new Person();
2.需求:生成多个类似实例的方法
- 方法一:创对象,加元素(pass)
var cat1 = {};//创建一个空对象
cat1.name="大明";
cat1.color ="黄色";
var cat2 = {};//创建一个空对象
cat2.name="小明";
cat2.color ="白色";
- 方法二:封装一个函数,返回一个公共对象
function cat(name,color){
return {
name:name,
color:color//这里没有用this!!
}
}
var c1 = cat("大明","黄色");
var c2 = cat("大明","黄色");
var c3 = cat("大明","黄色");
- 方法三:构造函数
function Cat(name,color){
this.name = name;
this.color = color;
//以下这两点可以放在prototype里共用!!
//this.type = '动物';
// this.eat = function(){
// console.log('吃老鼠')
// }
};
Cat.prototype.type = '动物';
Cat.prototype.eat = function(){console.log('吃老鼠')};
var c1 = new Cat("大明","黄色");//实例化
var c2 = new Cat("大明","黄色");
c1.eat()
3.原型链大总结
function F1(){
this.name1 = 'f1'
};
F1.prototype.name = 'object';
function F2(){
this.name2= 'f2'
};
function F3(){
this.name3 = 'f3'
};
F2.prototype = new F1(); //f2的原型是f1
F3.prototype = new F2(); //f3的原型是f2
var f = new F3(); //实例化的处理
console.log(f.name); //'object'
//修改
f.__proto__.__proto__.__proto__.name= '12414';
console.log(f.name); //'12414'
//删除
delete f.__proto__.__proto__.__proto__.name;
console.log(f.name); //'undefined'
}
4.三种继承类型的总结
(1)原型继承
- 优点:
同一个原型对象 - 缺点:
①不能修改原型对象,会影响所有实例
②当我们访问一个原型对象的属性时,用__proto__是一级级来获取,当继承关系很复杂,未知继承时就无法实现
eg1:继承的写法
function Animal(){
this.type = "动物"
};
function Cat(name,color){
this.name = name;
this.color = color;
};
可以理解为使猫的原型被动物覆盖:这样猫就继承了动物的属性,原来猫的原型就丢失了
Cat.prototype = new Animal();
var c1 = new Cat('x','白色');
var c2 = new Cat('t','花色');
c1.type //'动物'
eg2:继承中修改原型对象的方法
function Animal(){
this.type = '动物'
};
function Cat(name,color){
this.name = name;
this.color = color;
};
Cat.prototype = new Animal();
var cat1 = new Cat("大明","黄色");
var cat2 = new Cat("大明","黄色");
cat1.type ='我是猫';
console.log(cat2.type);
//这样写并不会修改原型中的属性,只是在构造的对象cat1中新建了一个type属性
//不会影响原型animal,甚至不会影响其他实例cat2
//若要改变原型需要用 cat1.__proto__.type = '我是猫';
//这样所有的地方用到type都变成了'我是猫'(这个是缺点!!)
eg3:原型继承中获取值的顺序:
function Animal(){
this.type = '动物'
};
function Cat(name,color){
this.name = name;
this.color = color;
this.type = '我是猫'
};
Cat.prototype = new Animal();
var cat1 = new Cat("大明","黄色");
var cat2 = new Cat("大明","黄色");
//父与子有相同的属性时,先取到子类中的元素
//获取的是当前构造的对象中的属性
console.log(cat1.type);
console.log(cat2.type);
//若想获取对象原型Animal中的属性
console.log(cat1.__proto__.type);
console.log(cat2.__proto__.type);
(2)构造函数的继承
- 优点:
不存在修改原型对象影响所有实例,各自拥有独立属性 - 缺点:
①父类的成员会被创建多次,存在冗余且不是同一个原型对象(复制后即切断了父子的关系,以及新建的实例之间的关系)
②通过apply/call只能拷贝成员,原型对象不会拷贝
eg1:继承的写法
function Animal(){
this.type = "动物"
};
function Cat(name,color){
//将Animal对象的成员复制到Cat对象上
//通过apply/call只能拷贝成员,原型对象不会拷贝
Animal.apply(this);
this.name = name;
this.color = color;
};
var cat1 = new Cat("大明","黄色");
var cat2 = new Cat("小明","白色");
cat1.type = '我是黄猫';
console.log(cat1.type); //'我是黄猫' cat1被修改,不影响cat2
cat2.__proto__.type = '我是动物';//修改原型无效
console.log(cat2.type); //"动物"
//apply() call()
//作用:修改this指向(可以用复制移动的方式解释)
//区别:传参apply要加[],call什么都不加
// function Cat(name,color){
// this.name = name;
// this.color = color;
// };
// var obj = {};
// cat函数复制在obj对象中执行
// cat.call(obj,'a','red')
// Cat.apply(obj,['a','red'])
// obj.name // 'a'
(3)组合继承
- 优点:
①修改原型对象时,不会影响所有实例
②属于同一个原型对象:可以拿到父类的原型中的内容 - 缺点:
无(把前两种的缺点都弥补了)
eg1:继承的写法
function Animal(){
this.type = '动物'
};
Animal.prototype.eat = function(){console.log('吃')};
function Cat(name,color){
this.name = name;
this.color = color;
Animal.call(this);
};
Cat.prototype = new Animal();
var cat1 = new Cat("大明","黄色");
var cat2 = new Cat("小明","白色");
cat1.type = '我是黄猫'; //修改当前构造器中的属性
cat2.__proto__.type = '我是动物';//修改了原型对象的值,但并不影响cat1,cat2的值
console.log(cat1.type); //'我是黄猫' //原型对象的值变化,并不影响构造函数值
console.log(cat2.type); //'动物'
console.log(cat2.__proto__.type); //'我是动物
cat1.eat(); //还可以调用原型对象中eat()方法