JavaScript是一种基于对象的语言,与传统面向对象语言(C#、C++)相比,JavaScript中没有类的概念,其继承有两种基本形式:基于对象的继承和基于类型的继承(原型链继承)。无论哪种形式的继承,都是基于一个已经存在的对象创建一个新对象。
1.类继承的实现
基于对象的继承
ECMAScritp5中提供了一个Object.create()方法,可以简单的实现对象继承。在基于对象的继承中,一个对象继承另一个对象是不需要调用构造函数的。继承后的子类就拥有了父类的属性和方法,就如同将子类的原型设置为父类。示例如下:
var Base = { name: 'my name is Liu', sayName : function(){ return this.name; } } var Sub = Object.create(Base); console.log(Sub.sayName()); // -> my name is Liu console.log(Sub.__proto__.sayName()); // -> my name is Liu
从上面代码最后一行可以看出,子类实际上还是通过原型链访问父类中的属性和方法的。对子类属性和方法的重写将切断对原型链上属性和方法的访问,但依然可以通过__proto__属性访问原型(父类)上的方法。示例如下:
Sub.sayName = function() { return 'my name is Sub'; } console.log(Sub.sayName()); // -> my name is Sub console.log(Sub.__proto__.sayName()); // -> my name is Liu
基于类型的继承
基于类型的继承是通过构造函数实现的,这种继承方式依赖于原型(prototype),相比基于对象的继承,基于类型的继承更加灵活。每个函数对象都有一个的prototype属性,通过这个属性可以将属性和方法写到对象的原型链上,实现对象的扩展及继承。示例如下:
//类 function Base () { this.name = 'Base'; } Base.prototype.sayName = function() { return this.name; } //实例 var instace = new Base(); console.log(instace.sayName()); // -> Base
继承父类,并扩展父类中的方法。示例如下:
//父类 function Base() { this.name = 'Base'; } Base.prototype.sayName = function() { return this.name; } //子类 function Sub() { this.name = 'Sub'; } Sub.prototype = new Base(); Sub.prototype.sayName = function() { return this.name; } Sub.prototype.myName = function(name) { return name; } //对象实例 var subInstace = new Sub(); //子类中的方法 console.log(subInstace.sayName()); // -> Sub //父类中的方法 console.log(subInstace.__proto__.sayName()); // -> Base //子类扩展的方法 console.log(subInstace.myName('myName')); // -> myName
2.属性和方法的可访问性
JavaScript中没有类的概念,但通过原型链可以实现继承并模拟出许出类似传统面向对象语言的特性。在类型继承中,类的方法分为:公有方法(原型方法)、私有方法、特权方法(对象方法)、静态方法(类方法),类的属性分为:公有属性、私有属性、静态属性、原型属性。这些属性分别通过var、this和prototype实现,在使用和可访问性上有所区别。
this关键字
在JavaScript 中this对象是在运行时基于函数的执行环境绑定的,总是指向当前执行函数的作用域,该关键字和JavaScript函数的执行环境有关。在没有明确的当前对象时(如:匿名函数),this指向全局对象,在浏览器环境中为window对象。而当函数被作为某个对象的方法调用时,this等于那个对象。this在实现类及继承时也有重要的作用,实现公有属性及特权方法,需要将属性或方法放以this作用域上。
原型属性、公有属性、公有方法
公有属性在构造函数内部通过this关键字定义,在类实例化后才能访问。公有方法又称原型方法,是指通过prototype属性写到原型链上的方法,可访问公有属性、私有属性、原型属性,可在实例化后被调用或被特权方法或其它公有方法调用。原型属性与公有方法一样通过prototype被定义,其可访问性与公有属性类型,不同的是原型属性被写到对象的原型中。示例如下:
function Base () { //私有属性 var privateVar = 'privateVar'; //公有属性 this.publicVar = 'publicVar'; } //静态属性 Base.prototype.staticVar = 'staticVar'; //原型属性 Base.prototype.prototypeVar = 'prototypeVar'; //公有方法 Base.prototype.publicMethod = function(){ console.log(this.privateVar); console.log(this.staticVar); console.log(this.prototypeVar); console.log(this.publicVar); } //实例化,并调用公有方法及属性 var instace = new Base(); console.log(instace.publicVar); // -> publicVar instace.publicMethod(); // -> undefined // -> staticVar // -> prototypeVar // -> publicVar //原型属性 console.log(instace.__proto__.prototypeVar); // -> prototypeVar
私有属性和私有方法
私有属性在构造函数内部,通过var关键字定义,只能在私有方法和特权方法中访问。私有方法在构造函数内部定义,能被私有方法和特权方法访问,只能访问私有属性和私有方法。示例如下:
function Base () { //私有属性 var privateVar = 'privateVar'; //公有属性 this.publicVar = 'publicVar'; //私有方法 function privilegeMethod () { console.log(privateVar); // -> privateVar console.log(Base.staticVar); // -> undefined console.log(this.prototypeVar); // -> undefined console.log(this.publicVar); // -> undefined } //另一个私有方法 function otherPrivilegeMethod() { //可调用私有方法 privilegeMethod(); } //特权方法 this.privateMethod = function() { //可调用私有方法 privilegeMethod(); } privilegeMethod(); } //静态属性 Base.prototype.staticVar = 'staticVar'; //原型属性 Base.prototype.prototypeVar = 'prototypeVar'; //实例化后私有方法被调用 var instace = new Base();
特权方法
特权方法在构造函数内部通过this关键字定义,可调用所有类型的方法及变量,可以在对象实例化后被调用。标例如下:
function Base () { //私有属性 var privateVar = 'privateVar'; //公有属性 this.publicVar = 'publicVar'; //私有方法 function priviteMethod () { console.log('priviteMethod'); } //特权方法 this.privilegeMethod = function() { //可调用私有方法 priviteMethod(); //可调用公有方法 this.publicMethod(); //可调用静态方法 Base.staticMethod(); //可访问私有属性 console.log(privateVar); //可访问公有属性 console.log(this.publicVar); //可访问静态属性 console.log(Base.staticVar); //可访问原型属性 console.log(this.prototypeVar); } } //静态属性 Base.staticVar = 'staticVar'; //原型属性 Base.prototype.prototypeVar = 'prototypeVar'; //公有方法 Base.prototype.publicMethod = function(){ console.log('publicMethod'); } Base.staticMethod = function(){ console.log('staticMethod'); } //实例化后可访问特权方法 var instace = new Base(); instace.privilegeMethod(); //priviteMethod //publicMethod //staticMethod //privateVar //publicVar //staticVar //prototypeVar
静态属性和静态方法
静态属性和方法无需实例化即可访问,静态方法只能访问静态属性和调用静态方法,静态属性和方法不能被继承。示例如下:
function Base () { } //静态属性 Base.staticVar = 'staticVar'; //静态方法 Base.staticMethod = function(){ console.log(Base.staticVar); this.otherStaticMethod(); } Base.otherStaticMethod = function(){ console.log('otherStaticMethod'); } Base.staticMethod(); // -> staticVar // -> otherStaticMethod