JavaScript之创建对象

一. 工厂模式

/**
 * 工厂模式
*/
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");</span>

二. 构造函数模式


/**
 * 构造函数模式
*/
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");</span>
1.将构造函数当做函数
    构造函数与其他函数的唯一区别就是调用他们的方式不同,任何函数只要通过new来调用,那它就可以作为构造函数。通常情况下我们将构造函数首字母大写。
//当做构造函数调用
var person = new Person("Nicholas",29,"Software Engineer");
person.sayName();

//作为普通函数滴啊用
Person("Greg",27,"Doctor");//添加到window
window.sayName();

//在另一个对象的作用域中调用
var o = new Object();
Person.call(o,"Lucare",24,"Engineer");
o.sayName();</span>


2.构造函数的问题
    每个方法都要在每个实例上创新创建一遍,函数也是对象,因此没定义个函数,也就实例化了一个对象。从逻辑角度讲,此时的构造函数相当于这样:
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = new Function("alert(this.name)");//与声明函数在逻辑上是等价的。
}
//改进:
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName(){
    alert(this.name);
}</span>

    转移到外部可以解决上面的问题,共享一个sayName函数  但是在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。如果对象需要定义很多方法,那么就要定义很多个全局函数,于是这个自定义的引用类型就没有丝毫封装性可言了。好在我们可以通过原型模式来解决。

三. 原型模式

    我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。按字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处就是可以让所有对象实例共享它所包含的属性和方法。
/**
 * 原型模式
*/
function Person(){

}

Person.prototype.name = "Lucare";
Person.prototype.age = 24;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
}

var person1 = new Person();
person1.sayName();//Lucare

var person2 = new Person();
person2.sayName();//Lucare

alert(person1.sayName == person2.sayName);//true</span>

1.理解原型对象
    无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。创建了自定义的构造函数之后,其原型对象默认都会自动获得一个constructor属性,至于其他方法,则都是从object继承而来。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。
每当代码读取某个对象的某个属性时,都会执行一次搜索,首先从对象实例本身开始。如果实例中找到则返回该属性的值,没找到则搜索指针指向的原型对象。

2.原型与in操作符
   单独使用:alert("name" in person1);//true    in操作符会在通过对象能够访问给定属性时返回true  无论该属性存在于实例还是原型中使用hasOwnProperty()方法:可以检测一个属性是存在于实例中还是存在于原型中(object继承而来) 存在于对象实例中才会返回true.
so:确定该属性是存在于对象中还是存在于原型中
function hasPrototypeProperty(object,name){
    return !object.hasOwnProperty(name) && (name in object);
}

3.更简单的原型语法
function Person(){

}

Person.prototype = {
    name:"Lucare",
    age:24,
    job:"Engineer",
    sayName:function(){
        alert(this.name);
    }
};
将Person.prototype设置为等于一个对象字面量形式创建新对象。但是constructor属性不再指向Person了。

4.原型的动态性
    由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来--即使是先创建了实例后修改原型也照样如此。其原因归结于实例和原型之间松散的连接关系,这种连接仅仅是一个指针,而非一个副本。
var friend = new Person();

Person.prototype.sayHi = function(){
    alert("hi");
}

friend.sayHi();  //"hi" 没有问题
但是如果重写整个原型对象:
function Person(){

}

var friend = new Person();

Person.prototype = {
    constructor:Person,//此处需要注意
    name:"Lucare",
    age:24,
    job:"Engineer",
    sayName: function () {
        alert(this.name);
    }
}

friend.sayName();//error
5.原生对象的原型
    原型对象的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有原生引用类型(Object,Array,String)都在其构造函数的原型上定义了方法。
alert(typeof Array.prototype.sort);//"function"
alert(typeof String.prototype.substring);//"function"
example:给基本包装类型String添加一个startWith()的方法
String.prototype.startWith = function (){
    return this.indexOf(text) == 0;
}

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

    创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混合模式还支持向构造函数传递参数:集两种模式之长。
/**
 * 两种模式混用
*/
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Bob","Shell"];
}

Person.prototype = {
    constructor:Person,
    sayName:function(){
        alert(this.name);
    }
}</span>

五. 动态原型模式

/**
 * 动态原型模式
*/
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);
        }
    }
}</span>
在sayName方法不存在的情况下,才会将它添加到原型中。这段代码只会在除此调用构造函数时才会执行,此后,原型已经完成初始化。这里对原型所做的修改,能够立即在所有实例中得到反映。这种方法非常完美,其中if语句检查的可以是初始化之后应该存在的任何属性和方法--不必检查每个,只需其中一个即可。

六. 寄生构造函数模式

/**
 * 寄生构造函数模式
*/
function Person(){
    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("Lucare",24,"Engineer");
friend.sayName();</span>
这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们创建一个具有额外方法的特殊数组,由于不能直接修改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"

七. 稳妥构造模式

    稳妥对象:没有公共属性,而且方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境会禁止使用this和new),或者在防止数据被其他应用程序(如Mashup程序改动时使用)。与寄生构造函数相比有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数。
/**
 * 稳妥构造函数模式
*/
function Person(){
    //创建要返回的对象
    var o = new Object();

    //可以在这里定义私有变量和函数

    //添加方法
    o.sayName = function () {
        alert(this.name);
    }
    return o;
}
var friend = new Person("Lucare",24,"Engineer");
friend.sayName();</span>
--特此声明,此文为学习笔记,阅读书籍《JavaScript高级程序设计》所摘精华。
================================== 赵客缦胡缨,吴钩霜雪明。 银鞍照白马,飒沓如流星。 ==================================
原文地址:https://www.cnblogs.com/lucare/p/9312666.html