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
属性)的实例。
网友评论