JavaScript之面向对象(ECMAScript5)

  • 精晓对象属性
  • 创制对象
  • 继承

 

略知一二对象属性

ECMA-262称对象为:无序属性的聚众,其特性能够涵盖基本值,对象或者函数。

通过在ECMAScript中能够把目的想象成散列表,无非就是键值对,值可以为数量或者函数。

//两种定义对象的方式
var person = new Object();
person.name="xiaoming";
person.age=29;
person.showName = function(){
    alert(this.name); 
}

var person = {
      name:"xiaoming",
      age:29,
      showName:function(){
        alert(this.name);     
      }
}

属性类型

稍稍特性定义是为了兑现JavaScript引擎用的。为了表示特性时内部值,ECMA-262业内把她们置身两对方括号中,例如:[
[ Enumerable ] ]。

ECMAScript中有二种属性:数据属性和走访器属性。

数码属性  ** 
有以下描述行为的性状(多数场合下很少用到这一个高级效用)

  • [ [ Configurable ]
    ]:表示是否通过delete删除属性从而再一次定义属性,能否修改属性特性,或者是否把性能修改为访问器属性。默认值都为true
  • [ [ Enumerable ] ]:表示是否通过for-in循环再次来到属性。默认true
  • [ [ Writable ] ]:表示是否修改属性的值。默认为true
  • [ [ Value ] ]:包含属性的数据值,用作读写。

修改上述属性的默认特性应用Object.defineProperty()方法

var person={}
//接收三个参数:属性所在的对象,属性的名字和配置特性的对象
Object.defineProperty(person,"name",{
    writable:false,
    value:"xiaoming"
})    

做客器属性
 包含一对getter和setter函数。有如下4个特性:

  • [ [ Configurable ]
    ]:表示是否通过delete删除属性从而再次定义属性,能否修改属性特性,或者是否把性能修改为访问器属性。默认值都为true
  • [ [ Enumerable ] ]:表示是否通过for-in循环再次回到属性。默认true
  • [ [ Get ] ]:在读取属性时调用的函数。默认为undefined
  • [ [ Set ] ]:在写入属性时调用的函数。默认为undefined

图片 1图片 2

 1 var book = {
 2   _year:2017,
 3   edition:1
 4 }
 5 
 6 Object.defineProperty(book,"year",
 7 {
 8    get:function()
 9    {
10         return this._year;
11    }
12    set:function(newValue)
13    {
14         if(newVaule>2017)
15         {
16             this._year = newValue;
17             this.edition+=newVlaue-2017
18         }
19     }
20 })
21 
22 //定义多个属性
23 var book2 ={};
24 Object.defineProperties(book2,
25 {
26     _year:
27     {
28       writable:true,
29       value:2004
30     },
31     
32     edition:
33     {
34       writable:true,
35       value:1
36     },
37 
38      year:
39      {
40        get:function()
41        {
42          return this._year;
43        }
44        set:function(newValue)
45        {
46           if(newVaule>2017)
47           {
48             this._year = newValue;
49             this.edition+=newVlaue-2017;
50            }
51        }
52      }
53 })

View Code

 

若想要读取属性的某些特点,可以采取Object.getOwnPropertyDescriptor()方法,接收多少个参数:属性所在的目的和要读取其描述的属性名称。

 

创设对象

问题一:
前边通过Object构造函数或对象字面量的花样创制单个对象,不过这种艺术拔取同样接口创造很多目的,产生大量再度代码。

釜底抽薪措施:工厂格局,用函数来封装以特定接口创制对象的底细。

实际落实:函数createSchool()可以基于接收的参数来构建一个富含所有必要音信的School对象。

function createSchool(NO,name,address)
{
    var obj = new Object();

    obj.No = NO;
    obj.name = name;
    obje.address = address;

    return obj;
}

 

题目二:工厂格局解决了创建七个一般对象的问题,可是并未解决对象识别问题,不晓得对象是何许项目,如上边的createSchool方法,得不到创设的项目是School类型。

解决方法:构造函数模式。创设自定义的构造函数,从而自定义对象类型的属性和方法。

实际落实:与工厂形式区别在于没有显得的成立对象,直接将性能和艺术赋给了this对象,没有return语句。

function School(No,name,address)
{
    this.No = No;
    this.name = name;
    this.address = address;
    this.showName = function(){
        console.log(this.name);
    }
}

 要开改进实例要利用new操作符,会经历两个步骤

  1. 创办一个新目的
  2. 将构造函数的效率域赋给新对象,this就本着了那一个新目的
  3. 推行构造函数中代码,为目标添加属性
  4. 回来新对象

制造后的目的都会有一个constructor属性,该属性指向School。通过应用创设的实例对象school1.constructor
== School来检测对象类型。相比较保险的措施仍旧选择

school1 instanceof
School情势。此外构造函数其实也就是JS中简易的函数,当通过new操作符调用时,作为构造函数来利用,否则它也就是概括的函数。

 

题目三:构造函数即便缓解了工厂情势的短处,然而对于每个方法都亟待在每个实例上再一次创建几次。也就是说对于地方School中的showName方法,其实等价于this.showName
= new Function(“console.log(..)”);

也就是又实例化了一个目的。以这种艺术会促成不同效用域链和标识符解析,不同实例上的同名函数是不对等的,不可能实现共享。例如:school1.showName==school2.showName
回来的是false.

缓解情势:将函数定义放在构造函数的外表。

function School(No,name,address)
{
    this.No = No;
    this.name = name;
    this.address = address;
    this.showName = showName;
}

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

 

题目四:将艺术定义外面,确保了实例对象调用了千篇一律的轩然大波,不过在大局效率域中定义函数使得在自由地点均可调用,没有封装性可言。

釜底抽薪办法:原型形式。每个函数都有一个prototype(原型)属性,它是一个指南针,指向一个目的(原型对象),用途可以将想要实例间共享的特性和情势定义在那个目标上。

function School(){}

School.prototype.No = 1001;
School.prototype.name = "xxx";
School.prototype.showName = function(){
    console.log(this.name);
}

var school = new School();
school.showName();
var school2 = new School();
school2.showName();
console.log(school.showName==school2.showName);//true

知识点:

  • 原型对象:只要成立一个新函数,就会遵照一组特定的条条框框为该函数创立一个prototype属性,那一个特性指向函数的原型对象。默认境况下,所有原型对象都会活动获取一个constructor属性,这么些特性是一个针对性prototype属性所在函数的指针。

 

 图片 3

  • 每当代码读取某个属性时,遵照给定名字的特性,搜索首先会从目的的实例本身起首,如若找到则赶回该属性的值;尽管没有找到,继续查找指针指向的原型对象,在原型对象中追寻具有给定名字的习性,找到再次来到值,否则未定义,报错。
  • 更简便的原型语法:

    School.prototype = {

    //为了使自动获取的constructor指向School,
    //因为通过这种原型语法constructor等于Object,需要手动设置如下
    constructor:School,
    name:"",
    showName:function(){
        console.log(this.name);
    }
    

    }

  • 原生对象的原型。所有的原生引用类型(Object,Array,String等)都在其构造函数的原型上定义了措施。如在Array.prototype中得以找到sort()方法。我们也得以透过这种办法给某个原生类型添加方法和属性。例如:String.prototype.showText
    = function(){  alert(“Text”); }

 

问题四:通过原型形式中原型对象的法门,会促成所有实例在默认情况下都将得到一致的属性值,造成了劳苦。同时鉴于他的共享性本身,对于引用类型的值来说问题较大,因为引用类型保存的是一个指南针,共享导致某个实例改变改属性会同步到其他实例中。

缓解方法:将构造函数形式和原型形式组合使用:构造函数格局用于定义实例属性,而原型情势用于定义方法和需要共享的习性。
 使用最常见的一种模式。

function School(No,name)
{
    this.No = No;
    this.name = name;
    this.Classes=["一年级","二年级"];
}

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

 

继承

评释:ECMAScript补助促成持续,重要依赖原型链

原型链方法:利用原型让一个引用类型继承另一个引用类型的属性和办法。

function SuperType()
{
    this.supername="super";
}

function SubType()
{
    this.subname = "sub";
}

SubType.prototype = new SuperType();

var instance = new SubType();
alert(instance.subname);

浅析:在上述代码中,没有接纳SubType的默认提供的原型,而是替换为SuperType的实例。新原型不仅作为基类(SuperType)的实例而有所所有的特性和艺术,并且其内部还有一个指南针,指向了SuperType的原型。从而instance指向SubType的原型,SubType的原型又指向SuperType的原型。通过在此之前讲过的,原型搜索机制,沿着原型链继续发展,直到找到指定的法门或性能。

补充:

  • 是因为所有的引用类型默认都继承Object,所以具有函数的默认原型都是Object的实例,默认原型包含一个内部指针,指向Object.prototype,因而自定义的品类都会有toString(),valueOf()等方法。
  • 如何阐明实例兼容哪一类原型。可以拔取instanceof (instance instanceof
    Object)或 isPrototypeOf()方法(Object.prototype.isPrototypeOf())。
  • 给原型添加方法要放在替换原型的言语之后。即SubType.prototype.getSubValue=function(){};要放在SubType.prototype
    = new SubperType();之后
  • 毫无采取字面量添加新章程,因为会把原型链切断从而使得SubType和SuperType没有涉嫌。

 

原型链的题材:前边讲过,通过原型链的花样会使得引用类型值的原型属性被抱有实例共享,从而导致困难。其它创设子类的实例时,不可以向超类型的构造函数传递参数。看如下代码

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

function SubType()
{

}

SubType.prototype = new SuperType();

var instance = new SubType();
instance.colors.push("green");
console.log(instance.colors);//red,bule,greed

var instance2 = new SubType();
console.log(instance2.colors);//red,bule,greed

化解措施:借用构造函数,解决原型中富含引用类型值所带来的问题

贯彻思路:在子类型构造函数的内部调用超类型构造函数。使用apply()或call()方法,只要将上边的代码中SubType函数里投入SuperType.call(this);即可。同时也可以传递参数,如下

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

var instance = new SubType();
console.log(instance.name);//jack

 

借用构造函数方法的题材:仅仅使用这一形式无法使函数复用。而且在基类中定义的法门,对于子类型而言是不可见的。

釜底抽薪办法:组合继承。

实现思路:使用原型链实现对原型属性和艺术的接轨,通过借用构造函数来贯彻对实例属性的继续。

 

function SuperType(name)
{
    this.name = name;
    this.colors = ["red","blue"];
}
SuperType.prototype.showName = 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.showAge = function()
{
    console.log(this.age);
}

组合继承制止了原型链和借用构造函数的后天不足,融合了优点。

 

重组继承问题:其不论是在怎么着意况下,都会调用三回超类型构造函数:五遍是在开创子类型原型的时候(SubType.prototype
= new
SuperType())。另一遍是在子类型构造函数内部(SuperType.call(this,name))。这种场所会招致两组name和colors属性,实例上和SubType原型中。

缓解方法:寄生组合式继承

思路:通过借用构造函数来继续属性,通过原型链的混成形式来连续方法。我们所急需的就是超类型的副本。所以这种格局的主干代码结构如下

function SuperType(name)
{
    this.name = name;
    this.colors = ["red","blue"];
}
SuperType.prototype.showName = function()
{
    console.log(this.name);
}

function SubType(name,age)
{
    SuperType.call(this,name);
    this.age = age;
}
//将基类与子类联系在一起
inheritPrototype(subType,superType);
//然后给子类添加方法
SubType.prototype.showAge = function()
{
    console.log(this.age);
}


function inheritPrototype(subType,superType)
{
    var prototype = Object(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}

寄生组合式继承,只调用三回SuperType构造函数,防止在SubType.prototype上创造不必要,多余的特性,原型链还是能维系不变。

 

总括:ECMAScript匡助面向对象编程,使用如下情势创制对象:

  • 厂子形式,使用简单的函数创立对象,为目的添加属性和办法,然后回来对象。后来被构造函数格局代表
  • 构造函数格局,成立自定义引用类型,能够像对象实例一样采纳new操作符。缺点就是每个成员都无法得到复用,包括函数。由于函数与目的具备松弛耦合的表征,,因而尚未理由不在五个目的间共享函数。
  • 原型情势,使用构造函数的prototype属性指定那么些应该共享的属性和措施。组合使用构造函数格局和原型形式,从而采用构造函数定义实例属性,使用原型定义共享的习性和方法

JavaScript首要透过原型链实现持续。原型链的构建是通过将一个系列的实例赋值给另一个构造函数的原型实现的。那样,子类型就可以访问超类型所有属性和章程。原型链的题材是目的实例之间共享所有继续的习性和艺术,不得当单独采用。配和借用构造函数,即在子类型构造函数内部调用超类型构造函数。原型链继承共享的属性和方法,借用构造函数继承实例属性。此外还有寄生组合式继承,原型继承,寄生式继承等。

参照:《JavaScript高级程序设计》