了解完了对象,也了解了常用的创建对象的方式--构造函数式和字面量式,那么除此自外,还有哪些创建对象的方式呢?
工厂模式
说白了就是一个封装函数,在函数内部定义对象的细节,最后返回之
function createObj(name,job,age){
var obj = new Object();
obj.name = name;
obj.job = job;
obj.age = age;
obj.logAge = function(){
console.log(this.age)//该函数执行的时候是在对象中执行的所以,this指向对象本身
}
return obj
}
console.log(createObj("张三","小白",23))//是一个对象;
var person = createObj("李四","会计",23);//person保存了一个指针,指向新创建的对象;
var person1 = person;//共同指向同一个对象
console.log(person.logAge);//23
console.log(person1);//还是一个对象
构造函数模式
a).由构造函数创建出来的是一个对象,
b).构造函数内部的this指向的是new 出来的实例对象;
c).实例.constructor是一个指针,指向实例的创建者--构造函数,默认调用的是prototype对象的constructors属性;
function CreateObj(name, job, age){
// var hobby = "作诗";
this.name = name;
this.job = job;
this.age = age ;
this.logAge = function(){
console.log(this.age)
}
}
var person_new = new CreateObj("王五","财务",27);//person_new是一个对象;
var person_new1 = new CreateObj("吴六", "饭店老板", 46);//person_new是一个对象;
person_new.logAge();//27
person_new1.logAge()//46;
console.log(person_new.constructor)//构造函数本身;
console.log(person_new1 instanceof Object)//因为毕竟person_new1也是一个对象嘛!
//构造函数当作普通函数调用;
CreateObj("李白","诗人",48);//this此时指向的是window,因为调用的时候是在全局环境中调用的;
console.log(name)//李白
构造函数.prototype(原型模式):
每一个函数都有该属性,是一个指针,指向一个对象(原型对象),该对象包含由该构造函数创建的所有实例对象所共有的属性和方法;与此同时,该原型对象也包含着一个constructor属性(是一个指针,指向拥有该原型对象的构造函数)
1.实例.__proto__:在通过构造函数创建实例后,该实例有一个属性__proto__,是一个指针,指向该构造函数的原型对象。也就相当于,实例.__proto__ = 构造函数.prototype;
function Plant(height,classify){
this.height = height;
this.class = classify;
}
Plant.prototype.name = "水稻";//将来所有的实例都将拥有属性name,值为“水稻”
console.warn(Plant.prototype.constructor)//构造函数本身;
Plant.prototype.constructor.prototype.loca = "河南·许昌"//
console.warn(Plant.prototype.constructor)//构造函数本身;
var plant_xiaomai = new Plant(137,"旱季作物");//构造函数的实例
console.log(plant_xiaomai);//一个对象
console.log(plant_xiaomai.name)//水稻
console.log(plant_xiaomai.loca);//河南.许昌
console.log(plant_xiaomai.__proto__)//原型对象
2.访问构造函数实例的属性的搜索顺序:
当为实例对象添加一个属性的时候,如果该属性名和原型对象中的属性名一样,那么该属性就会屏蔽原型对象中的对应属性,结果是会阻止我们 访问原型中的对应属性(但不会修改),因为在访问实例中的某属性的时候,首先会访问实例中的属性,如果有就停止搜寻,如果没有就往构造函数的的原型对象中寻找。
2.1实例.hasOwnProperty(prop):就是用来检测属性prop是存在于实例中还是对应构造函数的原型对象中,当存在于实例中时返回true,毕竟顾名思义--就是看看实例中有没有属性prop嘛,
2.2 原型模式也是有缺点的,其最大的缺点就是实例对象的属性高度共享,因为prototype中的属性是所有构造函数实例所共有的。 因此当修改原型对象中的属性的时候,其它实例访问该属性的时候也会发生改变
// 访问实例中的属性的时候,其访问机制是,首先会在实例中的属性中寻找,如果找到了就停止寻找,如果找不到就在该实例的
//构造函数的原型中寻找....
function Xmw (name,sex,age){
this.name = name;
this.age = age;
this.sex = sex;
}
Xmw.prototype.school = "陈杨寨杨村庙小学";
var dmw = new Xmw ("董曼文","女",7);//一个对象
console.log(dmw.school);//陈杨寨杨村庙小学
console.log(dmw.__proto__);//指向构造函数的原型对象
console.log(dmw.__proto__ == Xmw.prototype);//true
var dhl = new Xmw("董涵琳","女",7);
dhl.school = "吴刘小学"
console.log(dhl.school)//吴刘小学;
console.log(dhl.hasOwnProperty("school"));//true
delete dhl.school;//删除实例属性
console.log(dhl.school)//杨村庙小学,没有了同名的实例属性,就会寻找该实例的对应构造函数的原型中的属性
构造函数和原型的正确打开方式
1.构造函数+prototype混用(推荐):创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数(可传参)模式用于定义实
例属性,而原型模式用于定义方法和共享的属性。
function Animal(name,age,weight){
this.name = name;
this.age = age;
this.weight = weight;
this.height = 1.03;
}
Animal.prototype = {
constructor:Animal,//显式指定constructor指针的指向
sayAniName:function(){
console.log(this.name)
}
}
var cat = new Animal("傲娇Cat",1,5);//一个对象
console.log(cat);
cat.sayAniName()//傲娇Cat
cat.height =1.30
console.log(cat.height)//1.30
var dog = new Animal("憨厚Dog",2,20);//另一个对象
console.log(dog.height)//1.03,因为dog实例对象中的height属性是1.03
2. 动态原型模式(不推荐)
function Animal_1(name, age, weight){
this.name = name;
this.age = age;
this.weight = weight;
this.height = 1.03;
if(typeof this.sayAniName != "function"){//当实例中没有sayAniName属性的时候,
//这段代码只会在初次调用构造函数时才会执行。此后,原型已经完成初始化
Animal_1.prototype.sayAniName = function(){
console.log(this.name)
}
}
}
var cat1 = new Animal_1("粘人猫",2,9);//新创建的实例,此时就会执行if语句,因为此时满足跳捡,在此之后,当再次调用该构造函数的时候不执行if语句了
//,因为此时实例已经有了公用的方法了
cat1.sayAniName()//粘人猫
var dog1 = new Animal_1("淘气狗",2,10);
dog1.sayAniName()//淘气狗
3.寄生构造函数模式(不推荐):说白就是一个构造函数,在函数体内新建一个对象,然后定义对象的一些细节,最后返回对象 然后调用的时候使用 new 操作符
function Plant(classify,iswatered){
var plant = new Object();
plant.classify = classify;
plant.iswatered = iswatered;
return plant
}
var apple = new Plant("灌木类",false);//既可以被当作构造函数使用
var orange = Plant("草",true);//也可也被当作普通的函数使用
console.log(apple);
console.log(orange);
4.稳妥构造函数模式(一般用在安全性要求高的环境中):
和寄生构造函数模式差不多,
只不过在该模式下,新创建对象的实例方法不引用this(也就是说,在构造函数中定义对象实例细节的时不能使用this),其二不使用new操作符调用构造函数(要像调用普通函数那样调用)
function Person(name, age, weight) {
var person = new Object();
person.alertName = function () {
alert(name)
}
return person
}
//新对象
Person("张三", 23, 170).alertName();//张三
注意:
函数对象有 __proto__ 属性,又有 prototype 属性。普通对象只有 __proto__ 属性,没有 prototype 属性。
console.log(obj.__proto__)//Object对象
console.log(obj.prototype)//undefined
function abs(){
console.log(1)
}
console.log(abs.__proto__)//function对象的原型
console.log(abs.prototype)//将abs其视为一个对象,对象的原型是Object
Function.prototype 比较特殊,是个空函数,因为Function是一个构造函数,构造函数.prototype指向该构造函数的原型对象
typeof Function.prototype 输出 “function” 但是它不能当成构造器,Function.prototype.prototype 为 undefined
Object.prototype.__proto__ 为 null,原型链到这里结束。
更多信息请参考:JavaScirpt深入之从原型到原型链