一、创建单个对象实例
- 构造函数创建对象:var obj=new Object(); obj.属性名=属性值; 添加属性
- 对象字面量 obj{属性名:属性值,…}
- 问题:使用一个接口创建很多对象,产生大量重复代码
- 解决:工厂模式
二、工厂模式
- 用函数封装接口创建对象的细节,调用函数创建多个对象。
function createPerson(name, age, job) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.job = job;
obj.sayname = function() {
console.log(this.name);
}
return obj; //创建一个新对象,将对象返回
}
var obj1 = createPerson('张三', 20, '软件工程师');
var obj2 = createPerson('李四', 22, '软件工程师');
- 问题:不能做到函数复用
三、构造函数模式
- 构造函数首字母大写
- 使用new 创建对象,没有显式创建对象
- 构造函数的作用域赋值给新对象,this指向这个新对象,直接将属性和方法赋值给this
- 构造函数中,不写return语句 或 return 基本类型; 会将this作为返回值。 return对象; 会将对象作为返回值
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayname = function() {
console.log(this.name);
}
}
var obj3 = new Person('张三', 20, '软件工程师');
var obj4 = new Person('李四', 22, '软件工程师');
console.log(obj3.sayname==obj4.sayname);//false
使用new 操作符调用构造函数创建实例的过程
- 创建一个新对象
- 将构造函数作用域赋值给新对象,this指向新对象
- 执行构造函数中的代码,为新对象添加属性
- 返回新对象 return this;
构造函数作为普通函数 直接调用是给window添加属性和方法
构造函数模式的缺点
- 每个方法都要在每个实例上重新创建一遍。因为方法(函数)是对象,每定义一个方法,就是实例化了一个对象。
- 解决:原型模式
四、原型模式
- 每个函数都有原型属性prototype ,是一个指针,指向函数的原型对象
- 原型对象,可以让所有对象实例共享所有它所包含的属性和方法。
- 原型模式创建对象: 在原型上添加属性和方法 Person.prototype.属性名=‘属性值’
function Person() {//构造函数
}
// 在原型上添加属性和方法
Person.prototype.name = '张三';
Person.prototype.age = '21';
Person.prototype.job = '前端攻城狮'
Person.prototype.sayName = function() {
console.log(this.name);
}
//必须先重写原型再创建实例对象
var person1=new Person();
var person2=new Person();
console.log(person1.sayName==person2.sayName);//true 引用同一个方法
原型、构造函数 、实例的关系
-
实例对象的constructor属性指向构造函数。
person.constructor == Person
-
构造函数原型对象的constructor属性指向该构造函数。
Person.prototype.constructor==Person
-
构造函数的prototype属性指向构造函数的原型对象(简称原型)
-
构造函数与原型之间类似双向链表的关系
-
实例对象的__proto__ 属性直接指向构造函数的原型对象。兼容性:safari,firefox,chrome可以使用
person.__proto__==Person.prototype
-
构造函数和实例的关系: 实例 instanceof 原型链中的构造函数 返回true
-
原型和实例的关系:
- 原型.isPrototypeOf(实例)方法判断对象之间是否存在原型关系,存在 返回true
- Object.getPrototypeOf(实例); 返回对象的原型。支持IE9+
原型链:当代码读取某个对象的属性时,从实例本身构造函数开始查找,如果没有找到,查找__proto__ 指向的原型对象。
自身属性和原型属性
- 优先访问自身属性
- 使用null清空自身属性,再次访问该属性时不能访问到原型上的同名属性。
- 用delete删除自身属性,delete person.name; 会获取到原型属性。
区分自身属性和原型属性
- 实例.hasOwnProperty(‘属性’) 方法继承于Object
- 检测一个属性存在于实例中还是原型中,在实例中,返回true
- ‘属性’ in 实例; in操作符 可以通过对象访问到给定属性时(无论是实例中还是原型中)返回true。
原型模式 使用对象字面量添加原型属性 的问题
问题一:使用单独的Person.prototype 添加属性时,会保留原型中自带的constructor属性。
使用对象字面量添加属性时 会重写原型对象 ,失去默认的constructor属性。原型指向新的内存空间。
- 解决: 在原型里 手动添加 constructor属性 指向构造函数
问题二:先创建实例对象,再使用对象字面量添加原型属性时,不能使用原型中的属性和方法。
- 原型指向新的地址,构造函数可以自动指向新的地址,而实例仍然指向旧的原型地址.
- 解决:创建对象写在原型下面。
原型模式 自身的问题
- 使用实例对象可以修改 原型中引用类型的属性,而这个属性被所有实例共享
五、组合使用构造函数 和 原型模式 —— 创建自定义类型的常见方式
- 构造函数定义实例属性
- 原型上定义方法和共享的属性
//属性写在构造函数里面,每个对象都有自己的属性
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends=['lily','nacy'];//引用类型的属性值
}
Person.prototype={
constructor:Person,
//方法写在原型里,实例可以共享
sayName:function(){
console.log(this.name);
}
}
var person1=new Person('张三',29,'软件工程师');
var person2=new Person('李四',39,'医生');
person1.friends.push('tom');
//引用类型的属性值 不共享
console.log(person1.friends);//(3) ["lily", "nacy", "tom"]
console.log(person2.friends);//(2) ["lily", "nacy"]
console.log(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(){
console.log(this.name);
}
}
}
var obj1=new Person('lily',25,'后勤');
var obj2=new Person('nacy',25,'财务');
obj1.sayName();//lily
obj2.sayName();
console.log(obj1.sayName== obj2.sayName)//true 共享原型方法
七、寄生构造函数模式
- 类似于 工厂模式+构造函数模式
function Person(name, age, job){
var o=new Object();
o.name=name;
o.age = age;
o.job = job;
o.sayName=function(){
console.log(this.name);
}
return o;
}
var obj=new Person('lily',25,'后勤');
obj.sayName();//lily
八、稳妥构造函数模式
- 稳妥对象指没有公共属性,方法也不引用this的对象。适合在安全环境中使用
function Person(name, age, job){
var o=new Object();
// 可以定义私有变量和函数
var name='lily';
function otherFunc(){
conosle.log('ok');
}
o.sayName=function(){
console.log(name);
}
return o;
}
var obj=new Person3('lily',25,'后勤');
obj.sayName();//lily