Javascript的继承和标准的oop继承有很大的区别,Javascript的继承是采用原型链的技术,每个类都会将“成员变量”和“成员函数”放到
prototype 上,Js++都过superclass将其链接起来,即 C.prototype.superclass
= C.superclass = P.prototype;
当 var c = new C()时,c.__proto__ = C.prototype ;

c访问“成员变量”时,如果在__proto__无法获取时,就会到C.prototype查找,如果又不存在,又会到父类的prototype查找,由于只有
__proto__
是对象创建时分配的(每个对象独立分配),其他都是定义时分配的(每个对象共享),此时,如果访问C.prototype中“成员变量”是对象时,不修改“成员变量”的本身,而是修改“成员变量”对象的成员时,修改的“成员变量”对象的成员就会被所有对象实例共享,这样就违背类设计的初衷。
例如:

(一)原型链继承机制

基本思想是利用原型链继承另一个引用类型的属性和方法

  • 创建Car构造函数

function Car(){
    this.color = "黑色";// 汽车基础颜色
}

Car.prototype.changeColor = function(otherColor){
    // 提供更换颜色方法
    this.color = otherColor;
}
  • 创建Audi构造函数

function Audi(master){
    this.master = master;
}
  • Audi原型链继承Car

Audi.prototype = new Car();
  • 创建Audi原型链方法

Audi.prototype.getColor = function(){
    return this.color;
}
Audi.prototype.getMessage = function(){
    return this.master+"的奥迪颜色是"+this.color;
}
  • 实例继承测试

var car1 = new Audi("老王");
console.log(car1.getColor());// 黑色
console.log(car1.getMessage());// 老王的奥迪颜色是黑色

验证原型和实例之间的关系

  • 第一种instanceof

console.log(car1 instanceof Object);// true
console.log(car1 instanceof Car);// true
console.log(car1 instanceof Audi);// true
  • 第二种isPrototypeOf

console.log(Object.prototype.isPrototypeOf(car1));// true
console.log(Car.prototype.isPrototypeOf(car1));// true
console.log(Audi.prototype.isPrototypeOf(car1));// true

通过原型链实现继承时,不能使用对象字面量创建原型方法!!!

原型对象

  1. 所有的javascript方法都是一个对象—-称为函数对象

  2. 函数对象有个自带属性prototype,这个属性就是原型对象(普通对象没有prototype)

  3. 原型对象用来做什么—–继承

var person = function(name){
    this.name = name
};

person.prototype.getName = function(){
    return this.name;
}

var hhc = new person('huahaichuan');
console.log(hhc.getName());

怎么实现的?往下继续原型链

 1  function Father() {
 2             this.fatherValue = "爸爸";
 3         }
 4         Father.prototype.getFatherValue = function () { 
 5             return this.fatherValue
 6         }
 7         function Son() {
 8             this.sonValue = "儿子";
 9         }
10         Son.prototype = new Father();
11         Son.prototype.getSonValue = function () {
12             return this.sonValue;
13         }
14         var xiaoMing = new Son();
15         alert(xiaoMing.getSonValue());

复制代码 代码如下:

原型链

  1. JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype
    ,这句话很费解

console.log(person.prototype)
console.log(hhc._proto_)

通过控制台发现hhc.__proto__==person.prototype,再回过头去__proto__指向创建它的函数对象的原型对象prototype就不难理解了

同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype

继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null

我们把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链。

先实例化father,并且把这个对象赋值给son的原型.
son继承了father之后,再修改原型,添加getSonValue()方法.
通过原型来实现继承时,原型实际上会变成另一个类型的实例.于是,原先的实例属性也就顺利成章的变成了现在的原型属性.
这里存在一个问题,子类son无法向父类father中传参.这个问题会在下一章的借用构造函数继承中有所优化.

‘package’.j(function () {
        ‘class A’.j(function () {
            jpublic({
                v:{a: 1}
            });
            jprivate({
                p:{a:1}
            });
            jprotected({
                x:{a:1}
            });

思考

var food = function(){}
food.price=5;

var fruit = function(){}
fruit.prototype=food;
var apple = new fruit();

console.log(fruit.price);
console.log(apple.price);

        });

总结

  1. 原型和原型链是JS实现继承的一种模型。

  2. 原型链的形成是真正是靠__proto__ 而非prototype

        ‘class B extends A’.j(function () {

补充和对比几种继承方式

  1. 原型链继承

var people = function(){
    this.getName=function(){
        return this.name;
    }
}

var person = function(name){
    this.name=name;
};

person.prototype = new people();

var hhc = new person('huahaichuan');
console.log(hhc.getName());

原型链方式继承缺点,就是实例化子类时不能将参数传给父类

  1. 临时属性方式

var people = function(age){
    this.age=age;
    this.getName=function(){
        return this.name;
    }
}

var person = function(name,age){
    this.temp = people;
    this.temp(age);
    delete this.temp;
    this.name=name;
};

var hhc = new person('huahaichuan',25);

console.log(hhc.getName());
console.log(hhc.age);
  1. call继承

var people = function(age){
    this.age=age;
    this.getName=function(){
        return this.name;
    }
}

var person = function(name,age){
    this.name = name;
    people.call(this,age);
};

var hhc = new person('huahaichuan',25);

console.log(hhc.getName());
console.log(hhc.age);

4.apply继承

var people = function(age){
    this.age=age;
    this.getName=function(){
        return this.name;
    }
}

var person = function(name,age){
    this.name = name;
    people.apply(this,[age]);
};

var hhc = new person('huahaichuan',25);

console.log(hhc.getName());
console.log(hhc.age);

call和apply的区别是传参形式不同

5.混合继承

var people = function(age){
    this.age=age;
}

people.prototype.getName=function(){
    return this.name;
}

var person = function(name,age){
    this.name = name;
    people.call(this,age);                    //成员变量采用对象冒充方式
};

person.prototype = new people();            //成员方法采用原型链方式
var hhc = new person('huahaichuan',25);

console.log(hhc.getName());
console.log(hhc.age);

        });
});

var b1 = new B();
b1.v.a = 5;
b1.x.a = 5;
var b2 = new B();

console.log(b1.v.a) // 输出为 5
console.log(b1.x.a) // 输出为 5

console.log(b2.v.a) // 输出也为 5,并不是预想的 1
console.log(b2.x.a) // 输出为 1
console.log(b2.p.a) // 不可用,会提示 p不存在

如何解决此问题?
A. 将 v
这样的成员“成员变量”(其本身是对象)不在原型链上定义,而是在构造函数中调用,此时,创建对象实例时,就会在对象的__proto__上分配。

Js++提供了类似的方法,只要在jprivate中定义的“成员变量”或“成员函数”都会分配到对象的__proto__上,且只有本实例可用,
jprotected中定义的“成员变量”(其本身是对象)也会分配到对象的__proto__上,且只有继承他的可用,

B. 原型链上只定义只读的“成员变量”(其本身是对象)

C.jpublic
定义的“成员变量”(其本身是对象)中的成员,只是只读成员,切记不可赋值,否则会在各个实例中共享。

protot…

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图