创建对象

(1)工厂模式
考虑到ES5中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节
function  createperson(name,age,job){
             var o = new  object();
             o.name = name;
             o.job = job;
             o.sayname = function (){
                    alert(this.name);
       };
            return o;
}
//var  person1 =  createperson("xiaoming" ,29 ,"student")
优点:创建实例化函数后,通过无数次调用函数来创建对象  解决了多个类似对象声明问题
缺点:识别问题,根本无法搞清楚他们到底是哪个对象的实例         alert(typeof box1);    //objet        alert(box2  instanceof objet);    //true
ps:  javascript中判断类型的typeof 与instanceof的区别
        JavaScript 中 typeof 和 instanceof 常用来判断一个变量是否为空,或者是什么类型的。但它们之间还是有区别的:
         typeof,是一个运算符,运算中需要一个操作数, 运算的结果就是这个操作数的类型,运算的结果是一个字符串。 他有一定的局限性,对于对象类型的值,只能得到一个”object”结果,却不能精确得到此值的精确类型。
示例:typeof 'hello' //得到"string"
       instanceof,也是一个运算符,运算中需要两个操作数, 运算的结果是true或false,表示此值是不是某一个类的示例,能得到一个值的具体类型。
示例:function User(){}  var u=newUser;  console.log( u instanceof User );//true
       constructor是对象的一个属性,不是运算符,constructor属性指向对象的构造函数。
示例:function User(){}  var u=newUser;  console.log( u.constructor===User);//得到true
(2)构造函数模式
        ECMAScript中可以采用 构造函数 (构造方法)可用来创建特定的对象。像object和array这样的原生构造函数,在运行时会自动出现在执行环境中。
 
function Box(name, age) {       //构造函数模式

this.name = name;

this.age = age;

this.run = function () {

return this.name + this.age + '运行中...';

};

}

var box1 = new Box('Lee', 100); //new Box()即可

var box2 = new Box('Jack', 200);

alert(box1.run());


alert(box1 instanceof Box); //很清晰的识别他从属于Box


使用了构造函数的方法,和使用工厂模式的方法他们不同之处如下:
1. 构造函数方法没有显示的创建对象(new Object());
2. 直接将属性和方法赋值给this对象;
3. 没有renturn语句。

构造函数的方法有一些规范:
1.函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和普通函数)
2.通过构造函数创建对象,必须使用new运算符。


构造函数和普通函数的唯一区别,就是他们调用的方式不同。只不过,构造函数也是函数,必须用new运算符来调用,否则就是普通函数。

var box = new Box('Lee', 100); //构造模式调用
alert(box.run());
Box('Lee', 20); //普通模式调用,无效

var o = new Object();
Box.call(o, 'Jack', 200) //对象冒充调用   对象冒充还有一种方法是apply
alert(o.run());
优点:通过构造函数的方法,即解决了重复实例化的问题,又解决了对象识别问题
缺点:使用构造函数的主要问题是每个方法都要在每个实例上重新创建一遍(

             alert(box1.run == box2.run); //false,因为比较的是实例化后的地址

           alert(box1.run() == box2.run()); //true,方法的值相等,因为传参一致
             box1和box2都有一个名为run()方法,但是两个方法不是同一个Funtion的实例)

(3)原型模型
什么是原型?
我们创建一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向函数的原型对象,也可以说是,prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
 使用原型的好处就是可以让所有对象实例共享它所包含的属性和方法。我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途就是包含可以由特定类型的所有实例共享的属性和方法。

function Box() {} //声明一个构造函数

Box.prototype.name = 'Lee'; //在原型里添加属性

Box.prototype.age = 100;

Box.prototype.run = function () { //在原型里添加方法

return this.name + this.age + '运行中...';

};

比较一下原型内的方法地址是否一致:

var box1 = new Box();

var box2 = new Box();


alert(box1.run == box2.run); //true,方法的引用地址保持一致(在构造函数中是false,引用的地址不一样没有实现原型共享)

只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype(原型)属性,prototype指向函数的原型对象
在原型模式声明中,会自动多了两个属性
_proto_属性:是实例指向原型对象的一个指针,也就是说指向prototype属性所在函数的指针(指向构造函数的原型属性constructor)

原型模式的执行流程:
1.   先查找构造函数实例里的属性或方法,如果有,立刻返回;
2.   如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;


isPrototypeOf()   判断一个对象是否指向了该构造函数的原型对象        alert(Box.prototype.isPrototypeOf(box)); //只要实例化对象,即都会指向

delete box1.name;            //删除实例中属性

hasOwnProperty()     判断一个属性是否在实例中           alert(box.hasOwnProperty('name')); //实例里有返回true,否则返回false

in操作符               判断属性是否在实例或者原型中               alert('name' in box); //true,存在实例中或原型中
优点:可以让所有对象实例共享它所包含的属性和方法(不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中)
缺点:不能传参(省略了构造函数里面的传参,里面初始化的值都一样)
           实例化两个对象,应该保持各自独立的地方,如果可引用类型指向的地址是可以修改的,一旦修改之后,修改的是原型,而原型又是共享的,所以第v     二个实例化的时候,由于共享,就会得到修改后的数据

(4)动态原型模型(构造函数+原型)  推荐使用

为了解决构造传参和共享问题,可以 组合构造函数+原型模式

function Box(name, age) { //不共享的使用构造函数
this.name = name;
this.age = age;
this. family = ['父亲', '母亲', '妹妹'];
};
Box.prototype = { //共享的使用原型模式
constructor : Box,
run : function () {
return this.name + this.age + this.family;
}
};
var box1 = new Box('lee',100);
alert(box1.family);    // 父亲,母亲,妹妹
box1.family.push('弟弟');
alert(box1.family);    // 父亲,母亲,妹妹,弟弟
var box2 = new Box('jack',200);
alert(box2.family);    // 父亲,母亲,妹妹      //引用类型没有使用原型,所以没有共享 

引用类型(数组,对象,函数)  基本值类型(数值,布尔值,null,undefined)
  PS:这种混合模式很好的解决了传参和引用共享的大难题。是创建对象比较好的方法。
升级版  动态原型模式
合二为一,将构造函数与原型封装在一起,形成动态原型模型

function Box(name ,age) { //将所有信息封装到函数体内
this.name = name;
this.age = age;
if (typeof this.run != 'function') { //仅在第一次调用的初始化
Box.prototype.run = function () {
return this.name + this.age + '运行中...';
};
}
}
var box 1= new Box('Lee', 100);
alert(box1.run());       
当第一次调用构造函数时,run()方法发现不存在,然后初始化原型。当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会再初始化了。这样及得到了封装,又实现了原型方法共享,并且属性都保持独立。
优点: 这种混合模式很好的解决了传参和引用共享的大难题。是创建对象比较好的方法。

(5)寄生构造函数

function Box(name, age) {

var obj = new Object();

obj.name = name;

obj.age = age;

obj.run = function () {

return this.name + this.age + '运行中...';

};

return obj;

}


寄生构造函数,其实就是工厂模式+构造函数模式。这种模式比较通用,但不能确定对象关系,所以,在可以使用之前所说的模式时,不建议使用此模式。

(6)稳妥构造函数

在一些安全的环境中,比如禁止使用thisnew,这里的this是构造函数里不使用this,这里的new是在外部实例化构造函数时不使用new。这种创建方式叫做稳妥构造函数。 

function Box(name , age) {

var obj = new Object();

obj.run = function () {

return name + age + '运行中...'; //直接打印参数即可

};

return obj;

}

var box = Box('Lee', 100); //直接调用函数

alert(box.run());


PS:稳妥构造函数和寄生类似。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值