关于原型链,__proto__和prototype,继承

ES6关于构造函数和继承有了新的语法,但先分析之前的写法,有助于理解原理

1.原型链

javascript没有像Java、C#类的概念,要实现继承只能是通过“原型(prototype)”这条链子将要继承的对象链接起来

//对象
var o={}
o.__proto__===Object.prototype//true
//数组
var arr=[1,2]
arr.__proto__===Array.prototype //true
Array.prototype.__proto__===Object.prototype //true
Object.prototype.__proto__===null //true
//函数
function f(){}
f.__proto__===Function.prototype//true
Function.prototype.__proto__===Object.prototype//true

说实话一直困惑__proto__prototype这俩啥关系,通过上面的代码就再清晰不过了__proto__指向了所继承的父原型,prototype指向自己的原型,所以通过修改__proto__可以改变所继承的原型,但__proto__ 并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性,但依旧不建议在生产中使用该属性,避免对环境产生依赖。生产环境中,我们可以使用 Object.getPrototypeOf方法来获取实例对象的原型,然后再来为原型添加方法/属性。

2.构造函数

function Person(name,age){
  this.name=name
  this.age=age
  this.sayName=function(){
    alert(this.name)
  }
}
Person.prototype.constructor===Person//true

以上是个构造函数,我一直觉得就是个函数,但其实它返回的是个对象,用new关键字构造一个对象。并且在自己的prototype上将constructor属性指向了自己

var person1=new Person('张三',27)
var person2=new Person('李四',18)
person1.constructor===Person//true
person1.__proto__===Person.prototype//true
person2.__proto__===Person.prototype//true
person1.sayName===person2.sayName//false 这不是我们想要的

每个实例的属性需要不一样,因为每个人的名字不一样,但是sayName需要共用,可以把需要共用的属性或方法放在prototype上,

Person.prototype.sayName2=function(){
  alert(this.name)
}
person1.sayName2===person2.sayName2//true

现在Person上其实有2个属性,一个是默认生成的constructor,另一个是sayName2,所以又可以用以下的写法解释

Person.prototype={
  constructor:Person,
  sayName2:function(){
    alert(this.name)
  }
}

接下来对比以下ES6的新语法class

class Person{
  constructor(name,age){//放在构造器里面的是 不公用的
    this.name=name
    this.age=age
    this.sayName=function(){
      alert(this.name)
    }
  }
 //放在构造器外面的是公用的
  sayName2(){
    alert(this.name)
  }
}

咋样,就感觉是把之前的写法合并起来了

3.继承

//父构造函数
function SuperType(name){
  this.name=name
}
SuperType.prototype.sayName=function(){
  alert(this.name)
}
//子构造函数
function SubType(name,age){
  SuperType.call(this,name)
  this.age=age
}
var sub=new SubType('张三',12)
sub.name//"张三"
sub.sayName//undefined
sub.__proto__===SubType.prototype//true
SubType.prototype.__proto__===SuperType.prototype//false

先用call方法继承构造器里的属性,此时的关系为,sub——>SubType——>Object,要实现继承要把SubType.prototype.__proto__===SuperType.prototype 你可能想这么写 SubType.prototype = SuperType.prototype,但这样是不行的,他们俩公用一个原型的话,SubType就没有存在的意义了,我们可以用下面的代码

function F(){}//先创建了一个临时性的构造函数
F.prototype = SuperType.prototype;//将父原型作为临时构造函数的原型
var prototype=new F()//这边也可以直接用new SuperType(),但这样加上上面的call方法会导致2次调用SuperType
prototype.contructor=SubType
SubType.prototype=prototype//然后将临时构造函数的实例作为子构造函数的原型
SubType.prototype.__proto__===SuperType.prototype//true

下面来看看ES6继承的写法

class SuperType{
  constructor(name){
    this.name=name
  }
  sayName(){
    alert(this.name)
  }
}
class SubType extends SuperType{
  constructor(name,age){
    super(name)//必须调用下父类
    this.age=age
  }
  sayAge(){
    alert(this.age)
  }
}
var sub=new SubType('张三',12)
sub.__proto__===SubType.prototype//true
SubType.prototype.__proto__===SuperType.prototype//true

注意这边会多一个联系,这个是ES6特有的

SubType.__proto__===SuperType//true

这样的结果是因为,类的继承是按照下面的模式实现的。

class SuperType {
}
class SubType {
}
// B 的实例继承 A 的实例  B.prototype.__proto__=A.prototype
Object.setPrototypeOf(SubType.prototype, SuperType.prototype);
// B 继承 A 的静态属性  B.__proto__=A
Object.setPrototypeOf(SubType , SuperType );

这两条继承链,可以这样理解:作为一个对象,子类(SubType)的原型(__proto__属性)是父类(SuperType);作为一个构造函数,子类(SubType)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例。

你可能感兴趣的:(关于原型链,__proto__和prototype,继承)