继承 extend
构造函数的应用
当多个构造函数需要使用一些共同的方法或者属性的时候,我们需要把这些共同的东西拿出来,单独书写一个构造函数,让其他的构造函数去继承自这个公共的构造函数
概念:让B构造函数的实例能够使用A构造函数的属性和方法,我们管B构造函数叫做A构造函数的子类,A构造函数叫做B构造函数的父类
目的:让B构造函数能够使用A构造函数的属性和方法. (子类的实例使用父类的属性和方法)
//-------------------原型继承
//---------- 准备一个父类
function Person(name,age){
//
this.name=name;
this.age=age;
}
Person.prototype.sayHi=function(){
console.log('hello world')
}
/*
person的实例={
name:'jack',
age:18,
__proto__:{//Person.prototype
constructor:Person,
sayHi:function(){
console.log('hello world')
},
__proto__:Object.prototype
}
}
原型继承
利用改变 原型链的方式来达到继承的效果
直接把父类的实例当做子类的 prototype
构造函数的原型 对象
我们把原型赋值为一个新的对象
new Person的时候,得到的也是一个新的对象
核心代码: 子类.prototype = new 父类
原型继承的优缺点:
优点:构造函数体内和原型上的都可以继承
缺点:
1: 一个构造函数的内容,在两个位置传递参数
2: 继承来的属性不再子类实例的身上,看起来很别扭
*/
//---子类
function Student(gender){
this.gender=gender;
}
//const p=new Person('jack',18);
//直接把父类的实例当做子类的原型对象
//Student.prototype=p;
//直接把父类的实例当做子类的原型对象
Student.prototype=new Person('jack',18);
const s=new Student('男');
console.log(s);
/*
Student
gender: "男"
[[Prototype]]: Person //即是Student.prototype 也是 Person的实例
age: 18
name: "jack"
[[Prototype]]: Object //Person的prototype
sayHi: ƒ ()
constructor: ƒ Person(name,age)
[[Prototype]]: Object
......
*/
s.sayHi(); //student的实例用了Person的方法
//---------------------借用继承
/*
借用构造函数继承 (借用继承/call 继承)
通过改变父类的构造函数的 this指向来达到继承的效果
核心代码:在子类的构造函数体内,父类.call(子类的实例)
构造函数的执行
1 是一个普通函数,可以当做函数直接调用
2 当做普通函数执行的时候,this指向谁,就向谁身上添加内容
3 call 方法可以改变函数的this指向
*/
//父类
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayHi=function(){
console.log('hello world')
}
//function Student(gender,name,age){
function Student(gender){
this.gender=gender;
/*
将Person函数当做普通函数调用
使用call方法改变了一下Person函数内部的this指向
改变成指向谁就会向谁身上添加一个name 一个age属性
当我们new Student的时候,Strdent内部这个位置的this指向Studend的实例对象
我们使用call方法改变了Person函数内的指向,指向了 Student的实例 this === s
借用继承的优缺点:
优点:
1:继承来的属性是在自己身上
2:我们一个实例化的过程在一个位置传参
缺点:
只能继承父类构造函数体内的内容
父类原型上的内容不能继承
*/
Person.call(this,'rose',16);//可以在上面传参
// Person.call(this,name,age);//可以在上面传参
//这个函数执行完毕以后,会像Student的实例身上添加一个name 一个age属性
}
//书写属于 student自己方法
Student.prototype.stduy=function(){
console.log('学术的天职就是好好学习');
}
var s=new Student('男');
// var s=new Student('男','rose',16);
console.log(s);//
/*
Student {gender: '男', name: 'rose', age: 16}
age: 16
gender: "男"
name: "rose"
[[Prototype]]: Object
stduy: ƒ ()
constructor: ƒ Student(gender)
[[Prototype]]: Object
*/
//------------组合继承
/*
-----------组合继承
把原型继承 和 借用构造函数继承 合并在一起使用
组合继承的优缺点:
优点:
1: 父类构造函数体内 和原型上的内容能继承
2: 继承下来的属性放在自己身上
3: 在一个位置传递所有参数
缺点:
1:当你的子类添加方法的时候,实际是添加在了父类的实例上(但是这一点不影响我们使用)
2018年前大部分人继承都是使用这种方式
*/
//父类
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayHi=function(){
console.log('hello world')
}
//子类
function Student(gender,name,age){
this.gender=gender;
//借用继承 ,目的:把属性继承在自己的身上
Person.call(this,name,age)
}
//原型继承,目的:继承父类原型上的方法
Student.prototype=new Person();//这个位置的参数可传可不传 因为自己身上有就不会向原型上找
//书写属于 student自己方法
Student.prototype.stduy=function(){
console.log('学术的天职就是好好学习');
}
//使用student 创建实例
var s=new Student('男','sun',1200)
console.log(s);
/*
Student {gender: '男', name: 'sun', age: 1200}
age: 1200
gender: "男"
name: "sun"
[[Prototype]]: Person
age: undefined
name: undefined
stduy: ƒ ()
arguments: null
caller: null
length: 0
name: ""
prototype: {constructor: ƒ}
[[FunctionLocation]]: 继承.html:155
[[Prototype]]: ƒ ()
[[Scopes]]: Scopes[1]
[[Prototype]]: Object
sayHi: ƒ ()
constructor: ƒ Person(name,age)
[[Prototype]]: Object
*/
// -----------------------------------拷贝继承(for in继承)
/*
拷贝继承(for in继承)
利用for in循环的特点,来继承所有的内容
先实例化一个父类的实例
使用for in循环来遍历这个实例对象,因为for in循环可以遍历对象自己身上的属性还会遍历———proto———上的属性
直接把父类实例身上的所有内容直接复制到子类的prototype
*/
//父类
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayHi=function(){
console.log('hello world')
}
// var p=new Person('jack',18);
// for(var key in p){
// console.log(key);//name age sayHi
// }
//子类
function Student(gender,name,age){
this.gender=gender;
//for in 继承
var p=new Person(name,age);
for(var key in p){
// console.log(key);//name age sayHi
//将p实例的属性及原型上的方法 添加到Student的原型上
Student.prototype[key]=p[key];
}
}
Student.prototype.stduy=function(){
console.log('学术的天职就是好好学习');
}
const s=new Student('男','sun',199)
console.log(s);
/*
Student {gender: '男'}
gender: "男"
[[Prototype]]: Object
age: 199
name: "sun"
sayHi: ƒ ()
stduy: ƒ ()
constructor: ƒ Student(gender,name,age)
[[Prototype]]: Object
*/
/*
拷贝继承的优缺点:
优点:
1:父类的构造函数体内的和原型上的属性和方法都可以继承
2:在同一个位置传参
3:constructor 能正常配套
4:添加自己的方法的时候,确实是在自己的原型身上
总体来说这个继承还好很不错的
缺点:
1:for in循环需要一直遍历到Object.prototype ,很消耗性能
2:不能继承不可枚举的属性
3:继承来的属性不在自己的身上
*/
//----------寄生继承
/*
构造函数不要写return
return 一个基本数据类型 ,写了白写
return 一个复杂数据类型,构造函数没有意义
寄生继承1
寄生继承实际上是一种伪继承
(最敷衍的一个继承但是确实达到了继承的目的:在子类中使用父类的属性)
核心代码:
const instance=new Person(name,age)
return instance
*/
//父类
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayHi=function(){
console.log('hello world')
}
//子类
function Student(name,age){
this.gender='男';
//寄生继承1----------------------------
const a=new Person(name,age);
//--3 将父类实例的原型改为子类的原型
a.__proto__=Student.prototype;
//在子类里面直接retuen 父类的实例
return a;
}
//---2 书写属于 student自己方法 在修改原型前设置不上去
Student.prototype.stduy=function(){
console.log('学术的天职就是好好学习');
}
var s=new Student('rose22',19);
console.log('---',s);//s确实是new Student的实例,但是真实的内容是Person的实例
/*
Person {name: 'rose', age: 19}
age: 19
name: "rose"
[[Prototype]]: Object
sayHi: ƒ ()
constructor: ƒ Person(name,age)
[[Prototype]]: Object
//---将父类实例的原型改为子类的原型后的打印
Person {name: 'rose22', age: 19}
age: 19
name: "rose22"
[[Prototype]]: Object
stduy: ƒ ()
constructor: ƒ Student(name,age)
[[Prototype]]: Object
*/
/*
----------------寄生继承2-------------------
出现了第二种寄生继承
不直接寄生实例,寄生原型
*/
function Student(gender){
this.gender=gender;
}
//寄生实例
Student.prototype=Person.prototype;
//想要的在自己的原型上添加一些方法的时候,父类的原型上也有了这个方法
Student.prototype.stduy=function(){
console.log('好好学习');
}
const s2=new Student('男')
console.log(s2);
const p1=new Person('sun',100)
console.log(p1);
/*
寄生继承的优缺点:
优点:号称完美继承
1 原型和构造函数体内的都可以继承
2 寄生原型的话,自己的属性和方法依旧可以添加和使用
缺点:
1: 寄生实例的时候,没有自己的任何内容
2:寄生原型的时候,一旦修改原型,父类的实例也会有这些方法
*/
/*
//----------------------------------寄生组合继承(完美继承)
合并了 寄生继承 + 原型继承 + 第三方的构造函数 + 借用继承
*/
//父类
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayHi=function(){
console.log('hello world')
}
function Abc(){
}
//子类
function Student(gender,name,age){
this.gender=gender;
//借用继承 :继承来父类的属性 直接将父类的属性放到自己的身上
Person.call(this,name,age)
}
//如果这样写的话 子类原型直接为父类的原型,如果需要向子类的原型上添加方法时,那父类的原型上也就有了
//这样的话调用父类时候,父类实例也就有了子类的方法,不推荐
//Student.prototype=Person.prototype
//使用立即执行函数的目的是为了变量私有化
//----------核心代码
(function(){
//声明一个第三方的构造函数
function Abc(){
}
//让第三方的构造函数的原型为父类的原型 这样就得到父类原型上的方法
Abc.prototype=Person.prototype;
//子类的原型为 第三方函数的实例
Student.prototype=new Abc();
}())
//向子类的原型上添加方法 实际是添加到了第三方函数的实例上了,这样不会污染父类的原型
Student.prototype.stduy=function(){
console.log('学术的天职就是好好学习');
}
var s=new Student('男','sun',1700)
console.log(s);
/*
Student {gender: '男', name: 'sun', age: 1700}
age: 1700 //----------这样既有了父类的属性
gender: "男"
name: "sun"
[[Prototype]]: Person
stduy: ƒ () //-----------又可以在原型上设置自己的方法且不会污染父类
[[Prototype]]: Object
sayHi: ƒ () //-----------继承了父类原型上的方法
constructor: ƒ Person(name,age)
[[Prototype]]: Object
//---这样既有了父类的属性,又可以在原型上设置自己的方法且不会污染父类,又继承了父类原型上的方法
*/
//创建父类的实例打印,发现父类不受任何影响
console.log('_____', new Person('zju',16));
/*
----------------------------ES6 类型继承-----------------------
es6 把继承这个使用变成了关键字 实际上就是封装了寄生组合继承
1:extends
class 子类类名 extends 父类{}
2: super()
在 constructor里面书写一个supper()
super(name,age) 等价于 Person.call(this,name,age)
注意:
1:super需要写在 constructor 里面
2:如果你要写自己的属性,必须写在supper后面
3:es6 的继承可以继承ES5的构造函数 也可以继承es6的类
*/
//父类
//---es5 构造函数
// function Person(name,age){
// this.name=name;
// this.age=age;
// }
// Person.prototype.sayHi=function(){
// console.log('hello world')
// }
//---es6 类
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
sayHi(){
console.log('hello word');
}
}
console.log(new Person('小红',23));
//------es6中创建一个类
// class Student{
// constructor (gender){
// this.gender=gender;
// }
// stduy(){
// console.log('好好学习');
// }
// }
//-----es6中通过关键字实现子类继承父类
class Student extends Person{
constructor (gender,name,age){
//super() 要写在自己的属性前面
//super(name,age)等价于 Person.call(this,name,age)
super(name,age);
this.gender=gender;
}
stduy(){
console.log('好好学习');
}
}
console.log('---',new Student('女','rose',18));
/*
Student {name: 'rose', age: 18, gender: '女'}
age: 18
gender: "女"
name: "rose"
[[Prototype]]: Person
constructor: class Student
stduy: ƒ stduy()
[[Prototype]]: Object
sayHi: ƒ ()
constructor: ƒ Person(name,age)
[[Prototype]]: Object
*/
类语法
在es5中,我们使用函数来充当 构造函数(类)
Es6 引入一个类的概念,就是使用一个新的关键字 class 来定义类, class 是构造函数的语法糖
注意:一个class定义的类,不能被当做普通函数调用,必须要和new关键字连用,不然就报错
只能通过一个new来得到一个对象
语法:
--------------------定义一个类
class 类名{
// 构造器 ,等价于我们的构造函数 ,定义这个类创造的对象里有哪些属性
//一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。
constructor(a,b){
this.属性名1=a;
this.属性名2=b;
}
// ------ 直接书写原型上的方法,不用function:
// ------ 方法与方法之间不需要逗号 , 分隔,加了会报错
方法名1(){
console.log('***',this)
}
方法名2(){
console.log('***',this)
}
方法名3(){
console.log('***',this)
}
}
-----------------定义一个名为Person的类
class Person{
// 构造器 ,这个类创造的对象里有哪些属性
constructor(a,b){
this.name=a;
this.age=b;
}
// 直接书写原型上的方法,不用function:
// 也不用,
init(){
console.log('***',this)
}
}
//---使用Person类去创建对象
var newObj1=new Person('你好',100); //----得到一个实例对象
newObj1.init1(); //----调用实例对象的方法,方法中的this指向这个创建出来的实例对象
//ES5 一样,类的所有实例共享一个原型对象。
//newObj1和newObj2都是Person的实例,它们的原型都是Person.prototype,所以__proto__属性是相等的。
newObj1.__proto__ === newObj2.__proto__
标签:name,继承,age,js,Person,Student,父类,构造函数
From: https://www.cnblogs.com/zwteast/p/17901180.html