高程(第六章) 面向对象的程序设计

ECMA-262把对象定义为:“无序属性的鸠合,其属性可以包含基础值、对象或许函数”。严厉来讲,这就相当于说对象是一组没有特定递次的值。

1 明白对象

建立对象:

var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";

person.sayName = function(){
    alert(this.name);
};

字面量情势:

var person = {
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function(){
        alert(this.name);
    };
}

1.1 属性范例

ECMA-262第5版在定义只需内部才用的特征时,形貌了属性的种种特征。ECMA-262定义这些特征是为了完成JavaScript引擎用的,因而在JavaScript中不能直接接见它们。
ECMAScript中有两种属性:数据属性接见器属性

1.1.1 数据属性

数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个形貌其行动的特征

  • [[Configurable]]:示意可否经由历程delete删除属性从而从新定义属性,可否修正属性的特征,或许可否把属性修正为接见器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特征的属性值为true
  • [[Enumerable]]:示意可否经由历程for-in轮回返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特征的默许值为true
  • [[Writable]]:包含这个属性的数据值。读取属性值的时刻,从这个位置读;写入属性值的时刻,把新值保留在这个位置。这个特征的默许值为undefined

要修正属性默许的特征,必需运用ECMAScript 5Object.defineProperty()要领:

var person = {};
Object.defineProperty(person, "name", {
    writable : false,
    value : "Nicholas"
});

console.log(person.name);    //"Nicholas"
person.name = "Greg";
console.log(person.name);    //"Nicholas"

在严厉形式下,上面的赋值操纵将会致使抛出毛病

var person = {};
Object.defineProperty(person, "name", {
    configurable : false,
    value : "Nicholas"
});

console.log(person.name);    //"Nicholas"
delete person.name;
console.log(person.name);    //"Nicholas"

一旦把属性定义为不可设置的,就不能再把它变回可设置了:

var person = {};
Object.defineProperty(person, "name", {
    configurable : false,
    value : "Nicholas"
});

Object.defineProperty(person, "name", {
    configurable : true,    //抛出毛病
    value : "Nicholas"
});

也就是说,可以屡次挪用Object.defineProperty()要领修正统一个属性,但在把configurable特征设置为false以后就会有限定了

注重!应用Object.defineProperty()要领建立一个新的属性时,假如不指定,configurableenumerablewritable特征的默许值都是false

1.1.2 接见器属性

接见器有以下4个属性:

  • [[Configurable]]:示意可否经由历程delete删除属性从而从新定义属性,可否修正属性的特征,或许可否把属性修正为接见器属性。关于直接在对象上定义的属性,这个特征的属性值为true
  • [[Enumerable]]:示意可否经由历程for-in轮回返回属性。关于直接在对象上定义的属性,这个特征的默许值为true
  • [[Get]]:在读取属性时挪用的函数。默许值为undefined
  • [[Set]]:在写入属性时挪用的函数。默许值为undefined

接见器属性不能直接定义,必需运用Object.defineProperty()来定义:

var book = {
    _year : 2004,
    edition : 1
};

Object.defineProperty(book, "year", {
    get : function(){
        return this._year;
    },
    set : function(){
        if(newValue > 2004){
            this._year = newValue;
            this.edition += newValue - 2004;
        }
    }
});

book.year = 2005;
console.log(book.edition);    //2

不一定非要同时指定getter和setter。只指定getter意味着属性是不能写,尝试写入属性会被疏忽。

1.2 定义多个属性

  • Object.defineProperties():应用这个要领可以经由历程形貌符一次定义多个属性
var book = {};

Object.defineProperties(book, {
    _year : {
        writable : true,
        value : 2004
    },

    edition : {
        writable : true,
        value : 1
    },

    year : {
        get : function(){
            return this._year;
        },

        set : function(){
            if(newValue > 2004){
                this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});

1.3 读取属性的特征

  • Object.getOwnPropertyDescriptor():可以获得给定属性的形貌符
//接上段代码
    var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
    console.log(descriptor.value);                //2004
    console.log(descriptor.configurable);    //false
    console.log(typeof descriptor.get);        //"undefined"

    var descriptor = Object.getOwnPropertyDescriptor(book, "year");
    console.log(descriptor.value);                //undefined
    console.log(descriptor.enumerable);        //false
    console.log(typeof descriptor.get);        //"function"

关于接见器属性yearget是一个指向getter函数的指针

2 建立对象

Object组织函数或对象字面量都可以用来建立单个对象,但这些体式格局有个显著瑕玷:运用统一个接口建立很多对象,会发生大批的反复代码。为处理这个题目,人们开始运用工场形式的一种变体。

2.1 工场形式

工场形式是软件工程范畴一种广为人知的设想形式,这类形式笼统了建立详细对象的历程。斟酌到在ECMAScript中没法建立类,开发人员就发清楚明了一种函数,用函数来封装以特定接口建立对象的细节:

function createPerson(name, age, job){
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function(){
    console.log(this.name);
  };
  return o;
}

var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

工场形式虽然处理了建立多个相似对象的题目,但却没有处理对象辨认的题目(即如何晓得一个对象的范例)。

2.2 组织函数形式

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function () {
    console.log(this.name);
  };
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

组织函数形式与工场形式的差别之处:

  • 没有显式地建立对象;
  • 直接将属性和要领赋给了this对象;
  • 没有return语句。

根据通例,组织函数一直都应当以一个大写字母开首,而非组织函数则应当以一个小写字母开首。

要建立Person的新实例,必需运用new操纵符。以这类体式格局挪用组织函数现实上会阅历一下4个步骤:

  • 建立一个对象;
  • 将组织函数的作用域赋给新对象(因而this就指向了这个新对象);
  • 实行组织函数中的代码(为这个新对象增加属性);
  • 返回新对象。
console.log(person1.constructor == truPersone);   //true
console.log(person2.constructor == Person);   //true

console.log(person1 instanceof Object);     //true
console.log(person1 instanceof Person);     //true
console.log(person2 instanceof Object);     //true
console.log(person2 instanceof Person);     //true

建立自定义的组织函数意味着未来可以将它的实例标识为一种特定的范例;而这恰是组织函数形式赛过工场形式的处所

这类体式格局定义的组织函数是定义在Global对象(在浏览器中是window对象)中的

2.2.1 将组织函数看成函数

//看成组织函数运用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName();   //"Nicholas"

//作为一般函数挪用
Person("Greg", 27, "Doctor");   //严厉形式下会抛出毛病!
window.sayName();   //"Greg"

//在另一个对象的作用域中挪用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName();    //"Kristen"

2.2.2 组织函数的题目

运用组织函数的主要题目,就是每一个要领都要在每一个实例上从新建立一遍。在前面的例子中,person1person2都有一个名为sayName()的要领,但那两个要领不是统一个Function的实例

不要忘了——ECMAScript中的函数是对象,因而每定义一个函数,也就是实例化了一个对象。从逻辑角度讲,此时的组织函数也可以如许定义:

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = new Function("console.log(this.name);");    //此声明函数在逻辑上是等价的    
}

console.log(person1.sayName == person2.sayName);    //false

建立两个完成一样使命的Function实例确实没有必要;何况有this对象在,基础不用在实行代码前就把函数绑定到特定对象上。因而可以将函数定义转移到组织函数外部来处理这个题目:

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = sayName;
}

function sayName() {
  console.log(this.name);
};

如许做处理了两个函数做统一件事的题目,但是新题目又来了:在全局作用域中定义的函数现实上只能被某个对象挪用,这让全局作用域有点有名无实。更主要的是:假如对象须要定义很多要领,那末就要定义很多个全局函数,因而我们这个自定义的援用范例就涓滴没有封装性可言了。幸亏,原型形式可以处理这些题目

2.3 原型形式

function Person() {
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
  console.log(this.name);
};

var person1 = new Person();
person1.sayName();    //"Nicholas"

var person2 = new Person();
person2.sayName();    //"Nicholas"

console.log(person1.sayName == person2.sayName);    //true

2.3.1 明白原型对象

《高程(第六章) 面向对象的程序设计》

console.log(Person.prototype.isPrototypeOf(person1));   //true
console.log(Person.prototype.isPrototypeOf(person2));   //true

ECMAScript 5增加了一个新要领,叫Object.getPrototypeOf(),这个要领返回[[Prototype]]的值

console.log(Object.getPrototypeOf(person1) == Person.prototype);    //true
console.log(Object.getPrototypeOf(person1).name);   //"Nicholas"

不能经由历程对象实例重写原型中的值。假如在实例中增加一个属性,而该属性与实例原型中的一个属性同名,那末该属性将会屏障原型中的谁人属性:

function Person() {
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
  console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

console.log(person1.hasOwnProperty("name"));    //false
console.log("name" in person1);    //true

person1.name = "Greg";
console.log(person1.name);    //"Greg"——来自实例
console.log(person1.hasOwnProperty("name"));    //true
console.log("name" in person1);    //true

console.log(person2.name);    //"Nicholas"——来自原型
console.log(person2.hasOwnProperty("name"));    //false

delete person1.name;
console.log(person1.name);    //"Nicholas"——来自原型
  • in操纵符只需经由历程对象可以接见到属性就返回true
  • 运用hasOwnProperty()要领可以检测一个属性是存在于实例中,照样存在于原型中。这个要领(不要忘了它是从Object继承来的)只在给定属性存在于对象实例中时,才会返回true
function hasPrototypeProperty(object, name) {
  return !object.hasOwnProperty(name) && (name in object);
}
  //返回true则表明该属性存在于原型中
  //返回false则表明该属性存在于实例中

在运用for-in轮回时,返回的是一切可以经由历程对象接见的、可罗列的属性,个中包含实例和原型中的属性。

  • ECMAScript 5Object.keys()要领可以获得对象上一切可罗列的实例属性
var keys = Object.keys(Person.prototype);
alert(keys);    //"name, age, job, sayName"
//keys中保留一个数组,数组中是字符串"name, age, job, sayName"。
  • Object.getownPropertyNames()可以获得一切实例属性,不管它是不是可罗列
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys);    //"constructor, name, age, job, sayName"

2.3.3 更简朴的原型语法

function Person() {
}

Person.prototype = {
  name : "Nicholas",
  age : 29,
  job : "Software Engineer",
  sayName : function () {
    console.log(this.name);
  }
};

上面代码将Person.prototype设置为即是一个以对象字面量情势建立的新对象。效果雷同,但constructor属性不再指向Person了。因而上面运用的语法,本质上完整重写了默许的prototype对象,因而constructor属性也就变成了新对象的constructor属性(指向Object组织函数),不再指向Person函数

var friend = new Person();
console.log(friend instanceof Object);    //true
console.log(friend instanceof Person);    //true
console.log(friend.constructor == Object);    //false
console.log(friend.constructor == Person);    //true

假如constructor的值真的很主要,可以像下面如许特地将它设置回适当的值:

function Person() {
}

Person.prototype = {
  constructor : Person
  //……
};

这类体式格局重设constructor属性会致使它的[[Enumerable]]特征被设置为true,默许情况下,原声的constructor属性是不可罗列的

因而假如你运用兼容ECMAScript 5JavaScript引擎,可以试一试Object.defineProperty()

Object.defineProperty(Person.prototype, "constructor", {
  enumerable : false,
  value : Person
});

2.3.4 原型的动态性

在原型中查找值的历程是一次搜刮

var friend = new Person();
Person.prototype.sayHi = function(){
    console.log("hi");
}

friend.sayHi();    //"hi" (没有题目!)

重写原型对象:

function Person(){
}

var friend = new Person();
Person.prototype = {
  constructor : Person,
  name : "Nicholas",
  age : 29,
  job : "Software Engineer",
  sayName : function () {
    console.log(this.name);
  }
};
friend.sayName();   //error

《高程(第六章) 面向对象的程序设计》

重写原型对象切断了现有原型与任何之前已存在的对象实例之间的联络;它们援用的仍然是最初的原型

2.3.5 原生对象的原型

一切原生援用范例(Object、Array、String等)都在其组织函数的原型上定义了要领

console.log(typeof Array.prototype.sort);    //"function"
console.log(typeof String.prototype.substring);    //"function"

给原生对象的原型增加要领:

String.prototype.startsWith = function(text){
    return this.indexOf(text) == 0;
}

var msg = "Hello world!";
console.log(msg.startsWith("Hello"));    //true

2.3.6 原型对象的题目

关于包含援用范例值得属性来讲,能够涌现以下题目:

function Person() {
}

Person.prototype = {
  constructor : Person,
  name : "Nicholas",
  age : 29,
  job : "Software Engineer",
  friends : ["Shelby", "Court"],
  sayName : function () {
    console.log(this.name);
  }
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push("Van");

console.log(person1.friends);
console.log(person2.friends);
console.log(person1.friends === person2.friends);   //true

2.4 组合运用组织函数形式和原型形式

实例属性都在组织函数中定义,一切实例同享的属性和要领都在原型中定义:

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

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

这类组织函数与原型混成的形式,是如今在ECMAScript中运用最普遍、认同度最高的一种建立自定义范例的要领。可以说,这是用来定义援用范例的一种默许形式

2.5 动态原型形式

有其他OO言语履历的开发人员在看到自力的组织函数和原型时,很能够会觉得异常疑心。动态原型形式正式致力于处理这个题目的一个计划

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;

  //要领
  if(typeof this.sayName != "function"){
    Person.prototype.sayName = function () {
      console.log(this.name);
    }
  }
}

if语句搜检的可所以初始化以后应当存在的任何属性和要领——没必要用一大堆if语句搜检每一个属性和每一个要领;只需搜检个中一个即可。关于采纳这类形式的对象,还可以运用instanceof操纵符肯定它的范例

2.6 寄生组织函数形式

一般,在前述的几种形式都不实用的情况下,可以运用寄生组织函数形式。这类形式的基础思想是建立一个函数,该函数的作用仅仅是封装建立对象的代码,然后再返回新建立的对象;但从表面上看,这个函数又很像是典范的组织函数:

function Person(name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function () {
    console.log(this.name);
  };
  return o;
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();   //"Nicholas"

组织函数在不返回值的情况下,默许会返回新对象实例。而经由历程在组织函数的末端增加一个return语句,可以重写挪用组织函数时返回的值。

这个形式可以在特别的情况下用来为对象建立组织函数。假定我们想建立一个具有分外要领的特别数组。由于不能直接修正Array组织函数,因而可以运用这个形式:

function SpecialArray() {
  //建立数组
  var values = new Array();
  //增加值
  values.push.apply(values, arguments);
  //增加要领
  values.toPipedString = function () {
    return this.join("|");
  }
  //返回数组
  return values;
}

var colors = new SpecialArray("red", "blue", "green");
console.log(colors.toPipedString());    //"red|blue|green"

注重:返回的对象与组织函数或许与组织函数的原型属性直接没有关联;也就是说,组织函数返回的对象与在组织函数外部建立的对象没有什么差别。为此,不能依托instanceof操纵符来肯定对象范例。

由于存在上述题目,发起在可以运用其他形式的情况下,不要运用这类形式。

2.7 稳妥组织函数形式

所谓稳妥对象,指的是没有大众属性,而且其要领也不援用this的对象。稳妥对象最合适在一些平安的环境中(这些环境中会制止运用thisnew),或许在防备数据被其他应用顺序(如Mashup顺序)修改时运用。

function Person(name, age, job) {
  //建立要返回的对象
  var o = new Object();
  //可以在这里定义私有变量和函数

  //增加要领
  o.sayName = function () {
    console.log(name);
  }

  return o;
}

var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName();   //"Nicholas"

变量friend中保留的是一个稳妥对象,而除了挪用sayName()要领外,没有别的体式格局可以接见其数据成员。纵然有其他代码会给这个对象增加要领或数据成员,但也不能够有别的方法接见传入到组织函数中的原始数据。

稳妥组织函数形式供应的这类平安性,使得它异常适合在某些平安实行环境下运用——比方,ADsafeCaja供应的环境

3 继承

很多OO言语都支撑两种继承体式格局:接口继承和完成继承。接口继承只继承要领署名,而完成继承则继承现实的要领。由于函数没有署名,在ECMAScript中没法完成接口继承。ECMAScript只支撑完成继承,而且其完成继承主如果依托原型链来完成的

3.1 原型链

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

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

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

//继承了SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function () {
  return this.subProperty;
};

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

《高程(第六章) 面向对象的程序设计》

3.1.1 别忘记默许的原型

一切援用范例默许都继承了Object,而这个继承也是经由历程原型链完成的。
《高程(第六章) 面向对象的程序设计》

3.1.2 肯定原型和实例的关联

  • instanceof:只需用这个操纵符来测试实例与原型链中涌现过的组织函数,效果就会返回true
console.log(instance instanceof Object);      //true
console.log(instance instanceof SuperType);   //true
console.log(instance instanceof SubType);     //true
  • isPrototypeOf:只需是原型链中涌现过的原型,都可以说是该原型链所派生的实例的原型,因而该要领也会返回true
console.log(Object.prototype.isPrototypeOf(instance));      //true
console.log(SuperType.prototype.isPrototypeOf(instance));   //true
console.log(SubType.prototype.isPrototypeOf(instance));     //true

3.1.3 郑重地定义要领

子范例有时刻须要掩盖超范例中的某个要领,或许须要增加超范例中不存在的某个要领。但不管如何,给原型增加要领的代码一定要放在替代原型的语句以后

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

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

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

//继承了SuperType
SubType.prototype = new SuperType();

//增加新要领
SubType.prototype.getSubValue = function () {
  return this.subProperty;
};

//重写超范例中的要领
SubType.prototype.getSuperValue = function(){
    return false;
}

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

getSuperValue()是原型链中已存在的一个要领,重写这个要领将会屏障本来的谁人要领。当经由历程SubType的实例挪用getSuperValue()时,挪用的就是这个从新定义的要领;但经由历程SuperType的实例挪用getSuperValue()时,还会继承挪用本来的谁人要领

在经由历程原型链属性继承时,不能运用对象字面量建立原型要领。由于如许就会重写原型链:

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

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

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

//继承了SuperType
SubType.prototype = new SuperType();

//运用字面量增加新要领,会致使上一行代码无效
SubType.prototype = {
    //……
}

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

3.1.4 原型链的题目

包含援用范例值的原型会被一切实例同享。在经由历程原型来完成继承时,原型现实上会变成另一个范例的实例。因而,本来的实例属性也就变成了如今的原型属性了

function SuperType(){
  this.colors = ["red", "blue", "green"];
}

function SubType(){
}

//继承了SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);    //"red,blue,green,black"

var instance2 = new SubType();
console.log(instance2.colors);    //"red,blue,green,black"

SubType的一切实例都邑同享这一个colors属性。

原型链的第二个题目:在建立子范例的实例时,不能向超范例的组织函数中通报参数。现实上,应当说是没有方法在不影响一切对象实例的情况下,给超范例的组织函数通报参数。

3.2 借用组织函数

在子范例组织函数的内部挪用超范例组织函数

function SuperType(){
  this.colors = ["red", "blue", "green"];
}

function SubType(){
  //继承了SuperType
  SuperType.call(this);
}

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);    //"red,blue,green,black"

var instance2 = new SubType();
console.log(instance2.colors);    //"red,blue,green"

3.2.1 通报参数

function SuperType(name){
  this.name = name;
} 

function SubType(){
  //继承了SuperType,同时还通报了参数
  SuperType.call(this, "Nicholas");

  //实例属性
  this.age = 29;
}

var instance = new SubType();
console.log(instance.name);   //"Nicholas";
console.log(instance.age);    //29

为了确保SuperType组织函数不会重写子范例的属性,可以在挪用超范例组织函数后,再增加应当在子范例中定义的属性

3.3 组合继承

组合继承避免了原型链和借用组织函数的缺点,融会了它们的长处,成为JavaScript中最经常使用的继承形式。而且,instanceofisPrototypeOf()也能狗用于辨认基于组合继承建立的对象。

function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function (){
  console.log(this.name);
}

function SubType(name, age){
  //继承属性
  SuperType.call(this, name);

  this.age = age;
}

//继承要领
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function (){
  console.log(this.age);
}

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors);    //["red", "blue", "green", "black"]
instance1.sayName();    //Nicholas
instance1.sayAge();     //29

var instance2 = new SubType("Greg", 27);
console.log(instance2.colors);   //["red", "blue", "green"]
instance2.sayName();    //Greg
instance2.sayAge();     //27

3.4 原型式继承

原型式继承并没有运用严厉意义上的组织函数。他的主意是借助原型可以基于已有的对象建立新对象,同时还没必要因而建立自定义范例

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

var person = {
  name : "Nicholas",
  friends : ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends);    //["Shelby", "Court", "Van", "Rob", "Barbie"]

object()函数内部,先建立了一个暂时性的组织函数,然后将传入的对象作为这个组织函数的原型,末了返回了这个暂时范例的一个新实例。从本质上讲,object()队传入个中的对象实行了一次浅复制

ECMAScript 5经由历程新增Object.create()要领范例化了原型式继承:

var person = {
  name : "Nicholas",
  friends : ["Shelby", "Court", "Van"]
};

var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends);    //["Shelby", "Court", "Van", "Rob", "Barbie"]

在传入一个参数的情况下,Object.create()object()要领的行动雷同

var person = {
  name : "Nicholas",
  friends : ["Shelby", "Court", "Van"]
};

var anotherPerson = Object.create(person, {
    name : {
        value : "Greg"
    }
});

console.log(anotherPerson.name);    //"Greg"

Object.create()要领的第二个参数与Object.defineProperties()要领的第二个参数花样雷同:每一个属性都是经由历程本身的形貌符定义的。以这类体式格局指定的任何属性都邑掩盖原型对象上的同名属性

在没有必要调兵遣将地建立组织函数,而只想让一个对象与另一个对象坚持相似的情况下,原型试继承时完整可以胜任的。不过别忘了,包含援用范例值的属性一直都邑同享响应的值,就像运用原型形式一样。

3.5 寄生式继承

寄生式继承是与原型试继承严密相干的一种思绪。寄生式继承的思绪与寄生组织函数和工场形式相似,即建立一个仅用于封装继承历程的函数,该函数在内部以某种体式格局来加强对象,末了再像真的是它做了一切事情一样返回对象。一下代码范例了寄生式继承形式:

function createAnother(original){
  var clone = object(original);   //经由历程挪用函数建立一个新对象
  clone.sayHi = function(){       //以某种体式格局来加强这个对象
    console.log("hi");
  };
  return clone;     //返回这个对象
}

可以像下面如许来运用createAnother()函数:

var person = {
  name : "Nicholas",
  friends : ["Shelby", "Court", "Van"]
};

var anotherPerson = createAnother(person);
anotherPerson.sayHi();    //"hi"

新对象不仅具有person的一切属性和要领,而且另有本身的sayHi()要领

在主要斟酌对象而不是自定义范例和组织函数的情况下,寄生式继承也是一种有效的形式。前面树模继承形式时运用的object()函数不是必需的;任何可以返回新对象的函数都实用于此形式

3.6 寄生组合式继承

经由历程借用组织函数来继承属性,经由历程原型链的混成情势来继承要领
寄生组合式继承的基础形式以下:

function inheritPrototype(subType, superType){
  var prototype = object(superType.prototype);    //建立对象
  prototype.constructor = subType;    //加强对象
  subType.prototype = prototype;    //指定对象
}
function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function (){
  console.log(this.name);
};

function SubType(name, age){
  SuperType.call(this, name);
  this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function (){
  console.log(this.age);
};

这个例子的高效率体如今它只挪用了一次SuperType组织函数,而且因而避免了再SubType.prototype上面建立没必要要的、过剩的属性。与此同时,原型链还能坚持稳定;因而,还可以一般运用instanceofisPrototypeOf()

开发人员普遍认为寄生组合式继承时援用范例最理想的继承体式格局。

    原文作者:Cor
    原文地址: https://segmentfault.com/a/1190000009275204
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞