天天看点

Javascript之 面向对象的程序设计

对象:

创建对象的方式:

  • 实例化对象:
  • 对象字面量:

属性类型:

数据属性:

包含一个数据值的位置。在这个位置可以读取和写入值。

4个描述其行为的特性:

  • [ [ Configurable ] ]:表示能否通过delete删除属性,能否修改属性的特性,能否把属性修改为访问器属性。默认为true。
  • [ [ Enumerable ] ] :表示能否通过for-in循环返回属性。默认为true。
  • [ [ Writable ] ] :表示能否修改属性的值。默认为true。
  • [ [ Value ] ] :属性的数据值。默认为undefined。

修改属性默认的特性:

Object.defineProperty( Object , propertyName, obj );
/********************************************
 Object: 属性所在的对象,
 propertyName: 属性名,
 obj: 描述符对象  { Writable : false, ..... }
********************************************/
           
使用

Object.defineProperty()

创建新属性时,如果不指定,则

Configurable

Enumerable

Writable

默认为 false
访问器属性:

不包含数值。不能直接定义,智能通过

Object.defineProperty()

定义。

  • [ [ Configurable ] ]:表示能否通过delete删除属性,能否修改属性的特性,能否把属性修改为访问器属性。
  • [ [ Enumerable ] ] :表示能否通过for-in循环返回属性。默认为true。
  • [ [ Get ] ] :读取属性时调用的函数。默认为undefined。
  • [ [ Set ] ] :在写入属性时调用的函数。默认为undefined。

定义多个属性:

Object.defineProperties( Object , {
    propertyName_1: {
		writable: ... ,
          ...
    },
    propertyName_2: {
        writable: ... ,
          ...
    },
    ....
});
           

读取属性的特性:

var obj = Object.getOwnPropertyDescriptor( Object , propertyName );
//返回一个对象,如果是数据属性,则对象属性为数据属性的特性,访问器属性同理。
           

创建对象:

工厂模式:

用函数封装对象:

function createPerson (name, age, job) {
    var o  = new Object();
    o.name = name;
    o.age  = age;
    a.job  = job;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}
// 实例化一个对象
var person_1 = createPerson("Xuchao", 23, "Web Security Engineer");
           
缺点:没有解决对象识别问题

构造函数模式:

创造自定义的函数构造属性和方法:

function Person (name, age, job) {
    this.name = name;
    this.age  = age;
    this.job  = job;
    this.sayName = function() {
        alert(this.name);
    };
}
// 实例化一个对象
var person_1 = new Person("Xuchao", 23, "Web Security Engineer");
           
实例化对象后会产生一个

constructor

(构造函数)属性,该属性指向原型,既

person_1.constructor = Person

优点:解决工厂模式的缺点。

缺点:每个方法需要在每个实例上重新创建一遍。

原型模式:

让所有对象实例共享原型对象的属性和方法:

function Person () {
    Person.prototype.name = "Xuchao";
    Person.prototype.age  = 23;
    Person.prototype.job  = "Web Security Engineer";
    Person.prototype.sayName = function () {
        alert(this.name);
    };
}
//实例化一个对象
var person_1 = new Person();
person_1.sayName();   // "Xuchao"
alert(person_1.sayName() == person_2.sayName());  // "true"
           
理解原型对象:
1、

Obj.prototype.isPrototypeOf( obj_x );

判断obj_x中是否有Obj.prototype的指针

2、

Object.prototype.getPrototypeOf( obj_x );

返回obj_x中[ [ prototype ] ]的值

​ 为实例添加名字相同的属性,会将指针指向实例属性而屏蔽掉原型属性上的值,若想让指针重新指向原型属性,需用

delete

删除实例属性。

Obj.hasOwnPrototype( propertyName )

判断属性值是实例属性还是原型属性
原型与in操作符:

单独使用

in

操作符,可以判断指定属性是否可以通过对象访问到(与实力属性、原型属性无关,只要能访问该属性都返回

true

hasOwnProperty()

方法结合使用可以判断该属性是存在于实例对象中,还是存在于原型中。

Object.keys( Element )

Object.getOwnPropertyNames( Element )

更简单的原型语法:

通过对象字面量重写原型对象:

function Person(){
}
Person.prototype = {
    name: 'Xuchao',
    age: 23,
    job: 'Web',
    sayName: function(){
        alert(this.name);
    }
};
           
使用该方法重写原型,

constructor

将不再指向

Person

,而是指向

Object

,解决方法如下:
Person.prototype = {
    constructor: Person,   //解决constructor不指向Person的问题
      ...
};//此方法会将constructor变为可枚举对象,即[ [ Enumberable ] ]特性被设置为true
           
原型的动态性:

对原型对象所进行的任何修改都能够立即从实例上反映出来,因为指向原型的是一个指针而不是一个副本

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

​ 重写原型对象后,实例指向的依旧是原来的原型对象而非新的原型对象

原型对象的问题:
  1. 省略了为构造函数传递初始化参数的环节,结果会导致所有的实例具有相同的属性值。
  2. 原型中所有的属性都是被很多实例共享的,在一个实例中修改原型属性其他实例也同样会修改。

组合使用构造函数模式和原型模式:

构造函数模式用于定义实例属性,原始模式用于定义方法和共享的属性

> 每一个实例都会有一份实例属性的副本,但又同时共享着对方法的引用
           

寄生构造函数模式:

创建一个函数,封装创建对象的代码,再返回新创建的对象。

function Person (name, age, job) {   //一般情况下不建议使用
    var o  = new Object();
    o.name = name;
    o.age  = age;
    a.job  = job;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}
// 实例化一个对象
var person_1 = new Person("Xuchao", 23, "Web Security Engineer");
           
与工厂模式区别:使用

new

操作符,包装函数叫做构造函数

稳妥构造函数模式:

  1. 没有公共属性
  2. 其方法不引用

    this对象

与其他模式不同:

  1. 新建方法不引用

    this

  2. 不使用

    new

    操作符调用构造函数
适合在安全执行环境下使用

继承

利用原型链实现继承。

原型链

一个实例指向原型对象,而原型对象又是另一个原型的实例,层层叠进,构成原型链。

默认原型:

所有的引用类型都继承了

Object

,所有函数都是

Object

的实例。因此默认原型都会包含一个指针指向

Object.prototype

原型和实例的关系:

  1. instanceof

    操作符
  1. isPrototypeOf

    方法
原型链定义需要注意的问题:
  1. 给原型添加方法的代码一定要放在替换原型的语句以后
  2. 通过原型链事先继承时,不能使用对象字面量创建原型方法。因为这样会重写原型链。

借用构造函数(伪造对象、经典继承)

在子类型构造函数的内部调用超类型构造函数

function SuperType(){
    this.colors = ['red','green','blue'];
}
function SubType(){
    // 继承了SuperType
    SuperType.apply(this);
}
           
不会共享属性,而是会通过apply()方法产生一个副本
  1. 可以在子类型构造函数中向超类型构造函数传递参数
function SuperType( name ){
    this.name = name;
}
function SubType(){
    // 继承了SuperType,同时还传递了参数
    SuperType.apply(this, 'Xuchao');
}
           
  1. 方法函数无法复用,在超类型原型定义的方法,对子类型也不可见。

组合继承(伪经典继承)

将原型链和借用构造函数的技术结合到一块

function SuperType(name){
    // 利用构造函数模式创建原型属性
    this.name = name;
}
SuperType.prototype.sayName = function(){
    // 利用原型链的方式创建原型方法
    console.log(this.name);
}
           

原型式继承

借助原型中可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

function object(o){
    function F(){}     // 创建一个临时性的构造函数
    F.prototype = o;   // 将传入的对象作为这个构造函数的原型
    return new F();    // 返回了这个临时类型的一个新实例
} // 其本质是object对传入的对象进行了一次浅复制
           

ES5通过新增

Object.create()

方法规范了原型式继承:

包含引用类型值得属性始终都会共享相应的值,就像是用原型模式一样

寄生类继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象

function createAnother( func_1 ){
    var clone = object( func_1 );   // 通过调用函数创建一个新对象[可以用其他方式]
    clone.sayHi = function(){       // 以某种方式来增强这个对象
        alert("Hi");
    };
    return clone;   // 返回这个对象
}
           

寄生组合式继承

通过借用构造函数来继承属性,通过原型链的混合形式来继承方法。

function inheritPrototype( subType, superType ){
    var prototype = object(superType.prototype);   // 创建对象
    prototype.constructor = subType;               // 增强对象
    subType.prototype = prototype;                 // 指定对象
}// 只调用一次SuperType构造函数,避免了在SubType.prototype上创建不必要的、多余的属性,且原型链不变
           

继续阅读