// 虽然构造函数和对象字面量都可以用来创建对象,但这些方式都有一个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码.为解决这个问题,人们开始使用工厂模式的一种变体。
//工厂模式
// 优点:解决了创建多个相似对象的问题,
// 缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(o.name);
};
return o;
}
var person1 = createPerson('jerry',20,'honker');
// person1.sayName();//jerry
// console.log(typeof person1);//object
console.log(person1 instanceof createPerson);
//构造函数模式
// ECMAScript中构造函数可以创建特定类型的对象,像object和Array这样的原生构造函数,在运行时会自动出现在执行环境中.从而定义自定义对象类型的属性和方法
//要创建新实例,必须使用 new 操作符
//缺点:就是每个方法都要在每个实例上重新创建一遍
function Person(name,age,job){//按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头;为了区别于 ECMAScript 中的其他函数
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
};
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
//person1 和 person2 分别保存着 Person 的一个不同的实例。这两个对象都有一个 constructor (构造函数)属性,该属性指向 Person
//以这种方式定义的构造函数是定义在 Global 对象(在浏览器中是 window 对象)中的
console.log(person1)
console.log(person1.constructor == Person);//实例的constructor属性指向Person构造函数
console.log(person1 instanceof Person);//检测对象类型
//构造函数和其他函数的唯一区别就是调用的方式不同,构造函数也是函数,任何函数通过new来定义,那么他都可以称为构造函数
var person = new Person("Nicholas", 29, "Software Engineer");//当做构造函数来使用
// person.sayName(); //"Nicholas"
Person("Greg", 27, "Doctor");//添加到window // 作为普通函数调用
// window.sayName();//"Greg"
var o = new Object();// 在另一个对象的作用域中调用
Person.call(o,"Kristen", 25, "Nurse");
// o.sayName();
//原型模式
// 创建的每一个函数都有一个prototype属性,这个属性是一个指针, 指向一个对象,而这个对象的作用是包含可以由特定类型的所有实例共享的属性和方法;
function Person(){};//构造函数为空, 方法和所有属性直接添加到了 Person 的 prototype 属性中
Person.prototype.name = "Nicholas";
Person.prototype.age = 23;
Person.prototype.job = "hunter";
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
// person1.sayName();//Nicholas
var person2 = new Person();
// person2.sayName();//Nicholas
// alert(person1.sayName == person2.sayName) // true 新对象的这些属性和方法是由所有实例共享的
console.log(Person);//ƒ Person(){} 构造函数
console.log(Person.prototype);//{name: "Nicholas", age: 23, job: "hunter", sayName: ƒ, constructor: ƒ} 构造函数的原型对象
console.log(Person.prototype.constructor);//ƒ Person(){} 原型对象中指向构造上函数的指针
console.log(person1)//Person {} //构造函数创建的实例
console.log(person1.__proto__)//实例内部指针, 指向构造函数的原型对象
// isPortotypeof() 方法来检测实例 和 原型对象之间的关系
console.log(Person.prototype.isPrototypeOf(person1));//true
//getPrototypeOf() 方法来获取实例内部指针 指向的 原型对象的值
console.log(Object.getPrototypeOf(person1).name);//Nicholas
//每当有代码要读取某个对象的值时, 都会执行一次搜索,先从该对象的实例中查找, 如果找到则返回该属性的值,如果没找到继续像实例指向的原型对象中寻找,找到则返回该属性的值
//虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例修改原型对象中的值,在实例中重新给一个属性赋值的话, 那么相对于在实例中创建了该属性, 该属性会屏蔽原型对象中的值
function Person (){};
Person.prototype.name = 'jerry';
Person.prototype.age = 20;
Person.prototype.job = 'hunter';
var person1 = new Person();
var person2 = new Person();
person1.name = 'jack';//修改person1实例中name的值
console.log(person1.name);//jack 实例中的name属性屏蔽了构造函数的原型对象中的name属性
console.log(person2.name);//jerry 实例2 的name值还是获取原型对象中的值
//delete 操作符 可以删除实例中的属性
delete person1.name;//删除了person1实例中的name属性
console.log(person1.name);//在person1中查询不到name值, 所以继续通过__proto__ 实例内部指针去构造函数原型对象中查找, 结果为jerry
//hasOwnProperty 检测给定属性是否是存在在实例中,是属于实例的属性返回true , 否则返回false
function Person(){};
Person.prototype.name = 'Petter';
Person.prototype.age = 12;
var person1 = new Person();
var person2 = new Person();
console.log(person.hasOwnProperty('name'));//false
person1.age = 30;
console.log(person1.hasOwnProperty('age'));//true
//in 操作符 : 判断能否通过对象访问到给定属性, 无论属性存在在实例中还是原型对象中, 都返回true
function Person(){};
Person.prototype.name = "Betty";
Person.prototype.age = 16;
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
var person2 = new Person();
// alert('name' in person1);//true
console.log(person1.constructor instanceof Person)
person1.name = 'Tom';
// alert('name' in person1);//true
// alert('father' in person1);//false 因为father属性即不存在于实例中, 也不存在于原型对象中
// alert(person1.hasOwnProperty('name'));//true name存在与person1实例中
//for-in 列举对象中可枚举的属性 , [[Enumerable]]特性为false 的属性不会被枚举出来
var objNameArr = [];
for(var item in person1){
console.log(item);
objNameArr.push(item);
}
console.log(objNameArr);// ["name", "age", "job", "sayName"]
var objNameArr = [];
for(var item in Person.prototype){
console.log(item);
objNameArr.push(item);
}
console.log(Person.prototype);//构造函数原型对象包含 name age job sayName 和 constructor
console.log(objNameArr);// ["name", "age", "job", "sayName"] constructor的[[Enumerable]]特性为false ,所以不可枚举
//要获取对象中可枚举的属性可以用Object.keys()方法
var keys = Object.keys(Person.prototype);
console.log(keys);// ["name", "age", "job", "sayName"]
//如果要获取对象中的所有属性,无论可枚举和无可枚举 , 可以用方法Object.getOwnPropertyNames()方法
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);//["constructor", "name", "age", "job", "sayName"] constructor 也会存在于数组当中
//更简单的原型模式写法
var Person = function(){};
Person.prototype = {
name :'jerry',
age:20,
job:'hunter',
sayName:function(){
alert(this.name);
}
}
var person1 = new Person();
console.log(Person.prototype.constructor);//ƒ Object() { [native code] } 构造函数的原型对象的constructor属性, 不在指向构造函数Person
console.log(Person.prototype.contructor instanceof Person);//false 原型对象指针constructor不再指向构造函数Person; 如果constructor很重要,可以通过设置
console.log(person1 instanceof Person) //true
console.log(person1.constructor == Object);
console.log(person1.constructor == Person);
//设置constructor
var Person = function(){};
Person.prototype = {
constructor:Person,
name :'jerry',
age:20,
job:"hunter",
sayName:function(){
alert(this.name);
}
}
console.log(Person.prototype.constructor == Person);//true 通过这种方式设置的constructor属性是可枚举的, [[Enumerable]]会被设置为true,默认情况下constructor属性是不可枚举的,可以使用Object.defineProperty()方法
//原型的动态性
function Friend(){};
var girl = new Friend();
Friend.prototype.sayHi = function(){//sayHi 方法是在创建实例后加的, 下方在实例中调用sayHi方法还是可以成功
alert('hi');
}
// girl.sayHi();//hi
function Person(){};
var friend = new Person();
Person.prototype = {
name:'jerry',
age:20,
job:'hunter',
sayHi:function(){
alert(this.name);
}
}
console.log(friend.sayHi);//undefined
console.log(Person.prototype);
//原生对象的原型
console.log(Array.prototype);
//为原生对象添加新方法
String.prototype.startWith = function(text){//不推荐
return this.indexOf(text)==0;
}
var str = 'Hello World';
console.log(str.startWith('Hello'));//true
//组合使用构造函数模式和原型模式 使用度最高的模式
// 组合模式:就是组合使用构造函数模式和原型模式; 构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
function Friend(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.firends = ["Shelby", "Court"];
}
Friend.prototype = {
constructor:Friend,
sayName:function(){
alert(this.name);
}
}
var firend1 = new Friend("Nicholas", 29, "Software Engineer");
var firend2 = new Friend("Greg", 27, "Doctor");
console.log(firend1.firends);
firend1.firends.push('cao');
console.log(firend1.firends);//["Shelby", "Court", "cao"] 自己的实例属性
console.log(firend2.firends);//["Shelby", "Court"]
// 虽然构造函数和对象字面量都可以用来创建对象,但这些方式都有一个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码.为解决这个问题,人们开始使用工厂模式的一种变体。
6_2 创建对象
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 2018.2.3 目标:身心健康,财富丰盛 好种子: 1早起健康早餐,种下健康的种子,回向群里姐妹及家人身心健康 ...