JS面向对象编程之封装

我们所熟知的面向对象言语如 C++、Java 都有类的的观点,类是实例的范例模板,比方Student示意门生这类范例,而不示意任何详细的某个门生,而实例就是依据这个范例建立的一个详细的对象,比方zhangsanlisi,由类天生对象表现了笼统模板到详细化的历程,这叫做基于类的面向对象体式格局,而 JavaScript 没有类的观点,是基于原型的面向对象体式格局(虽然 Es6 增加了 class,本质是对原型体式格局的封装)。总结起来就是以下两点:

  • 在基于类的面向对象体式格局中,对象(object)依托类(class)来发生。
  • 在基于原型的面向对象体式格局中,对象(object)则是依托组织函数(constructor)和原型(prototype)组织出来的。

面向对象言语的第一个特征毫无疑问是封装,在 JS 中,封装的历程就是把一些属性和要领放到对象中“包裹”起来,那末我们要怎样去封装属性和要领,或者说怎样去建立对象呢(后文统一说建立对象)?下面用逐步推进的体式格局论述:

对象字面量 --> 工场情势 --> 组织函数 --> 原型情势 --> 组织函数+原型情势

对象字面量

JS中建立对象最原始的体式格局有两种:

  • 对象字面量
var  person = {
    name: "leon",
    age: "20",

    greeting: function() {
      alert('Hi!');
    }
}
  • Object实例增加属性要领
var person = new Object();
person.name = "leon";
person.age = "20";

person.greeting = function() {
  alert('Hi!');
};
  • 长处:代码简朴
  • 瑕玷: 建立多个对象会发生大批的代码,编写贫苦,且并没有实例与原型的观点。
  • 处理办法:工场情势。

工场情势

工场情势是编程范畴一种广为人知的设想情势,它笼统了建立详细对象的历程。JS 中建立一个函数,把建立新对象、增加对象属性、返回对象的历程放到这个函数中,用户只需挪用函数来天生对象而无需关注对象建立细节,这叫工场情势:

function createPerson(name, age) {
  var person = new Object();
  person.name = name;
  person.age = age;

  person.greeting = function() {
    alert('Hi!');
  };
  return person;
}

var person1 = createPerson("leon", "20");
  • 长处:工场情势处理了对象字面量建立对象代码反复题目,建立相似对象能够运用统一API。
  • 瑕玷:由于是挪用函建立对象,没法辨认对象的范例。
  • 处理办法:组织函数

组织函数

JS 中组织函数与其他函数的唯一区分,就在于挪用它的体式格局差别。任何函数,只需经由过程new 操作符来挪用,那它就能够作为组织函数。来看下面的例子:

function Person(name, age) {
  this.name = name;
  this.age = age;
  
  this.greeting = function() {
    alert('Hi!');
  };
  // return this;
}

var person1 = new Person("leon", "20");
var person2 = new Person("jack", "21");

经由过程组织函数new一个实例阅历了四步:

  1. 建立一个新对象;
  2. 将组织函数内的this绑定到新对象上;
  3. 为新对象增加属性和要领;
  4. 返回新对象(JS 引擎会默许增加 return this;)。

而经由过程组织函数建立的对象都有一个constructor属性,它是一个指向组织函数自身的指针,因而就能够检测对象的范例啦。:

alert(person1.constructor === Person) //true
alert(person1 instanceof Person) // true

然则依然存在题目:

alert(person1.greeting == person2.greeting) //false

统一个组织函数中定义了greeting(),而差别实例上的同名函数倒是不相等的,意味着这两个同名函数的内存空间不一致,也就是组织函数中的要领要在每一个实例上从新建立一次。这明显是不划算的。

  • 长处:处理了相似对象建立题目,且能够检测对象范例。
  • 瑕玷:组织函数要领要在每一个实例上新建一次。
  • 处理办法:原型情势。

原型情势

终究讲到了原型情势,JS 中每一个组织函数都有一个prototype属性,这个属性是一个指针,指向原型对象,而这个原型对象包含了这个组织函数一切实例同享的属性和要领。而实例对象中有一个proto属性,它指向原型对象,也就是组织函数.prototype == 原型对象 == 对象._proto_,那末对象就能够获取到原型对象中的属性和要领啦。同时,一切对象中都有一个constructor属性,原型对象的constructor指向其对应的组织函数。

运用原型,就意味着我们能够把愿望实例同享的属性和要领放到原型对象中去,而不是放在组织函数中,如许每一次经由过程组织函数new一个实例,原型对象中定义的要领都不会从新建立一次。来看下面的例子:

function Person() {
}

Person.prototype.name = "leon";
Person.prototype.age = "20";
Person.prototype.greeting = function() {
  alert('Hi!');
};

var person1 = new Person();
var person2 = new Person();
alert(person1.name); //"leon"
alert(person2.name); //"leon"
alert(person1.greeting == person2.greeting); //true
  • 长处:与纯真运用组织函数不一样,原型对象中的要领不会在实例中从新建立一次,勤俭内存。
  • 瑕玷:运用空组织函数,实例 person1 和 person2 的 name都一样了,我们明显不愿望一切实例属性要领都一样,它们照样要有本身独占的属性要领。而且假如原型中对象中有援用范例值,实例中获得的都是该值的援用,意味着一个实例修改了这个值,其他实例中的值都邑响应转变。
  • 处理办法:组织函数+原型情势组合运用。

别的 JS 中还定义了一些与原型相干的属性,这里排列一下:

  • Object.getPrototypeOf(),获得实例的原型对象。
Object.getPrototypeOf(person1);
  • isPrototypeOf(),推断是不是是一个实例的原型对象。
Person.prototype.isPrototypeOf(person1);
  • hasOwnProperty(),检测一个属性是不是存在于实例中
person1.hasOwnProperty("name");
  • in,推断一个属性是不是存在于实例和原型中。
"name" in person1;

组织函数+原型情势

末了一种体式格局就是组合运用组织函数和原型情势,组织函数用于定义实例属性,而同享属性和要领定义在原型对象中。如许每一个实例都有本身独占的属性,同时又有对同享要领的援用,节约内存。

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

Person.prototype = {
  constructor: Person,
  nationality: "China",
  greeting: function() {
    alert(this.name);
  }
}

var person1 = new Person("leon", "20");
var person2 = new Person("jack", "21");
alert(person1.greeting == person2.greeting) //true

上面代码顶用对象字面量的情势重写了原型对象,如许相当于建立了一个新的对象,那末它的constructor属性就会指向Object,这里为了让它继承指向组织函数,显现的写上了constructor: Person

这类组织函数与原型情势混成的情势,是现在在 JS 中运用最为普遍的一种建立对象的要领。

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