真正意义上来说 并不是一门面向对象的语言,没有提供传统的继承方式,但是它提供了一种原型继承的方式,利用自身提供的原型属性来实现继承。

 

原型继承

原型继承的定义

当你阅读关于JS原型继承的解释时,你时常会看到以下这段文字:

  当查找一个对象的属性时, 会向上遍历原型链,直到找到给定名称的属性为止。——出自 秘密花园

大多数 的实现用 __proto__ 属性来表示一个对象的原型链。在这篇文章里我们将看到 __proto__与 prototype 的区别何在。

注:__proto__ 是一个不应在你代码中出现的非正规的用法,这里仅仅用它来解释 原型继承的工作原理。

以下代码展示了JS引擎如何查找属性:

function getProperty(obj, prop) { 
    if (obj.hasOwnProperty(prop)) 
        return obj[prop] 
 
    else if (obj.__proto__ !== null) 
        return getProperty(obj.__proto__, prop) 
 
    else 
        return undefined 
} 

形式上的理解

类名.prototype.方法名=function(参数){方法体}

< >
function Myclass(x,y){
    this.x=x;
    this.y=y;
    this.say=function(){
        p(this.x+\":\"+this.y);
    }
}
Myclass.prototype.show=function(){
    p(this.x,this.y);
}
var obj1=new Myclass(1,2);
obj1.show();
p(\'show\' in obj1);//true
p(obj1.hasOwnProperty(\'show\')) //false
p(obj1.hasOwnProperty(\'say\'));  //true
p(\'show\' in Myclass);//false
p(\"say\" in Myclass);//false
p(typeof Myclass);//function
</ >

 

原型链

写过继承的人都知道,继承可以多层继承。而在JS中,这种就构成了原型链。那么,原型链是什么?一个实例,至少应该拥有指向原型的proto属性,这是 中的对象系统的基础。不过这个属性是不可见的,我们称之为“内部原型链”,以便和构造器的prototype所组成的“构造器原型链”(亦即我们通常所说的“原型链”)区分开。

原型继承支持一种称为原型链的功能,使用原型链有2个前提

a.所有的函数(对象)都具有名为prototype的属性(prototype属性所引用的对象则称为prototype对象) 

【对象实例 如上代码中的obj1没有prototype属性】

< >
p( \'prototype\' in obj1);//false
p(\'__proto__\' in obj1);//true
p(\'prototype\' in Myclass);//true
p(\"__proto__\" in Myclass);//true
 </ >

b.所有的对象都含有一个(隐藏的)链接,用以指向在对象生成过程中所使用的构造函数(Function对象)的prototype对象b.所有的对象都含有一个(隐藏的)链接,用以指向在对象生成过程中所使用的构造函数(Function对象)的prototype对象

< >
p(obj1.__proto__===Myclass.prototype);//true
p(Myclass.__proto__);// function Empty() {}
p(obj1.prototype);//undefined
p(\'x\' in Myclass);//false

</ >

 

js原型继承的几种方式

1. 原型链继承

function Show(){
this.name=\"run\";
}

function Run(){
this.age=\"20\"; //Run继承了Show,通过原型,形成链条
}
Run.prototype=new Show();
var show=new Run();
alert(show.name)//结果:run

 

2. 构造函数继承(对象冒充继承)

为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题 

function Box(age){
this.name=[\'Lee\',\'Jack\',\'Hello\']
this.age=age;
}
function Desk(age){
Box.call(this,age); //对象冒充,给超类型传参
}
var desk = new Desk(200);
alert(desk.age);//200
alert(desk.name);//[\'Lee\',\'Jack\',\'Hello\']
desk.name.push(\'AAA\'); //添加的新数据,只给 desk
alert(desk.name)//[\'Lee\',\'Jack\',\'Hello\',\'AAA\']

 

3. 组合继承(原型链继承+构造函数继承)

借用构造函数虽然解决了刚才两种问题, 但没有原型, 复用则无从谈起。 所以, 我们需要原型链+借用构造函数的模式,这种模式成为组合继承。

function Box(age) {
this.name = [\'Lee\', \'Jack\', \'Hello\']
this.age = age;
}
Box.prototype.run = function () {
return this.name + this.age;
};
function Desk(age) {
Box.call(this, age); //对象冒充
}
Desk.prototype = new Box(); //原型链继承
var desk = new Desk(100);
alert(desk.run());

 

4. 原型式继承

这种继承借助原型并基于已有的对象创建新对象,同时还不必因此创建自定义类型 

function obj(o) { //传递一个字面量函数
function F() {} //创建一个构造函数
F.prototype = o; //把字面量函数赋值给构造函数的原型
return new F(); //最终返回出实例化的构造函数
}
var box = { //字面量对象
name : \'Lee\',
arr : [\'哥哥\',\'妹妹\',\'姐姐\']
};
var box1 = obj(box); //传递
alert(box1.name);
box1.name = \'Jack\';
alert(box1.name);
alert(box1.arr);
box1.arr.push(\'父母\');
alert(box1.arr);
var box2 = obj(box); //传递
alert(box2.name);
alert(box2.arr); //引用类型共享了

 

5. 寄生组合式继承

寄生组合式继承解决了两次调用的问题,组合式继承就会有两次调用的情况

基本模型如下:

function  (o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function inheritPrototype(subType, superType) {
    var prototype =  (superType.prototype);  //创建对象
    prototype.constructor = subType;              //增强对象
    subType.prototype = prototype;                //指定对象
}
收藏 打印