目录
继承
继承的概念:子承父业,儿子继承了爹里面的内容,那么爹所拥有的属性和方法,子类经过继承之后同样自动拥有
原生js的继承的方法有:构造函数继承、原型链继承、混合构造原型链继承、混合构造寄生式继承(完美继承)、ES6继承
构造函数继承
继承方式:父类在构造函数中通过this,创建了实例属性和实例方法,子类在继承的时候需要在子类的构造函数中改变父类的this指向,就可以实现继承
父类.call(this,name,age)
缺点:相同的方法,每创建一个对象,就会开辟一个新的内存空间,所以造成大量的内存浪费。也就是说,在实用同一个构造函数new出来的对象 他们不在同一个内存空间里
代码:
//ES5的构造函数
//Dog
function Dog(name,age){
//实例属性
this.name = name;
this.age = age;
//实例方法
this.showName = function(){
return this.name;
}
this.showAge = function(){
return this.age;
}
}
//父类中的this代表:new Dog后的实例对象。
//子类(哈士奇) 继承过程
function Huskie(name,age){
Dog.call(this,name,age); //this : new Huskie后的实例对象
// Dog.apply(this,[name,age]); 修改this指向的三个方法 bind方法穿回来是一个函数,不调用就不会执行,所以要在后面加上()
// Dog.bind(this,name,age)();
}
//创建一个子类对象
let hsk = new Huskie('二哈',3);
console.log(hsk.showName());//二哈 成功继承了父类的方法showName
原型链继承
原型的特点:写在原型对象中的属性或方法都共同享有一个空间
继承方式:先创建一个子类的构造函数,然后通过函数名
prototype
找到原型对象 = new 继承的父对象;想要修改自己的属性或方法要在继承了父类之后再修改
原型及原型链:(面试题)
- 每一个函数都有一个属性
prototype
,通过这个属性就可找到该函数对应的原型对象。 - 每一个对象都有一个属性
__proto__
,通过这个属性就可以找到该对象的原型对象。原型对象也属于对象,也有__proto__
,通过这个属性又可以找到当前原型对象的父级原型对象,以此类推,这样一直向上查找的过程就叫做原型链。最终找到的是null对象。 - 每一个原型对象都有一个属性
constructor
,这个属性可以找到原型对象的构造函数。(反着找)
原型链继承的缺点:所有的对象都共享同样的属性,比如所父亲叫小花,那么继承的所有子类都会叫小花。
代码:
//Cat 原型链继承
function Cat(){} //构造函数
//原型属性
Cat.prototype.name = '小花'; //这里通过函数的属性prototype找到原型对象设置name属性
Cat.prototype.age = 3;//与上面同理
//原型方法
Cat.prototype.showName = function(){
return this.name;//原型方法的设置语法
}
Cat.prototype.showAge = function(){
return this.age;
}
let cat_01 = new Cat();
let cat_02 = new Cat();
console.log(cat_01.showName === cat_02.showName); //true 这里的方法共同享有一个空间
//原型链继承方法
function Garfield(){} //构造函数
//创建一个父类的对象赋值给子类的原型对象
Garfield.prototype = new Cat();
//自己的属性或方法要写在继承父类之后
Garfield.prototype.sex = '公';
let gf = new Garfield();
console.log(gf.showName());//小花 继承了父类的名字和方法
混合构造原型链继承
继承方式:为了解决原型链继继承的弊端,子类在继承的时候,通过构造函数继承父类的属性,通过原型链继承父类的方法
缺点:调用了两次父类构造函数,生成了两份实例对象。
代码:
//混合继承 构造函数继承属性 原型链继承方法
function Pig(name,age){
//实例属性
this.name = name;
this.age = age;
}
//原型方法
Pig.prototype.showName = function(){
return this.name;
}
//子类
function Ericius(name,age){
Pig.apply(this,[name,age]); //构造函数继承属性 修改this指向
}
//原型链继承方法
Ericius.prototype = new Pig();
let hz = new Ericius('天蓬元帅',888);
console.log(hz.showName());//天蓬元帅 成功继承
混合构造寄生式继承
完美的继承方式
继承方法:构造函数继承属性、寄生式继承继承方法
为什么说寄生式继承方式完美?:因为此方法在原型对象里面就已经继承完毕了。
寄生式继承的方法:使用for(let key in 父类的原型对象)
遍历父类的原型对象,赋值给子类的原型对象
// 混合构造寄生式继承
function Dog(name,age){
this.name = name;
this.age = age;
}
Dog.prototype.showName = function(){
return this.name;
}
function Tibetan(name,age){
//构造函数继承
Dog.call(this,name,age);
}
// Tibetan.prototype = Dog.prototype;
// //寄生式继承
for(let key in Dog.prototype){
Tibetan.prototype[key] = Dog.prototype[key];
}
let tib = new Tibetan('獒哥',18);
console.log(tib.showName());
ES6继承
继承方式:通过
class 子类 extends 父类
的语法继承、在constructor
中使用super()
方法继承父类的属性,父类的原型方法会自动继承
代码:
class Dog{
constructor(name,age){
//实例属性
this.name = name;
this.age = age;
//实例方法
}
//原型方法
showName(){
return this.name;
}
}
class Huskie extends Dog{
constructor(name,age){
super(name,age);
}
}
let hsk = new Huskie('二哈',3);
console.log(hsk.showName());
扩展
就近原则
new 一个实例对象时,会找最近的属性赋值。设置>构造函数>构造函数的原型对象。
代码:
案例一:
function Person(name){
//实例属性
this.name = name;
}
// 原型属性
Person.prototype.name = '王五';
// 原型方法
Person.prototype.showName = function(){
return this.name;
}
let ps = new Person('李四');
ps.name = '张三';
console.log(ps.showName());//张三
/*
分析:根据就近原则,直接设置了对象的属性name,最近一次是将张三赋值
给ps的name属性 所以输出张三
*/
案例二:
function Person(name){
//实例属性
this.name = name;
}
// 原型属性
Person.prototype.name = '王五';
// 原型方法
Person.prototype.showName = function(){
return this.name;
}
let ps = new Person('李四');
console.log(ps.showName());//李四
/*
分析:根据就近原则,没有设置实例对象的属性name,找到最近一次赋值是构造函数中的实例属性,所以输出李四。
*/
案例三:
function Person(name){
//实例属性
}
// 原型属性
Person.prototype.name = '王五';
// 原型方法
Person.prototype.showName = function(){
return this.name;
}
let ps = new Person('李四');
console.log(ps.showName()); //王五
/*
分析:没有设置实例对象的属性name,构造函数中没有实例属性,原型对象
中有原型属性,所以输出王五。
*/
修改js中原有对象的方法
增加方法
如果在现有的方法中无法满足需求,可以通过
对象.prototype.新增的方法
来给这个对象增一个方法。
语法代码:
//Array
// 给数组新增一个求和的方法
Array.prototype.fnSum = function(){
return this.reduce((a,b) => {
return a + b;
})
}
// 给数组新增求最大值的方法
Array.prototype.fnMax = function(){
return Math.max.apply(null,this);
}
let arr = [1,2,3,4,5];
let sum = arr.fnSum();
let max = arr.fnMax();
console.log(sum,max);
// 同样可以对 String、Date 进行增加方法
删除属性
通过
delete 对象.属性
可以删除该属性。
语法代码:
function Dog(name){
this.name = name;
}
let dog = new Dog('小黄');
//delete : 删除对象中指定的属性
delete dog.name;
console.log(dog.name);//undefined
检测一个属性是否属于某个对象
通过
'属性名' in 对象
来检测
语法代码:
function Dog(name){
this.name = name;
}
let dog = new Dog('小黄');
//in : 检测一个属性是否属于某个对象
if('age' in dog){
alert(true);
}else{
alert(false);
}
// 输出false 因为dog 这个对象没有age这个属性
总结
继承
js原生的继承方式有:构造函数继承、原型链继承、混合构造原型链继承、混合构造寄生式继承(完美继承)、ES6继承
原型及原型链:
- 每一个函数都有一个属性
prototype
,通过这个属性就可找到该函数对应的原型对象。 - 每一个对象都有一个属性
__proto__
,通过这个属性就可以找到该对象的原型对象。原型对象也属于对象,也有__proto__
,通过这个属性又可以找到当前原型对象的父级原型对象,以此类推,这样一直向上查找的过程就叫做原型链。最终找到的是null对象。 - 每一个原型对象都有一个属性
constructor
,这个属性可以找到原型对象的构造函数。(反着找)