创建对象的方法

一、工厂模式

用函数来封装,通过传入参数以特定接口创建对象的细节。

function createPerson(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 p1 = createPerson('kimi',26,'web');
p1.sayName();   //kimi
var p2 = createPerson('liujin',25,'java');
p2.sayName();   //liujin复制代码

缺点:没有解决对象识别的问题。

二、构造函数模式

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        console.log(this.name);
    }
}
var p1 = new Person('kimi','26','Web');
p1.sayName();   //kimi
var p2 = new Person('liujin','25','java');
p2.sayName();   //liujin复制代码

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

1、没有显示的创建对象。

2、直接将属性和方法赋值给this。

3、没有return语句。

4、调用时使用new关键字,首字母要大写。

构造函数存在的问题:构造函数中的属性和方法是实例特有的,不同的实例上的同名函数是不相等的。

解决同名函数不共享的问题:将构造函数中的函数定义为全局的函数。

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}
function sayName(){
    console.log(this.name);
}
var p1 = new Person('kimi','26','Web');
p1.sayName();    //kimi
var p2 = new Person('liujin','25','Java');
p2.sayName();    //liujin复制代码

然而新的问题来了:定义很多全局函数之后,破坏了对象的封装性。

解决办法:原型模式

三、原型模式

function Person(){}
Person.prototype.name = 'kimi';
Person.prototype.age = '26';
Person.prototype.job = 'Web';
Person.prototype.sayName = function(){
    console.log(this.name);
};
var p1 = new Person();
p1.sayName();    //kimi
var p2 = new Person();
p2.sayName();   //kimi复制代码

1、对原型的理解:

每个函数都有一个prototype属性,这个属性是一个指针,执行一个原型对象,而这个对象包含所有实例所共享的属性和方法。

每个原型都有一个constructor指针,这个指针指向Prototype属性所在的函数。

实例中存在一个__proto__属性,它指向构造函数的原型对象。


2、获取某个对象的属性的过程

首先在对象实例中搜索有没有该方法,如果有则返回该实例的值,如果没有继续搜索原型对象中是否包含该属性。

3、在对象实例中添加一个属性,该实例属性与原型中的属性同名时,该实例属性会覆盖原型属性,但并不会重写原型中的属性。

例子:

function Person(){}
Person.prototype.name = 'kimi';
Person.prototype.age = '26';
Person.prototype.job = 'Web';
Person.prototype.sayName = function(){
    console.log(this.name);
};
var p1 = new Person();
var p2 = new Person();
p1.name = 'zhangsan';
console.log(p1.name);    //zhangsan
console.log(p2.name);    //kimi复制代码

4、删除实例属性

delete 实例.property (注意将p1.name = null不管用)

function Person(){}
Person.prototype.name = 'kimi';
Person.prototype.age = '26';
Person.prototype.job = 'Web';
Person.prototype.sayName = function(){
    console.log(this.name);
};
var p1 = new Person();
var p2 = new Person();
p1.name = 'zhangsan';
p1.name = null;
delete p1.name;
console.log(p1.name);    //kimi
console.log(p2.name);    //kimi复制代码

5、更简单的原型写法

function Person(){
}
Person.prototype = {
    name:'kimi',
    age:26,
    job:'Web',
    sayName:function(){
        console.log(this.name);
    }
};
var p1 = new Person();
console.log(p1.constructor);   //[Function: Object]
console.log(p1.name);         //kimi复制代码

可以看到它的构造函数发生了变化。本质上是重写了prototype对象,因此constructor属性也变成了新对象的constructor属性(指向Object构造函数)。

function Person(){
}
Person.prototype = {
    constructor:Person,    //可以特意将其设为适当的值。
    name:'kimi',
    age:26,
    job:'Web',
    sayName:function(){
        console.log(this.name);
    }
};
var p1 = new Person();
console.log(p1.constructor);   //[Function: Person]
console.log(p1.name);         //kimi复制代码

6、原型的动态性

原型的动态性就是在给原型添加属性或者方法前,我们可以先创建实例。原因:实例与原型之间的连接是一个指针。

function Person(){}
var p1 = new Person();
Person.prototype.sayHi = function(){
    console.log('hi');
};
p1.sayHi();   //hi复制代码

但是在重写原型前创建对象,却会报错。

function Person(){
}
var p1 = new Person();
Person.prototype = {
    constructor:Person,
    name:'kimi',
    age:26,
    job:'Web',
    sayName:function(){
        console.log(this.name);
    }
};
p1.sayName();   //p1.sayName is not a function复制代码

原因:重写原型时,切断了实例与新的原型对象之间的联系。


7、原型对象的问题:对于包含引用类型的属性,一个实例对其进行修改,也会反映在其他实例上。

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

思想:使用构造函数定义实例属性,原型定义方法和共享属性。

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ['Shelby','Court'];
}
Person.prototype = {
    constructor:Person,
    sayName:function(){
        console.log(this.name);
    }
};
var p1 = new Person('Kimi',26,'Web');
var p2 = new Person('liujin',25,'Java');
p1.friends.push('Van');
console.log(p1.friends);   //[ 'Shelby', 'Court', 'Van' ]
console.log(p2.friends);   //[ 'Shelby', 'Court' ]复制代码

5、动态原型模式

把所有信息都封装在构造函数中,通过判断某一方法是否存在来决定是否需要初始化原型。

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ['Shelby','Court'];
    if(typeof this.sayName !== 'function'){
        Person.prototype.sayName = function(){   //只会初始化一次
            console.log(this.name);
        }
    }
}
var p1 = new Person('Kimi',26,'Web');
p1.sayName();   //kimi复制代码

6、寄生构造函数模式

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 p1 = new Person('kimi',26,'Web');
p1.sayName();复制代码

寄生构造函数模式返回的对象和构造函数,原型之间没有关系,所以不能依赖instanceof来确定对象的类型。不建议使用这种模式。

7、稳妥构造函数

function Person(name,age,job){
    var o = new Object();
    //定义私有变量和函数
    o.sayName = function (){
        console.log(name);          //不引用this
    };
    return o;
}
var p1 = Person('kimi',26,'Web');   //不使用new关键字
console.log(p1.name);               //undefined
p1.sayName();                       //只能通过sayName方法去获取name的值复制代码


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值