面向对象的程序设计——创建对象

1、普通对象声明

创建自定义对象的最简单方式就是创建一个Object的实例,然后在为他们添加属性和方法,如下所示:

var person = new Object();      //创建对象
person.name = "Nicholas";       //添加属性
person.age = 29;
person.job = "teacher";
person.sayName = function(){    //添加方法
    alert(this.name);
};

this的含义:

  1. this表示当前作用域下的对象;
  2. this表示new Objecy()实例化出来的那个对象;
  3. this要放在一个作用域下,比如person.sayName()person下的方法,方法可用this表示方法本身。

缺点:要创建一个类似的对象会产生大量的代码。

为了解决多个类似声明的问题,用一种工厂模式,这种方法是为了解决实例化对象产生大量重复的代码。

2、工厂模式

用函数来封装以特定接口创建对象的细节。

function createPerson(name,age,job){    //创建对象
    var obj = new Object();             //添加属性
    obj.name = name;
    obj.age = age;
    obj.job = job;
    obj.sayName = function(){           //添加方法
        alert(this.name);
    };
    return obj;                         //返回对象引用
}

var person1 = createPerson("Zhangsan",29,"Teacher");    //实例化第一个对象
var person2 = createPerson("Lisi",34,"Doctor");     //实例化第二个对象
alert(person2 instanceof Object)        //true

this的含义:
1.thisnew Object,实例化出来的那个对象;
2.this要放在一个作用域下,比如obj.sayName(){},这是o作用域下的的方法,可以用this来表示obj本身。

缺点:虽然集中实例化函数,解决了大量重复的代码,但不管怎样他们都是Object类型,就无法区分谁是谁的对象。

3、构造函数模式

构造函数可用来创建特定类型的对象,类似Object类型。

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}

function Person2(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}

var person1 = new Person("Zhangsan",29,"Teacher");
var person2 = new Person("Lisi",34,"Doctor");   
var person3 = new Person2("Wangwu",34,"Police");

alert(person1 instanceof Person);       //true,person1从属于Person
alert(person2 instanceof Person);       //true,person2从属于Person
alert(person3 instanceof Person2);      //true,person3从属于Person2
alert(person1 instanceof Person2);      //false,因为这里person1是从属于Person

alert(person1.sayName() == person2.sayName());  //true,构造函数的方法的值是想等的
alert(person1.sayName == person2.sayName);  //false,比较的是引用地址

构造函数和工厂模式的区别:

  1. 构造函数没有new Object但它后台会自动生成var obj = new Object();
  2. this相当于obj
  3. 构造函数没有return语句,他是后台自动返回的。

构造函数规则:

  1. 构造函数也是函数,但函数名第一个字母大写;
  2. 必须new 构造函数名(),如new Person(),而Person第一个字母也是大写的。

构造函数解决识别问题:
要在创建出来一个构造函数,并实例化之后才能比较出来。

构造函数与普通函数的区别:
调用方式不同,构造函数使用new调用,普通函数则无需。

构造函数虽然可以当做普通函数调用,但因为构造函数是要被封装起来的,内部是不允许修改的,它内部是没有return语句的,所以构造函数,用普通函数调用是无效的,必须使用new运算符。

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

4、把方法移动到构造函数外部

这做法没什么必要,只是加深学习了解:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName(){         //把构造函数内部的方法通过全局来实现,引用地址一致
    alert(this.name);
}

var person1 = new Person("Zhangsan",29,"Teacher");
var person2 = new Person("Lisi",34,"Doctor");   

alert(this.name)this含义
这个this当作对象调用的话,this代表Person,当普通函数调用this代表window

函数(方法)如果没有圆括号,输出函数代码,有括号才会输出函数值。

缺点:在全局作用域中定义的函数只能被某个对象调用,让全局作用域有点名不副实。

5、原型模式

使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

  1. 如果是实例方法,不同的实例化,不同的实例化,它们的方法和地址是不一样的,是唯一的;
  2. 如果是原型方法,那它们的地址是共享的,大家都一样。
function Person(){}     //构造函数什么体内什么都没有,如果有叫做实例方法,实力属性

Person.prototype.name = "Zhangsan";
Person.prototype.age = 29;
Person.prototype.job = "Teacher";
Person.prototype.sayName = function(){
    alert(this.name);
};

var person1 = new Person();
person.sayName = "Zhangsan";

var person2 = new Person();
person.sayName = "zhangsan";

alert(person1.sayName == person2.sayName);  //true

缺点:重复敲Person.prototype,造成大量的重复输入。

6、字面量方式创建原型

function Person(){}//使用字面量的方式创建原型对象,这里的`{}`就是对象,是`Object`,`new Object`相当于`{}`

Person.prototype = {
    name: "Zhangsan",
    age: 29,
    job: "Teacher",
    sayName: function(){
        alert(this.name);
    }
};

var person = new Person();

注意:实例化后重写原型对象,会切断现有实例和新原型之间的联系
缺点:constructor不在指向实例,而会指向Object。新对象的constructor重写Person原来的constructor,因此会指向新对象。
解决方法:在原型内部,可以设置constructor强行执行实例。

7、组合构造函数模式和原型模式

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

function Person(name,age,job){      //保持独立的使用构造函数
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Xiaoming","Fangfang"];
}

Person.prototype = {        //保存共享的使用原型
    constructor: Person,
    sayName: function(){
        alert(this.name);
    }
}

var person1 = new Person("Zhangsan",29,"Teacher");
var person2 = new Person("Wangwu",34,"Doctor");

person1.friends.push("Xiaohong");
alert(person1.friends); //"Xiaoming,Fangfang,Xiaohong"
alert(person2.friends); //"Xiaoming,Fangfang",引用类型没有使用原型,所以没有共享
alert(person1.friends == person2.friends);  //false
alert(person1.sayName == person2.sayName);  //true

8、动态原型模式

动态原型模式,把所有信息都封装在了构造函数中。

function Person(name,age,job){      //保持独立的使用构造函数
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Xiaoming","Fangfang"];
    
    if(typeof this.sayName != "function"){  //仅在第一次时初始化
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
}

原型的初始化,只要第1次初始化,就可以了,没必要每次构造函数实例化的时候都初始化,可以将原型封装在函数里。
注意:使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

你可能感兴趣的:(面向对象的程序设计——创建对象)