JavaScript面向对象的程序设计

目次

导语

1.明白对象和面向对象的顺序设想

2.建立对象的体式格局

3.JavaScript的继续机制

3.1 原型对象
3.2 原型链
3.3 与原型对象相干的要领

4.小结

导语

前面的系列文章,基础把JavaScript的中心知识点的基础语法、规范库等章节解说完;
本章最先进入JavaScript中心知识点的高等部份——面向对象的顺序设想,这一部份的内容将会对对象这一数据范例做进一步的深化明白,而且报告几种建立对象的设想形式以及JavaScript奇特的继续机制;

1.明白对象和面向对象的顺序设想

1.1 面向对象的顺序设想

“面向对象编程”(Object Oriented Programming,缩写为OOP)本身是一种编程的头脑形式,它把天下的统统看作是对象的鸠合,天下的运转就是靠一个个对象分工、协作的结果,表现统统皆“对象”头脑;
而在顺序设想当中,面向对象编程就可以看作编写各个具有特定功用的对象(模块)并将它们举行有机的分工协作,即如今的模块化编程就是面向对象的顺序设想的实际运用;

1.2 明白对象

对象在前面的系列文章中曾提到,从数据特征上看,对象是无序属性(键值对)的鸠合;
我们可以运用字面量和组织函数的体式格局去建立一个最为简朴的对象:

var person = new Object();
person.name = "teren";
person.age = 18;
person.greet = function(){
     console.log("hello "+this.name);
}

var teren = {
  name:"teren",
  age:18,
  greet:function(){
      console.log("hello "+this.name);
  }
}

平常建立一个简朴的对象,都是采纳字面量的体式格局;
上面的对象就是对实际对象的一种笼统表达;

1.3 对象的属性范例

前面章节中我们运用delete敕令可以删除一些对象的属性,有一些又不可以,运用Object.keys()要领只能遍历可罗列属性,那末对象的属性是不是有一些特征是我们还没有相识的呢?
ES5供应了一种只需内部才用的特征(attribute)去形貌对象的属性(property)的种种特征,运用[[attribute]]示意,在JavaScript中不能直接接见它们;
一个我们异常熟习的栗子就是Number组织函数组织出来的实例对象;

《JavaScript面向对象的程序设计》

我们没法直接接见num.[[PrimitiveValue]],这一属性,只能经由历程num.valueOf()接见该值;

ES5中定义对象属性的两种特征,数据特征和接见器特征,对象属性可以兼备这两种特征;

数据特征定义对象的属性值的特征,一个属性值可以包括以下四个数据特征:

[[Value]]:寄存属性值;
[[Writable]]:是不是可写属性;
[[Enumerable]]:是不是为可罗列属性;
[[Configurable]]:是不是可用delete敕令删除;

接见器特征定义对象的属性在接见属性和设置属性时挪用的两个函数getter和setter;

[[Get]]:接见属性时挪用的函数;
[[Set]]:设置属性时挪用的函数;

下面以一个实例对象直接解说这两个特征:

//数据特征;
var teren = {};
Object.defineProperty(teren,{
    value:"teren",
    writable:false,
    enumerable:true,
    configurable:true
})

//接见器特征;
//html
<div id="name"></div>
//js
var obj = Object.defineProperty({},"name",{
set:function(name){
 document.getElementById('name').innerHTML=name
},
get:function(){
 console.log( document.getElementById('name').innerHTML
   ) 
 },
})
obj.name = "hello world"
obj.name

【demo】
Object.defineProperties可以一次性设置对象的多个属性;
《JavaScript面向对象的程序设计》

2. 建立对象的体式格局

上一节我们对面向对象的顺序设想头脑和对象有了开端明白,这一节我们深切探讨一下对象的建立体式格局及其优瑕玷;

建立对象的差别体式格局也可以简朴的称作设想形式,差别的设想形式在实际编程运用中起到差别的作用;

2.1 单例形式

单例形式就是发生一个类的唯一实例对象,它可以确保您只需一个对象实例可以实际派上用场;

单例形式下,建立对象体式格局以下:

var singleton = {
  attr:1,
  method:function(){
    return this.attr
  }
}
var ex1 = singleton;
var ex2 = singleton;
ex1 === ex2//true

上述建立单例的体式格局:

长处:运用异常简便;
瑕玷:缺少封装,成员暴露,初始化时占用资本;

可以运用闭包体式格局处理这一题目:

var substance = (function(){
  var unique; 
  function init(){
    var type;   
    return {
      setType:function(t){
        return type = t;
      }
    }
  }
  return {
    getInstance:function(){
      if(!unique){
        unique = init();
      }
      return unique;
    }  
  }
})();

var Adam = substance.getInstance();
var Eve = substance.getInstance();
Adam === Eve
Adam.setType('Man')//Man

《JavaScript面向对象的程序设计》

2.2 工场形式

单例形式只能创作单个实例对象,也就是说假如将该实例对象给予多个变量时,会存在对象的援用题目,即修正个中一个变量会影响到另一个变量;
偶然我们须要制造多个构造相似的对象,只需部份属性有所辨别,这时候工场形式派上用场;

工场形式的设想头脑就是可以像工场一样批量生产出相似属性和要领的对象,运用工场形式能处理多个相似的题目,比方制造多个弹窗(只是题目差别);

function person(name,age){
  var obj = new Object();
  obj.name = name;
  obj.age = age;
  obj.greet = function(){
      return "hello "+this.name;
  };
  return obj
}
var Adam = person("Adam",18);
var Eve = person("Eve",20);

上述工场形式:

长处:能批量生产构造相似的对象;封装建立对象的细节;

瑕玷:未能处理对象的范例,即由哪一个组织函数建立的;

2.3 组织函数形式

组织函数可以建立特定范例的对象,相似之前的Array、RegExp等原生对象都能制造特定范例的实例对象;

function Person(name,age){
  this.name = name;
  this.age = age;
  this.greet = function(){
      return "hello "+this.name;
  }
}
var p1 = new Person('Adam',18);
var p2 = new Person('Eve',20);

运用组织函数形式就可以处理实例对象由谁建立的题目;

《JavaScript面向对象的程序设计》

上述代码和工场形式的辨别在于:

1.没有显现建立新对象;

2.直接将属性和要领赋给this对象;
3.没有return语句;
4.函数名开首大写以辨别平常函数;
5.运用new操纵符去建立对象实例;

new操纵符的道理
运用new操纵符去挪用函数和直接挪用函数差别,其new操纵符的运转函数的历程为:

  1. 建立一个新对象;

  1. 将组织函数的作用域赋给新对象并实行组织函
    内的代码;

  2. 返回新对象;

运用代码示意以下:

function Person(name,age){
  this.name = name;
  this.age = age;
  this.greet = function(){
      return "hello "+this.name;
  }
}
function createPerson(name,age){
    var o = new Object();
    Person.call(o,name,age);
   return o;
}
var p1 = createPerson('Adam',18);
var p2 = createPerson('Eve',20);

运用组织函数形式建立对象的优瑕玷在于:

长处:可以辨认对象属于的组织函数;

瑕玷:假如存在差别实例对象同享的属性和要领,运用组织函数形式则会糟蹋内存;

【注】
关于this症结字的更多知识点可以拜见【what’s this】;
组织函数假如不必new操纵符挪用和平常函数是一样的;

2.4 原型形式

每一个函数都有一个prototype原型属性,这个原型属性可以布置特定范例的实例同享的属性和要领;

function Person(){}
Person.prototype.greet = function(){
  return "hello "+this.name;
}

将本来的greet函数布置在Person函数的prototype原型属性上,如许p1和p2可以同享该要领,而不像组织函数形式每建立一个实例就增添一个greet要领糟蹋内存;

【注】
关于原型对象的更多明白详见下一节——JavaScript的继续机制;

运用原型形式建立对象的优瑕玷在于:

长处:关于每一个实例的同享属性和要领可以较好完成;

瑕玷:零丁采纳原型形式将没法辨别差别实例的私有属性;

2.5 夹杂形式

夹杂形式,就是综合组织函数形式和原型形式的优瑕玷,组织函数形式布置实例的私有属性,原型形式布置实例的公有属性;

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.greet = function(){
    return "hello "+this.name;
}
var p1 = new Person("Adam",18);
var p2 = new Person("Eve",20);

夹杂形式是如今运用最普遍、认同度最高的一种建立自定义范例(类)的设想形式;

【注】
固然,设想形式不单单议上述所提到,另有越发精湛可以参考《设想形式》一书以及之前小羊写的一篇文章《设想形式梗概》;

3.JavaScript的继续机制

上一节我们经由历程建立对象的差别形式,隐式引出了原型对象的观点,这一节中我们将细致相识一下原型对象、原型链及其完成的继续机制;

前面,我们从数据特征上看,晓得对象是无序属性(键值对)的鸠合;
如今,我们可以从面向对象的角度看,任何对象都是更加笼统的对象的实例,可以明白为类的观点;
从这个角度明白,我们如今可以从新定义一下对象和类的寄义:
对象可以说是对实际事物的笼统,对象封装了属性和要领,属性值指的是对象的状况,要领指的是对象的行动;
是供应一种模板的‘对象’,它是对象的笼统
举个简朴的栗子:

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.greet = function(){
    return "hello "+this.name;
}
var p1 = new Person("Adam",18);
var p2 = new Person("Eve",20);

上述代码表明,p1和p2两个实例是对实际Adam和Eve的笼统,而“类”Person又是对2个实例的笼统,为建立相似构造的人供应规范的模板;
[注]ES6之前JavaScript中没有类,在ES6中定义了类;

3.1 原型对象

在上一节的原型形式中,我们提到每一个函数都有一个prototype属性,这个属性指向函数的原型对象,可以布置特定范例的实例同享的属性和要领;

更加深切明白prototype原型对象,prototype原型对象不仅可以布置特定范例的实例同享的属性和要领,而且照样完成JavaScript继续的症结;

只需建立一个新函数就会为该函数建立一个prototype属性,每一个prototype属性自动取得一个constructor属性,该属性指向prototype属性地点的函数指针;

当运用组织函数建立一个实例时,该实例内部包括一个内部属性__proto__指向组织函数的原型对象;

由此,一个简朴的继续便发生了;
以下面代码为例:

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.greet = function(){
    return "hello "+this.name;
}
var p1 = new Person("Adam",18);
var p2 = new Person("Eve",20);

《JavaScript面向对象的程序设计》

组织函数建立以后,自动建立一个prototype属性,prototype原型对象下有一个默许的constructor属性指向prototype属性地点的函数Person中;
在prototype原型对象上布置greet要领,实例p1的内部属性__proto__指向组织函数Person.prototype,由此继续了组织函数的原型对象上的greet要领;

【注重】

  • 实例的__proto__指向组织函数的prototype原型对象完成继续,这类联络存在于实例与组织函数的原型对象之间而不是组织函数之间;

  • 当js引擎剖析对象的属性时,先会搜刮对象本身的属性,假如没有则会去搜刮__proto__指向的原型对象上的属性,直到找到为止,假如在对象本身定义的属性和原型对象上的具有雷同属性名,则在读取该属性时,本身属性会屏障原型对象上的属性;

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.greet = function(){
    return "hello "+this.name;
}
var p1 = new Person("Adam",18);
p1.greet()//hello Adam;
p1.greet = function(){
    return "hello world"
}

《JavaScript面向对象的程序设计》

  • 修正组织函数的原型对象可以直接运用点操纵,结果是直接在本来的原型对象上增添属性,偶然须要增添的属性太多是,点操纵就显得太贫苦,可以采纳重置原型对象的要领:

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype = {
  constructor:Person,
  greet1:function(){},
  greet2:function(){},
  greet3:function(){}
};
var p1 = new Person("Adam",18);
Person.prototype.constructor.name//"Object"

须要注重的是,重置原型对象后,要从新为原型对象的constructor的属性指回Person组织函数;
假如不重置constructor的话,那末此时的Person.prototype是由字面量建立的对象,字面量建立的对象默许的组织函数是Object;

3.2 原型链

上面我们只定义一个组织函数,完成一次继续;假如存在多个组织函数,它们之间也存在继续关联,那末就会构成一条关于继续的原型链;

function SuperType(name,age){
   this.name = name;
   this.age = age
}
SuperType.prototype.greet = function(){
    return "hello "+this.name
}
function SubType(name,age,height){
    SuperType.call(this,name,age);
    this.height = height;
}
SubType.prototype = Object.create(SuperType.prototype);
SubType.prototype.constructor = SubType;
SubType.prototype.method = function(){return 1;}

var instance = new SubType('teren',18,180)

上面就是一个最为经常使用的完成多个类间继续的设想形式;
运用Object.create(SuperType.prototype)的优瑕玷在于:

长处:可以建立一个新的SuperType.prototype对象赋给SubType.prototype,修正SubType.prototype这个而不影响本来组织函数SuperType.prototype;

瑕玷:虽然具有子类的prototype和父类的prototype值是雷同的,但内存差别,从而割断子类和父类之间的范例;

《JavaScript面向对象的程序设计》

还可以运用SubType.prototype =new SuperType()完成雷同结果,其优瑕玷在于:

长处:可以表现子类和父类的继续关联;

瑕玷:子类具有父类的私有属性;

《JavaScript面向对象的程序设计》
所以,平常在实际完成原型链时运用Object.create()要领,而明白原型链时运用new SuperType()要领;

《JavaScript面向对象的程序设计》

3.3 与原型对象相干的要领

遍历对象属性要领
Object.keys()Object.getOwnPropertyNames()用于遍历对象本身而不是继续的属性名,返回一个数组,个中Object.keys()只返回可罗列属性;

《JavaScript面向对象的程序设计》

in用于搜检一个对象是不是具有某个属性。它不辨别该属性是对象本身的属性,照样继续的属性;
for...in用于遍历对象的一切可罗列属性(不管是本身的照样继续的)

《JavaScript面向对象的程序设计》
假如只遍历本身的属性,可以运用以下代码:

for (var key in instance){
  if(instance.hasOwnProperty(key)){
      console.log(key)
  }
}

推断属性是不是为本身的要领
Object.prototype.hasOwnProperty()返回一个布尔值,用于推断某个属性定义在对象本身,照样定义在原型链上;

《JavaScript面向对象的程序设计》

设置和猎取实例对象的原型对象的要领
Object.getPropertyOf()返回一个实例对象的原型对象;
Object.setPropertyOf(obj,prototype)可传两个参数,第1个为现有参数,第2个为原型对象;

《JavaScript面向对象的程序设计》

推断一个对象是不是为另一个对象的原型对象
Object.prototype.isPrototypeOf()用于推断一个对象是不是是另一个对象的原型;
《JavaScript面向对象的程序设计》

小结

通读本文,我们可以晓得:

  • 面向对象编程时一种头脑形式,它把天下的统统看作是对象的鸠合,天下的运作是一个个对象分工协作的结果;映射到顺序设想中,就是编写各个具有特定功用的对象(模块),并将它们有机整合使顺序得以运作;

  • 对象从数据特征角度看,是无序键值对的鸠合,对象的属性具有两种特征——数据特征和接见器特征;

  • 建立对象的差别体式格局可以称为设想形式,本文简朴解说了单例形式、工场形式、组织函数形式、原型形式、夹杂形式等;

  • 从面向对象角度看,对象可以说是对实际事物的笼统,类是对对象的笼统;

  • 每一个函数都有一个原型对象prototype,既可以布置特定范例实例的同享属性和要领,也是JavaScript完成继续的症结;

  • prototype原型对象有一个constructor属性,该属性指向prototype地点的函数指针;

  • 每当运用组织函数建立一个实例时,该实例内部包括一个内部属性__proto__指向组织函数的原型对象,由此完成简朴的继续;

  • 当A组织函数是B组织函数的实例时,由此就会构成一条原型链,即
    A组织函数的实例对象C的__proto__指向A组织函数的原型对象prototype,A组织函数prototype的__proto__指向B组织函数的原型对象prototype,B组织函数prototype的__proto__指向Function组织函数的prototype,Function的prototype的__proto__指向Object的prototype;

  • 与原型对象的相干要领包括:Object.keys()和Object.getPropertyNames()、for…in要领,Object.getPrototypeOf()和Object.setPrototypeOf()要领,Object.prototype.hasOwnProperty()和Object.prototype.isPrototypeOf()要领;

参考资料

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