一,传统JS继承方法:
1,原型式继承:
//声明父类 function Person(name,age){ this.name = name; this.age = age; this.flag = true; } // 给父类添加方法 Person.prototype.sayName = function(){ console.log(this.name); } //实例化父类 var person = new Person('zhl',30); //调用父类方法 person.sayName(); // zhl // 声明子类 function Worker(name,age,work){ this.name = name; this.age = age; this.work = work; } //原型式继承 其实就是子类的原型指向父类的一个实例 Worker.prototype = new Person(); //给子类添加方法 Worker.prototype.sayWork = function(){ console.log(this.work); } //实例化一个子类 var worker = new Worker('vsmart',32,'itcoder'); //调用子类方法 worker.sayName(); // vsmart worker.sayWork(); // itcoder
貌似可以用了,不要高兴太早,这里是有很大的坑的!
此继承方法有几个弊端:
1,声明父类与子类的构造函数中有很多重复的初始化赋值;
2,实例化子类的对象 flag 属性竟然是 true
然而这个属性本身子类并没有初始化,哪来的?
console.log(worker.flag) //true
console.log(Worker.prototype.flag) //true
哦,原来是从子类的原型上查找到的,也就是从父类的一个实例上获取到的;
本来我们要做到 属性与方法分离的,现在呢,这个属性竟然在原型上,原型应该只放方法才对。
3,子类的构造函数 constructor 竟然指向了 父类的 constructor,这个指向错误将来势必会带来耦合
console.log(worker.constructor) //
function Person(name,age){ this.name = name; this.age = age; this.flag = true; }
综合以上几点我们要做优化;
1,我们可以借用父类的构造函数初始化子类的构造函数 利用 apply 或 call 改变指向
2,我们要重新改变子类的构造函数指向
//声明父类 function Person(name,age){ this.name = name; this.age = age; this.flag = true; } // 给父类添加方法 Person.prototype.sayName = function(){ console.log(this.name); } //实例化父类 var person = new Person('zhl',30); //调用父类方法 person.sayName(); // zhl // 声明子类 function Worker(name,age,work){ Person.apply(this,arguments); // 借用父类构造方法 this.work = work; this.flag = false; } //原型式继承 其实就是子类的原型指向父类的一个实例 Worker.prototype = new Person(); //给子类添加方法 Worker.prototype.sayWork = function(){ console.log(this.work); } //改变constructor指向 Worker.prototype.constructor = Worker; //实例化一个子类 var worker = new Worker('vsmart',32,'itcoder'); //调用子类方法 worker.sayName(); // vsmart worker.sayWork(); // itcoder console.log(worker.flag) //false //子类自己的属性 console.log(Worker.prototype.flag) //true 从子类原型上查找 console.log(worker.constructor) //正确指向自己的构造方法
function Worker(name,age,work){
Person.apply(this,arguments);
this.work = work;
this.flag = false;
}
2,混合拷贝继承:
所谓混合拷贝继承是指,构造函数还是借用父类的方式,但方法继承就是模拟拷贝的方式,
//声明父类 function Person(name,age){ this.name = name; this.age = age; this.flag = true; } // 给父类添加方法 Person.prototype.sayName = function(){ console.log(this.name); } //实例化父类 var person = new Person('zhl',30); //调用父类方法 //person.sayName(); // zhl // 声明子类 function Worker(name,age,work){ Person.apply(this,arguments); this.work = work; this.flag = false; } //拷贝继承 extends2(Worker.prototype,Person.prototype);
//貌似这样也可以啊:
//Object.setPrototypeOf(Worker.prototype, Person.prototype); //给子类添加方法 Worker.prototype.sayWork = function(){ console.log(this.work); } //拷贝继承核心方法 function extends2(child,parent){ for(var attr in parent){ if(parent.hasOwnProperty(attr)){ child[attr] = parent[attr]; } } } //实例化一个子类 var worker = new Worker('vsmart',32,'itcoder'); //调用子类方法 worker.sayName(); // vsmart worker.sayWork(); // itcoder console.log(worker.flag) //false //子类自己的属性 console.log(Worker.prototype.flag) //undefined 子类原型上没有查找到 console.log(worker.constructor) // /*function Worker(name,age,work){ Person.apply(this,arguments); this.work = work; this.flag = false; }*/
二,ES5中的继承:
//声明父类 function Person(name,age){ this.name = name; this.age = age; this.flag = true; } // 给父类添加方法 Person.prototype.sayName = function(){ console.log(this.name); } // 声明子类 function Worker(name,age,work){ Person.apply(this,arguments); this.work = work; this.flag = false; } //继承 Worker.prototype = Object.create(Person.prototype,{ hobby:{ value:'play', enumerable:true } }); //改变constructor指向 Worker.prototype.constructor = Worker; //给子类添加方法 Worker.prototype.sayWork = function(){ console.log(this.work); } //实例化一个子类 var worker = new Worker('vsmart',32,'itcoder'); //调用子类方法 worker.sayName(); // vsmart worker.sayWork(); // itcoder console.log(worker.hobby) //play console.log(worker.constructor) // /*function Worker(name,age,work){ Person.apply(this,arguments); this.work = work; this.flag = false; }*/
三,ES6中的继承
class Person{ //父类构造函数 constructor(name,age){ this.name = name; this.age = age; } //父类方法 sayName(){ console.log(this.name); } } class Worker extends Person{ //子类构造函数 constructor(name,age,work){ //调用父类构造函数 super(name,age); //自身独有的属性 this.work = work; } //自身独有的方法 sayWork(){ console.log(this.work); } } let person = new Person('zhl',30); person.sayName(); //zhl //实例化一个子类 let worker = new Worker('vsmart',32,'itcoder'); worker.sayName(); //vsmart worker.sayWork(); //itcoder console.log(worker.constructor)// 实例构造函数constructor指向 // class Worker extends Person{ // constructor(name,age,work){ // super(name,age); // this.work = work; // } // sayWork(){ // console.log(this.work); // } // }