原型及继承

原型

每个函数都有一个prototype属性,指向一个原型对象,这个对象专门保存特定类型的所有实例【共有的属性和方法】。

所有原型对象都会自动获得constructor属性,指向构造函数。

在调用构造函数创建新实例对象时,会自动设置新实例的内部属性[[Prototype]]指向构造函数的原型对象。

所有对象都有[[Prototype]]属性,字面量对象的原型为Object.prototype。

所有子对象共有的成员属性,都要保存在构造函数的原型对象中。即一次定义,重复使用。

每当代码读取对象的某个属性时,会执行一次搜索,目标是给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了给定的属性,则立即停止继续搜索并返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象。如果在原型对象中查找到该属性,则返回该属性的值。

实例中与原型同名的属性会屏蔽原型中的那个属性,所以不能在实例中修改原型中的属性,必须在原型上修改。

组合使用构造函数和原型:构造函数用于定义实例属性,原型用于定义方法(引用类型)和共享的属性。这样每个实例都会获得一份自己独立的实例属性副本,彼此间互不影响。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = [“Shelby”, “Court”];
}

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

var person1 = new Person(“Nicholas”, 29, “Software Engineer”);
var person2 = new Person(“Greg”, 27, “Doctor”);
person1.friends.push(“Van”);
alert(person1.friends); //”Shelby,Court,Van”
alert(person2.friends); //”Shelby,Court”
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true 

原型相关API:

  • 获取原型对象:

  从构造函数获得原型对象: 构造函数.prototype

  从子对象获得父级原型对象:

    子对象.__proto__(有兼容性问题)

  Object.getPrototypeOf({...}) === Object.prototype

  • 判断原型对象是否在实例的原型链上:

    obj.isPrototypeOf(实例)   //Array.prototype.isPrototypeOf([])判断数组

  • 自有属性和共有属性:

    obj.hasOwnProperty()     //检测一个属性是否存在于实例中。

  • in关键字:property in obj

    在obj的原型链上查找指定属性名property

  • 删除对象的属性:delete obj.property  //不能删除共有属性

原型链

本质是重写原型对象,让一个类型的原型对象等于另一个类型的实例

function SuperType(){
    this.property = true;
}

SuperType.prototype.getSuperValue = function(){
    return this.property;
};

function SubType(){
    this.subproperty = false;
}

//重写原型对象,以新的原型对象替换默认原型,实现继承。
//不能是父类的原型对象,只能是实例。否则子类修改原型属性时会影响父类。
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
    return this.subproperty;
};

var instance = new SubType();
console.log(instance.getSuperValue()); //true

继承的实现

1. 单个继承

Object.create(Proto [,propertiesObject]);  // 使用指定的原型对象和属性创建一个新对象。

等价于:

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

  Object.setPrototypeOf(Plane.prototype,Flyer.prototype);

  //直接设置一个对象的内部[[Prototype]]属性到另一个对象或null,实现继承。子类型的原型对象依然存在,子类型原型中重写父类型fly方法,不会影响父类型的原型对象。有性能问题,慎用

2.原型链方式

因为有可能重写或添加子类型的方法,为了保证之后创建的所有子类型都继承同一个超类型,一定要在创建新实例之前修改原型对象。

  子类型构造函数.prototype=超类型的实例;

注意:

  通过原型链实现的继承,不能使用对象字面量创建原型方法,因为这样会重写原型链。

  不能向超类型的构造函数中传递参数。

  如果原型被重写,最好同时修改原型的constructor属性。

  如果超类型的属性中有引用类型值,所有实例共享一个属性,不好。

3. 组合继承

(1)在子类型构造函数内部用call/apply调用父类型的构造函数。

function SubType(sColor, sName) {
    SupType.apply(this, arguments);
    this.name = sName;
}

注意:arguments指SubType接收到的所有参数,SupType从0位开始按顺序读取。所以SubType构造函数的参数中前面是SupType的参数,之后才是SubType用到的参数(SubType能够用标识符识别参数的值,而SupType只能通过位序使用)。

(2)将父类型的实例赋值给子类型的原型,继承父类共享的属性和方法。

  SubType.prototype=new SuperType();
  SubType.prototype.constructor=SubType;     //原型重写时最好同时修改

注意:组合继承会两次调用父类构造函数,在子类实例和原型中会有重复的属性。

4.寄生式组合继承

通过借用构造函数来继承属性,通过原型链的混合形式来继承方法。不必为了指定子类型的原型而调用超类型的构造函数,只需父类型的原型副本。

//使用指定的原型对象创建一个新的空对象作为中间体,类似于Object.create() 方法

//使用指定的原型对象创建一个新的空对象作为中间体,类似于Object.create() 方法
function cloneProto(o){
    function F(){}
    F.prototype = o;
    return new F();
}

//子类型的原型为上述空对象
function inheritPrototype(subType, superType){
    var prototype = cloneProto(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}

function SuperType(name){
    this.name = name;
    this.colors = [“red”, “blue”, “green”];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    SuperType.call(this, name); //继承了父类型,同时向父类型构造函数传递了参数
    this.age = age;
}

//不能直接继承父类型的原型对象(公用一个原型,子类型修改会影响父类型)
//也不能直接继承父类型的实例(父类型的实例属性也会被子类型实例继承,需要再次覆盖)
//通过一个中间对象只继承父类型的原型方法和属性,子类型修改时也不影响父类型
inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function(){
    alert(this.age);
};
时间: 2024-10-19 05:08:17

原型及继承的相关文章

JavaScript 随笔2 面向对象 原型链 继承

第六章 面向对象的程序设计 1.创建对象的几种方式 A)工厂模式 function CreatObj(name,sex,age){ this.name=name; this.sex=sex; this.age=age; } 缺点:虽然可以批量创建对象,却不能知道对象的类型 只知道他是Object类型: B)构造函数 function Person(name,sex){ this.name=name; this.sex=sex; this.sayName=function(){ alert(thi

JavaScript之基础-16 JavaScript 原型与继承

一.JavaScript 原型 原型的概念 - 在JavaScript中,函数本身也是一个包含了方法和属性的对象 - 每个函数中都有一个prototype属性,该属性引用的就是原型对象 - 原型对象是保存共享属性值和共享方法的对象 为对象扩展属性 - 扩展单个对象的成员 - 扩展共享的属性值 - 内存图描述 删除属性 - 可以使用delete关键字删除对象的属性 自由属性与原型属性 - 自有属性:通过对象的引用添加的属性;其它对象可能无此属性;即使有,也是彼此独立的属性 emp1.job = '

javascript原型链继承

一.关于javascript原型的基本概念: prototype属性:每个函数都一个prototype属性,这个属性指向函数的原型对象.原型对象主要用于共享实例中所包含的的属性和方法. constructor属性:每个原型对象都有一个constructor属性,这个constructor属性包含一个指向prototype属性所在函数的指针. 例如 Foo.prototype.constructor指向Foo函数.这个属性是只读的. __proto__属性(ES6通过对__proto__属性进行标

面向对象的原型与继承

1. 为什么需要原型 构造器创建对象的时候, 实际上会有成员重复 如果使用 构造器 this.方法名 = function .... 方式创建对象. 那么每一个对象 对应的方法就会重复. 解决办法就是让这个方法( 函数 )共享 -> 将函数写到外面, 那么 Person 在初始化对象的时候就不会再创建一个函数了. 只需要将 外面的函数引用 交给对象即可. 缺点: 一个对象可能有 n 多方法. 如果将所有的东西 都放到外面, 与其他库 冲突的几率就会变大. 所以不宜采取该方法. -> 将所有的方

js中的原型、继承的一些想法

最近看到一个别人写的js类库,突然对js中的原型及继承产生了一些想法,之前也看过其中的一些内容,但是总不是很清晰,这几天利用空闲时间,对这块理解了一下,感觉还是有不通之处,思路上没那么条理,仅作为分享, 一.instanceof 在JavaScript有instanceof运算符,是二元运算符,使用方法 instanceA instanceof A,返回值是布尔值(boolean),含义是判断instanceA是否是A的一个实例,其实质是判断A.prototype===instanceA.__p

JS面向对象,原型,继承

ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,ECMAScript没有类的概念,因此它的对象也与基于类的语言中的对象有所不同.var box = new Object();box.name = 'Lee';box.age = 100;box.run = function(){ return this.name + this.age + '运行中...'; //th

面向对象之笔记二——————原型与继承

原型与继承 原型 为什么需要原型? 构造器创建对象的时候, 实际上会有成员重复 如果使用 构造器 this.方法名 = function .... 方式创建对象. 那么每一个对象对应的方法就会重复 function Person( name ) { this.name = name; this.sayHello = function() { console.log( '你好,我是 ' + this.name ); }; } var p1 = new Person( 'Hello' ); var

《javascript高级程序设计(第二版)》学习(4)原型与继承

声明:这类属于学习笔记,主要是摘录书中内容,比较少的整理.内容经常是跳跃的,建议您阅读书本,收益更大. function Person(){} Person.prototype.name="tom"; //这里等于重写了原型对象 //切断了与构造函数之间的联系 Person.prototype={ name:"mike", age:22 }; //得到的不是Person了,而是Object console.log(Person.prototype.construct

原型与继承学习笔记4

经过前三节的研究,我们终于可以对js这门无法实现接口继承的语言进行实现继承,为了更好的面向对象..... 原型链继承 这个原型链我们上节引用了一下,用它实现继承的基本思想是利用原型让一个引用类型引用另一个引用类型的属性和方法.即把一个函数(狭义上来举例)的所有属性和方法(这个函数的实例)赋值给另一个函数的prototype,使一个函数的实例可以通过__proto__原型一层层的调用另一个函数的所有属性. 有点绕,简单的说就是把超类的实例赋值给子类的prototype.看个例子: 1 functi

JavaScript中的原型和继承

请在此暂时忘记之前学到的面向对象的一切知识.这里只需要考虑赛车的情况.是的,就是赛车. 最近我正在观看 24 Hours of Le Mans ,这是法国流行的一项赛事.最快的车被称为 Le Mans 原型车.这些车虽然是由"奥迪"或"标致"这些厂商制造的,可它们并不是你在街上或速公路上所见到的那类汽车.它们是专为参加高速耐力赛事而制造出来的. 厂家投入巨额资金,用于研发.设计.制造这些原型车,而工程师们总是努力尝试将这项工程做到极致.他们在合金.生物燃料.制动技术