写的比较早。本文只涉及基于原型的继承。ES6之后的Class-basedinheritance可以参考相关文献。两种调用知识储备构造函数的方式(结果完全不同)都是通过关键字new调用的:functionPerson(name){this.name=name;this.age=18;}varo=newPerson('hx');console.log(o.name,o.age);//hx18console.log(window.name,window.age);//''未定义直接调用:functionPerson(name){this.name=name;this.age=18;}varo=Person('hx');console.log(o);//undefinedconsole.log(window.name,window.age);//hx18从这里可以看出:constructor和普通函数一样,直接调用即可,没有返回值,this指向Window;如果通过new调用,返回值是一个对象,this指向这个对象new是干什么的?new关键字将执行以下操作:创建一个空对象;将对象链接到另一个对象(即:设置对象的构造函数);使用第一步创建的空对象作为this的上下文(this指向空对象);执行构造函数(给对象添加属性)并返回对象functionPerson(name){this.name=name;this.age=18;}varo=newPerson('hx');上面的代码对应四个第一步是:varobj={};obj.__proto__=Person.prototype;Person.call(obj,'hx');returnobj;JavaScript实现继承的几种方式1.原型链继承函数Parent(name){this.name=name;这个年龄=18;this.arr=['hello','world']}Parent.prototype.sayAge=function(){console.log(this.age)}functionChild(gender){这个。性别=性别;}Child.prototype=newParent();varchild1=newChild('male');child1.arr.push('js')console.log(child1.name);//undefinedconsole.log(child1.age);//18console.log(child1.arr);//['你好','世界','js']console.log(child1.gender);//malechild1.sayAge();//18varchild2=newChild('女性');console.log(child2.name);//undefinedconsole.log(child2.age);//18console.log(child2.arr);//['你好','世界','js']console.log(child2.gender);//femalechild2.sayAge();//18优点:Parent原型对象上的方法可以被Child继承缺点:Parent的引用类型属性会被所有Child实例共享,互相干扰Child不能给Parent传递参数2.构造函数继承(经典继承)函数父母(姓名){this.name=姓名;这个年龄=18;this.arr=['你好','世界'];this.sayName=function(){console.log(this.name)}}Parent.prototype.sayAge=function(){console.log(this.age)}functionChild(name,gender){Parent.call(this,姓名);//this指向要从Window创建的对象this.gender=gender;}varchild1=newChild('lala','male');child1.arr.push('js');console.log(child1.name);//lalaconsole.log(child1.age);//18console.log(child1.arr);//['你好','世界','js']console.log(child1.gender);//malechild1.sayName();//18child1.sayAge();//未捕获的类型错误:child1.sayAge不是函数varchild2=newChild('fafa','female');console.log(child2.name);//fafaconsole.log(child2.age);//18console.log(child2.arr);//['你好','世界']console.log(child2.gender);//femalechild2.sayName();//18child2.sayAge();//UncaughtTypeError:child1.sayAgeisnotafunction优点:避免引用类型属性被所有Child实例共享Child可以传递参数给Parent缺点:Parent原型对象的方法不能被Child继承。每次创建Child实例时,都会创建sayName方法,造成内存资源的浪费。3.组合继承函数Parent(name,age){this.name=name;这个。年龄=年龄;this.arr=['hello','world']}Parent.prototype.sayName=function(){console.log(this.name)}functionChild(name,age,gender){Parent.call(this,name,年龄);这个.gender=gender}Child.prototype=Object.create(Parent.prototype);Child.prototype.constuctor=Child;Child.prototype.sayAge=function(){console.log(this.age)}varchild1=newChild('lala',18,'male');child1.arr.push('js');child1。姓名;//'lala'child1.age;//18child1.arr;//['你好','世界','js']child1.gender;//'男'child1.sayName();//lalachild1.sayAge();//18varchild2=newChild('fafa',28,'female');child1.name;//'fafa'child1.age;//28child1.arr;//['你好','世界']child1.gender;//'女性'child1.sayName();//fafachild1.sayAge();//28CompositeinheritanceisbestpracticeforJavaScriptinheritancePropertiesuseconstructorinheritance——避免ParentreferencesAttributes受多个Child实例影响,同时支持使用原型链继承进行参数传递方法——支持Child继承Parent原型对象方法,避免在多个实例(Parent.prototype)中重复复制方法,有两种方法:Child.prototype=Parent.prototype或Child.prototype=newParent()Child.prototype=Parent.prototype:这是肯定不行,给Child.prototypeToParent添加方法或者效果;Child.prototype=newParent():这种方法有一个缺点,就是在创建一个新的Child实例时会调用Parent构造函数两次(一次是newParent(),一次是Parent.call(this,name)),浪费效率,而且如果Parent构造函数有副作用,重复调用可能会造成不良后果。对于第二种情况,除了使用Object.create(Parent.prototype)方法外,还可以用桥接函数来实现。其实无论使用哪种方法,实现思路都是调整原型链:从:newChild()---->Child.prototype---->Object.prototype---->null到:newChild()---->Child.prototype---->Parent.prototype---->Object.prototype---->nullfunctionParent(name){this.name=name}Parent.prototype.sayName=function(){console.log(this.name)}functionChild(name,age){Parent.call(this,name);this.age=age;}functionF(){}F.prototype=Parent.prototype;Child.prototype=newF();Child.prototype.constuctor=Child;Child.prototype.sayAge=function(){控制台。log(this.age)}可以看出,通过一个桥接函数F,只调用了一个Parent构造函数,因此避免了在Parent.prototype上创建不必要的冗余属性//封装上面的方法functionobject(o){functionF(){}F.prototype=o;returnnewF();}functionprototype(child,parent){varprototype=object(parent.prototype);child.prototype=原型;prototype.constructor=child;}//当我们使用:prototype(Child,Parent);2什么是最优的继承方式?其实无论是改进组合继承(使用Object.create还是或者使用Object.setPrototypeOf),或者所谓的寄生组合继承(使用桥接函数F),都不是回答这个问题的关键最优继承方式体现了一个设计理念:无论是静态属性还是动态属性,其维度的划分标准是:是否可以共享是每个子类共享的,但是子类的实例是独立的相互(非共享):应该是++放在父类++的构造函数上,然后通过子类调用父类构造函数进行初始化;对于每一个子类,都有子类实例可以共享的属性(无论是静态的还是动态的):应该++放在父类++的原型对象上,通过原型链获得;每个子类唯一,子类实例相互独立(非共享):++应该放在子类构造方法上的++实现;每个子类唯一,但子类实例可以共享的属性:应该++放在子类的原型对象上++,通过原型链获得;从文中不好理解,看代码:functionMan(name,age){//每个子类都有,但独立(不共享)this.name=name;this.age=age;}Man.prototype.say=function(){//每个子类都有并共享动态属性(shared)console.log(`我是${this.name}和${this.age}年old.`)}//每个子类都有并共享Man.prototype.isMan=true的静态属性(共享);函数游泳者(姓名、年龄、体重){Man.call(这个、姓名、年龄);//Swimmer子类是唯一的,每个实例都是独立的(非共享的)this.weight=weight;}functionBasketBaller(name,age,height){Man.call(this,name,age);//BasketBaller子类是唯一的,每个实例都是独立的(不共享)this.height=height;}//使用ES6直接设置原型关系构建原型链Object.setPrototypeOf(Swimmer.prototype,Man.prototype)//等价于Swimmer.prototype=Object.create(Man.prototype);游泳者.prototype.constructor=Swimmer;Object.setPrototypeOf(BasketBaller.prototype,Man.prototype)//等同于BasketBaller.prototype=Object.create(Man.prototype);BasketBaller.prototype.constructor=BasketBaller;//继续扩展子类原型对象Swimmer.prototype.getWeight=function(){//Swimmer子类独有,但共享动态属性(shared)console.log(this.weight);}//Swimmer子类独有,但共享静态属性(shared)Swimmer.prototype.isSwimmer=true;varswimmer1=newSwimmer('swimmer1',11,100);varswimmer2=newSwimmer('swimmer2',21,200);swimmer1;//游泳者{name:"swimmer1",age:11,weight:100}swimmer1.isMan;//tureswimmer1.say();//我是swimmer1,11岁.swimmer1.isSwimmer;//tureswimmer1.getWeight();//100swimmer2;//游泳者{name:"swimmer2",age:21,weight:200}swimmer2.isMan;//tureswimmer2.say();//我是swimmer2,21岁.swimmer2.isSwimmer;//tureswimmer2.getWeight();//200//与BasketBallerReason相同(省略)
