了解完了对象,也了解了常用的创建对象的方式--构造函数式和字面量式,那么除此自外,还有哪些创建对象的方式呢?
工厂模式
说白了就是一个封装函数,在函数内部定义对象的细节,最后返回之
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);//23console.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();//27person_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);//truevar dhl = new Xmw("董涵琳","女",7);dhl.school = "吴刘小学"console.log(dhl.school)//吴刘小学;console.log(dhl.hasOwnProperty("school"));//truedelete 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()//傲娇Catcat.height =1.30console.log(cat.height)//1.30var 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)//undefinedfunction 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深入之从原型到原型链