JavaScript-------深入理解对象

JavaScript工厂模式

工厂模式的作用

创建对象;降低代码的冗余度。

function person(name,age,gander){
   return {
        name:name,
        age:age,
        gander:gander,
        sayName:sayName=function(){
        console.log(name);
        }
   }
}
var p1=person('larry',20,'male');
var p2=person('terry',22,'female');
console.log(p1);
console.log(p2);
p1.sayName();
p2.sayName();
console.log(p1.sayNam === p2.sayName);//false

缺点:不知道谁创建了对象;每创建一个对象就要创建一个sayName()函数,造成了内存资源的浪费。

//优化 创建全局的sayName函数解决了 内存资源浪费问题
function sayName(){
 console.log(name);
}
//但还是不知道谁创建了这一个对象
function person(name,age,gander){
   return {
        name:name,
        age:age,
        gander:gander,
        sayName:sayName
   }
}
var p1=person('larry',20,'male');
var p2=person('terry',22,'female');
console.log(p1);
console.log(p2);
p1.sayName();
p2.sayName();
console.log(p1.sayNam === p2.sayName);//true

所以就有了自定义构造函数模式

构造函数模式

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

1.不需要返回值

2.属性和方法直接赋值给this

3.知道是谁创建了对象

function Person(name,age,gander){
    //new的作用:
    //1.js创建一个obj对象
    //2.js把this指向了obj对象
    //3.执行函数内部代码 唯一需要自己手动操作
    //4.js会自动的返回一个obj对象
    this.name=name;
    this.age=age;
    this.gander=gander;
    this.sayName=function(){
        console.log(this.name);
    }
}
var p1=new Person('zhangsan',20,'male');
var p2=new Person('lisi',18,'female');
console.log(p.sayName === p2.sayName);//false
console.log(p.sayName instanceof p2.sayName);//false
//解决内存资源浪费的方法 同上面工厂模式的一样

instanceof

constructor是用来标识对象类型的。但是用instanceof操作符来确定对象类型更加可靠。

instanceof操作符作用于检测构造函数的prototype属性是否出现在实例对象的原型链上。

var obj=new Object();
console.log(obj instanceof Object);//true
//构造函数模型上的也可以检测
console.log(p1 instanceof Person);//true

原型模式

使用prototype属性在函数的原型对象里面创建属性和方法。属性和方法可以被实例对象共享。

function Person(){}
Person.prototype.name='zhangsan';
Person.prototype.age=20;
Person.prototype.gender='male';
Person.prototype.sayName=function(){
    console.log(this.name);
};
var person1=new Person();
person1.sayName();//zhangsan
var person2=new Person();
person2.sayName();//zhangsan
console.log(person1.sayName === person2.sayName);//true

优点:属性和方法共享,解决了内存资源浪费的问题;

缺点:原型对象里面的属性值是不可以改变的;只能遮挡(如下案例)

function Person(){}
Person.prototype.name='zhangsan';
Person.prototype.age=20;
Person.prototype.sayName=function(){
    console.log(this.name);
}
var person1=new Person();
var person2=new Person();
person1.name='lisi';
console.log(person1.name);//lisi 来自实例
console.log(person2.name);//zhangsan 来自原型

遮挡可以用delete方法把该实例属性删除。

function Person(){}
Person.prototype.name='terry';
Person.prototype.age=20;
var person1=new Person();
// 通过hasOwnProperty()可以查看访问的是实例属性还是原型属性
console.log(person1.hasOwnProperty('name'));//false
person1.name='lisi';
console.log(person1.name);//lisi 来自实例
console.log(person1.hasOwnProperty('name'));//true
//通过delete删除name属性
delete person1.name;
console.log(person1.name);//terry 来自原型对象
console.log(person1.hasOwnProperty('name'));//false

这里可以衍生出一个判断属性是否是原型对象属性的方法:

function Person(){}
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
  console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
// 无论属性是在实例上还是在原型上,都可以检测到
console.log("name" in person1); // true
console.log("name" in person2); // true
// 判断一个属性是否是原型属性
function hasPrototypeProperty(obj,name){
    //不在实例中但是可以访问到的属性属于原型属性
    return !obj.hasOwnProperty(name) && (name in obj);
}
console.log(hasPrototypeProperty(person1, 'name'));//true

简单原型模式

优点:降低了代码的冗余度;

function Person(){}
Person.prototype={
	name:'larry',
	age:20,
	gender:'male';
	sayName:function(){
		console.log(this.name);
	}
};
//Person.prototype创建的属性和方法 相当于Person.prototype=new Object();创建了一个新的对象;Person.prototype成为了Object构造函数的一个实例对象,他的[[prototype]]就会指向Object构造函数的原型对象。所以其constructor指向就会发生改变,指向了Object构造函数
var person1=new Person();
console.log(person1.constructor === Person);//false
console.lg(person1.constructor === Object);//true

解决constructor指向问题

1.可以在重写原型对象时,专门给他设置一个constructor的属性

2.原生的constructor是不可枚举的,所以可以通过Object.defineProperty()方法来定义constructor属性

function Person(){}
Person.prototype={
	//1.直接设置constructor属性
	//constructor:Person;
	name:'lisi',
	age:20,
	gender:'male',
	sayName:function(){
		console.log(this.name);
	}
};
//2.通过Object.defineProperty()方法来需改constructor属性
Object.defineProperty(Person.prototype,'constructro',{
	enumerable:false,
    value:Person
});
var person1 = new Person()
console.log(person1.constructor == Person); //true
console.log(person1.constructor == Object); //false

原型问题

导致所有实例默认都拿到相同的属性值;真正的问题来自包含引用值的属性。来看下面的案例:

function Person(){}
Person.prototype={
	constructor:Person,
	name:'lisi',
	age:30,
	firends:['zhangsan','wangwu'],
	sayName:function(){
		console.log(this.name);
	}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("zhaoliu");
console.log(person1.friends); // [ 'lisi', 'wangwu', 'zhaoliu' ]
console.log(person2.friends); // [ 'lisi', 'wangwu', 'zhaoliu' ]
console.log(person1.friends === person2.friends); // true

组合模式

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

利用构造函数用于定义实例属性,原型模式用于定义方法和共享属性。

function Person(name,age,gender){
	this.name=name;
	this.age=age;
	this.gender=gender;
	this.firends=['lisi','zhangsan'];
}
Person.prototype={
	constructor:Person,
	sayName:function(){
		console.log(this.name);
	}
}
var p1 = new Person('larry', 44, 'male');
var p2 = new Person('terry', 39, 'male');

p1.firends.push('robin');
console.log(p1.firends); // [ 'zhangsan', 'lisi', 'robin' ]
console.log(p2.firends); // [ 'zhangsan', 'lisi' ]
console.log(p1.firends === p2.firends); // false
console.log(p1.sayName === p2.sayName); // true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值