chenyinkai / blog

学习笔记,技术心得,疑难困惑,生活总结。喜欢的请star。。
42 stars 1 forks source link

JavaScript高级程序设计----面向对象(2) #12

Open chenyinkai opened 7 years ago

chenyinkai commented 7 years ago

创建对象

一、工厂模式

封装一个函数,函数内部创建对象,添加属性方法并返回对象。

function createPerson(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

二、构造函数模式

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

与工厂模式相比:1.没有显式地创建对象;2.直接将属性方法赋值给this;3.没有return语句 构造函数函数名以大写字母开头; 要实例化构造函数,需要使用new操作符; new操作符实例化过程:

  1. 创建一个新对象;
  2. 将构造函数的作用域赋值给这个新对象(this指向了新对象);
  3. 执行构造函数中的代码(为对象添加属性和方法);
  4. 返回新对象

构造函数的缺点: 每个方法在每个实例上都会重新创建;如以下代码所示: alert(person1.sayName == person2.sayName); //false

三、原型模式

我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象, 而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以 让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是 可以将这些信息直接添加到原型对象中。

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

使用 isPrototypeOf() 方法测试指示对象是否存在于另一个对象的原型链中 使用 Object.getPrototypeOf() 可以方便地取得一个对象的原型 使用 hasOwnProperty() 方法可以检测一个属性是存在于实例中,还是存在于原型中 如果在实例中添加和原型中同名的属性,会访问实例中的属性,但实例中的属性不会覆盖原型中的同名属性; 使用delete操作符可以完全删除实例中的属性,从而能够重新访问到原型中的属性;

in操作符

in 操作符会在通过对象能够访问给定属性时返回 true ,无论该属性存在于实例中还是原型中。 同时使用in操作符和hasOwnProperty()方法就可以确定该属性到底是存在于对象中,还是存在于 原型中,如下所示。

function hasPrototypeProperty(object, name){
    return !object.hasOwnProperty(name) && (name in object);
}

原型模式的缺点:1.省略了构造函数传递初始化参数,导致所有实例在默认情况下都取得相同的属性值。2.所有实例共享原型的属性,对引用类型(数组,对象)的属性来说,当一个实例改变属性值时,所有实例都将得到改变后的属性值。

function Person(){
}
Person.prototype = {
    constructor: Person,
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    friends : ["Shelby", "Court"],
    sayName : function () {
        alert(this.name);
    }
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true

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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}
Person.prototype = {
    constructor : Person,
    sayName : function(){
        alert(this.name);
    }
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

五、动态原型模式

动态原型模式把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

function Person(name, age, job){
    //属性
    this.name = name;
    this.age = age;
    this.job = job;
    // 方法
    if (typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

可以使用 instanceof 操作符确定它的类型。

六、寄生构造函数模式

通常,在前述的几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。 除了使用 new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。

function Person(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改 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");
alert(colors.toPipedString()); //"red|blue|green"

构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。由于存在上述问题,我们建议在可以使用其他模式的情况下,不要使用这种模式。

七、稳妥构造函数模式

稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用 this 和 new ),或者在防止数据被其他应用程序(如 Mashup程序)改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用 this ;二是不使用 new 操作符调用构造函数。

function Person(name, age, job){
    //创建要返回的对象
    var o = new Object();
    //可以在这里定义私有变量和函数
    //添加方法
    o.sayName = function(){
        alert(name);
    };
    //返回对象
    return o;
}
var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此 instanceof 操作符对这种对象也没有意义。