您的位置:首页 > Web前端 > JavaScript

JS 面向对象的程序设计-创建对象

2019-03-07 22:59 162 查看

一、工厂模式
例:用函数来封装以特定接口创建对象的细节

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("Mary",28,"Teacher");
var person2=createPerson("Sam",20,"Student");

说明: createPerson()可根据接受的参数来构建一个包含所有必要信息的Person对象,可无数次地调用这个方法,每次它都会返回一个包含三个属性一个方法的对象。

2.优点:解决了创建多个相似对象的问题

3.缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)

二、构造函数模式
1.可以创建特定类型的对象,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。

例:使用构造函数模式将以上例子重写

function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
};
}
var person1=new Person("Mary",28,"Teacher");
var person2=new Person("Sam",20,"Student");

说明:Person()取代了 createPerson(),Person()中的代码除了与 createPerson()中相同的部分外,还存在以下不同:

  • 没有显示地创建对象;
  • 没有return语句;
  • 直接将属性和方法赋给了 this对象

2.

  • 函数名Person使用的是大写字母P。构造函数始终都应以一个大写字母开头,而非构造函数则应以一个小写字母开头。
  • 要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上会经历以下步骤:
    (1)创建一个新对象;
    (2)将构造函数的作用域赋给新对象(this就指向这个新对象);
    (3)执行构造函数的代码(为这个新对象添加属性);
    (4)返回新对象

3.标识对象类型:constructor
person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person,如下所示:

alert(person1.constructor==Person);  //true
alert(person2.constructor==Person);  //true

4.检测对象类型:instanceof

alert(person1 instanceof Object);  //true
alert(person1 instanceof Person);  //true
alert(person2 instanceof Object);  //true
alert(person2 instanceof Person);  //true

  • person1和person2之所以同时是Object的实例,是因为所有对象均继承自Object
  • 使用instanceof关键字,判断一个对象是否是类的实例化对象;使用constructor属性,判断一个对象是否是类的构造函数

优点:创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型

5.问题
Q:怎样解决两个函数做同一件事的问题?
A:把函数定义转移到构造函数外部

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("Mary", 28, "Teacher");
var person2 = new Person("Sam", 20, "Student");

缺点:在全局作用域中定义的函数实际上只能被某个对象调用;若对象需要定义很多方法,就要定义很多个全局函数,因此自定义的引用类型就没有封装性可言

三、原型模式
1.可让所有对象实例共享它所包含的函数和方法

例:不必在构造函数中定义对象实例的信息,而可将这些信息直接添加到原型对象中

function Person(){
}
Person.prototype.name="Mary";
Person.prototype.age=28;
Person.prototype.job="Teacher";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
person1.sayName();  //Mary   通过对象实例访问保存在原型中的值

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

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

说明:新对象的这些属性和方法是由所有实例共享的;person1和person2访问的都是同一组属性和同一个sayName()函数。

2.理解原型对象
(1)当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定姓名的属性。搜索首先从对象实例本身开始。若在实例中找到具有给定名字的属性,则返回该属性的值;若没有找到,则搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。在原型对象中找到这个属性,则返回该属性的值。

(2)当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,即添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性
例:为对象实例添加一个属性

function Person(){
}
Person.prototype.name="Mary";
Person.prototype.age=28;
Person.prototype.job="Teacher";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
var person2=new Person();

person1.name="Grey";
alert(person1.name);  //Grey--来自实例
alert(person2.name);  //Mary--来自原型

(3)使用delete操作符可完全删除实例属性,从而让我们能够重新访问原型中的属性
例:使用delete操作符

function Person(){
}
Person.prototype.name="Mary";
Person.prototype.age=28;
Person.prototype.job="Teacher";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
var person2=new Person();

person1.name="Grey";
alert(person1.name);  //Grey--来自实例
alert(person2.name);  //Mary--来自原型
delete person1.name;
alert(person1.name);  //Mary--来自原型

(4)hasOwnProperty():检测一个属性是存在于实例中,还是存在于原型中,这个方法只在给定属性存在于对象实例中时,才会返回true

例:使用hasOwnProperty()执行直接对象查找

function Person(){
}
Person.prototype.name="Mary";
Person.prototype.age=28;
Person.prototype.job="Teacher";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
var person2=new Person();

alert(person1.hasOwnProperty("name"));  //false

person1.name="Grey";
alert(person1.name);  //Grey--来自实例
alert(person1.hasOwnProperty("name"));  //true

alert(person2.name);  //Mary--来自原型
alert(person2.hasOwnProperty("name"));  //false

delete person1.name;
alert(person1.name);  //Mary--来自原型
alert(person1.hasOwnProperty("name"));  //false

3.原型与in操作符
(1)单独使用

  • in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中
function Person(){
}
Person.prototype.name="Mary";
Person.prototype.age=28;
Person.prototype.job="Teacher";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
var person2=new Person();

person1.name="Grey";
alert(person1.name);  //Grey--来自实例
alert(person1.hasOwnProperty("name"));  //true
alert("name" in person1);  //true

alert(person2.name);  /
1e92b
/Mary--来自原型
alert(person2.hasOwnProperty("name"));  //false
alert("name" in person2);  //true
  • 同时使用hasOwnProperty()方法和in操作符,可确定该属性到底是存在于对象中,还是原型中
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
  • 上面定义的hasPrototypeProperty()的用法:
function Person(){
}
Person.prototype.name="Mary";
Person.prototype.age=28;
Person.prototype.job="Teacher";
Person.prototype.sayName=function(){
alert(this.name);
};
var person=new Person();
alert(hasPrototypeProperty(person,"name"));  //true

person.name="Grey";
alert(hasPrototypeProperty(person,"name"));  //false

(2)在for-in循环中使用

  • 返回的是所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性

例:使用Object.keys(),取得对象上所有可枚举的实例属性
Object.kets()这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组

function Person(){
}
Person.prototype.name="Mary";
Person.prototype.age=28;
Person.prototype.job="Teacher";
Person.prototype.sayName=function(){
alert(this.name);
};
var keys=Object.keys(Person.prototype);
alert(keys);  //name,age,job,sayName

var p1=new Person();
p1.name="Rob";
p1.age=13;
var p1keys=Object.keys(p1);
alert(p1keys);  //name,age
  • 若想得到所有实例属性,无论它是否可枚举,都可使用Object.getOwnPropertyNames()方法
var keys=Object.getOwnPropertyNames(Person.prototype);
alert(keys);  //constructor,name,age,job,sayName

:结果中包含了不可枚举的constructor属性
说明:Object.keys()和Object.getOwnPropertyNames()均可替代for-in循环

4.原型的动态性
实例中的指针仅指向原型,而不指向构造函数

例:重写整个原型对象

function Person(){
}
var friend=new Person();
Person.prototype={
constructor:Person,
name:"Mary",
age:18,
job:"Student",
sayName:function(){
alert(this.name);
}
};
friend.sayName();  //error

说明:重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;它们引用的仍然是最初的原型

四、组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性原型模式用于定义方法和共享的属性

function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["Vue","Tim"];
}
Person.prototype={
constructor:Person,
sayName:function(){
alert(this.name);
}
}
var person1 = new Person("Mary", 28, "Teacher");
var person2 = new Person("Sam", 20, "Student");

person1.friends.push("Court");
alert(person1.friends);  //Vue,Tim,Court
alert(person2.friends);  //Vue,Tim
alert(person1.friends===person2.friends);  //false
alert(person1.sayName===person2.sayName);  //true

说明:实例属性都是在构造函数中定义的,而由所有实例共享的属性constructor和方法sayName()则是在原型中定义的;
修改了person1.friends,并不会影响到person2.friends,因为他们分别引用了不同的数组

五、动态原型模式
可通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型

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("Mary", 28, "Teacher");
riend.sayName();

:使用动态原型模式时,不能使用对象字面量重写原型。若在已经创建了实例的情况下重写原型,则会切断现有实例与新原型之间的联系,见三、4.

六、寄生构造函数模式
基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象

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("Mary", 28, "Teacher");
friend.sayName();  //Mary

说明:构造函数在不返回值的情况下,默认会返回新对象实例;而通过在构造函数的末尾添加一个return语句,可以重写调用构造函数返回的值

七、稳妥构造函数模式
稳妥对象,指没有公共属性,而且其方法不引用this的对象
特点:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数

function Person(name,age,job){
var o=new Object();
//定义私有变量和函数
o.sayName=function(){
alert(name);
};
//返回对象
return o;
}
var friend=Person("Mary", 28, "Teacher");
friend.sayName();  //Mary

说明:变量friend中保存的是一个稳妥变量,除了调用sayName()方法外,没有别的方式可以访问其数据成员

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: