首页 > 代码库 > JavaScript--面向对象
JavaScript--面向对象
1.创建对象
1) 工厂模式
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var p1 = createPerson("terry",11,"boss");
var p2 = createPerson("larry",12,"daBoss");
工厂模式的问题
var t1 = typeOf p1; //object 无法对象识别,即所有对象都是Object类型
2) 构造函数模式
js中可以自定义构造函数,从而自定义对象类型的属性和方法,构造函数本身也是函数,只不过可以用来创建对象
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var p1 = new Person("terry",11,"boss");
var p2 = new Person("larry",12,"daBoss");
使用new操作符调用构造函数创建对象实际上经历了如下几个步骤
1) 创建一个新对象
2) 将构造函数的作用域赋给新对象(this指向这个新对象)
3) 执行构造函数中的代码
4) 返回新对象。
这种创建对象的方法可以将实例标识为一种特定类型(例如Person类型)。
var t1 = typeOf p1; //t1为Person
1.构造函数当做函数
Person("larry",12,"daBoss")
当在全局作用域中调用一个函数时,this总是指向Global对象(window对象)。
2.构造函数的问题
每个方法都需要在每个实例上重新创建一遍,但是毫无必要。
可以在全局范围中声明一个函数,然后将引用传递给对象中的函数属性。但是这样做会导致全局函数过多,体现不了对象的封装性
console.log(p1.sayName == p2.sayName); //false
3) 原型模式
每个函数都有一个属性:prototype(原型属性),这个属性是一个指针,指向一个对象,该对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
function Person(){
}
Person.prototype.name = "tom";
Person.prototype.age = 22;
Person.prototype.job="boss";
Person.prototype.sayName = function(){
alert(this.name);
}
var p1 = new Person();
p1.name = "terry";
var p2 = new Person();
p2.name = "larry";
创建了自定义的构造函数之后,其原型对象默认会取得constructor属性;当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。(指向的是原型对象而不是构造函数)
1.属性的访问
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。
1) 首先从对象实例本身开始查找
2) 如果不在对象实例中,则继续搜索指针指向的原型对象。
2.删除实例属性
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。通过delete操作符可以完全删除实例属性。
3.检测属性是否存在于实例中
hasOwnProperty(p); 判断p指定的属性是否存在于实例中,如果存在返回true
console.log(p1.hasOwnProperty("name")); //false 存在于原型中而不是实例对象中
4.原型与in操作符
1) 在for-in 可以访问存在于实例中的属性,以及原型中的属性
2) 单独使用
a in b; 通过b对象可以访问到a属性的时候返回true,无论该对象在实例中还是在原型中
console.log("name" in p1); //true
判断一个属性是否在原型
function hasPrototypeProperty(obj,name){
//不在实例中但是可以访问到的属性属于原型属性
return !obj.hasOwnProperty(name) && (name in obj);
}
5.原生对象的原型
通过原生对象的原型,不仅可以取得所有默认方法的调用,而且可以定义新方法。可以向修改自定义对象的原型一样修改原生对象的原型,可以随时添加方法。
String.prototype.startsWith = function(text){
return this.indexOf(text) == 0;
}
var msg = "Hello world";
alert(msg.startsWith("Hello")); //true
6.原型对象的问题
所有实例在默认情况下都将取得相同的属性值,这种共享对于函数来说非常合适,但是包含引用数据类型的值就不太好
Person.prototype = {
name : "briup",
friends : ["larry","terry"]
}
var p1 = new Person();
var p2 = new Person();
p1.name = "terry";
p1.friends.push("tom");
p1.friends; //["larry","terry","tom"]
p2.friends; //["larry","terry","tom"]
7.更简单的原型语法
将原型对象设置为等于一个对象字面量形式创建的新对象。实例对象使用效果相同,但是原型中的constructor属性不再指向Person,因为每创建一个对象,就会同时创建它的 prototype对象,这个对象也自动获得constructor属性。这里我们重写了prototype对象因此该原型中constructor属性就变成了新对象的constructor属性(Object)
p1.constructor.prototype.constructor //Object
function Person(){
}
Person.property = {
//constructor: Person, 如果constructor比较重要,可以指定它的值,Enumerable ,true,默认为false
name:"tom",
age :22,
job :"boss",
sayName:function(){
alert(this.name);
}
}
//定义constructor属性,不可遍历
Object.defineProperty(Person.prototype,"constructor",{
enumerable : false,
value : Person
});
4) 组合使用构造函数模式和原型模式
构造函数用于定义实例属性,原型模式用于定义方法和共享属性。这种模式是目前在ECMAScript中使用最广泛,认同度最高的一种创建自定义类型的方法。
function Person(name,age){
this.name = name,
this.age = age,
this.friends = []
}
Person.prototype = {
constructor : Person,
sayName:function(){
alert(this.name);
}
}
var p1 = new Person("terry",11);
var p2 = new Person("larry",12);
p1.friends.push("tom");
p2.friends.push("jacky");
console.log(p1);
console.log(p2);
2.继承
1) 原型链
每个构造函数都有一个原型对象,原型对象中都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。当原型对象等于另外一个类型的实例即继承。调用某个方法或者属性的步骤
a.搜索实例
b.搜索原型
c.搜索父类原型
//定义父类类型
function Animal(){
this.name = "animal"
}
Animal.prototype = {
sayName : function(){
alert(this.name);
}
}
//定义子类类型
function Dog(){
this.color = "灰色"
}
//通过将子对象的原型对象指向父对象的一个实例来完成继承
Dog.prototype = new Animal();
//子对象的方法其实是定义在了父类对象的实例上。
Dog.prototype.sayColor = function(){
alert(this.color);
}
var dog = new Dog();
console.log(dog);
dog.sayColor();
dog.sayName();
1.默认原型
所有函数默认原型都是Object的实例,默认原型中都会包含一个内部指针,指向Object.prototype.
2.确定原型和实例的关系
1) 通过使用instanceof
instance instanceof Object //true
instance instanceof SuperType //true
instance instanceof SubType //true
2) 通过使用isPrototypeOf()
只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型
Object.prototype.isPrototypeOf(instance) //true
SuperType.prototype.isPrototypeOf(instance) //true
SubType.prototype.isPrototypeOf(instance) //true
3.谨慎定义方法
子类型覆盖超类型中的某个方法,或者是需要添加超类中不存在的方法,都需要将给原型添加方法的代码放在继承之后(即替换原型的语句之后)
4.原型链问题
1)通过原型来实现继承时,原型实际上会变成另一个类型的实例,原来的实例属性也就变成了现在的原型属性
2)在创建子类型的实例时,不能向超类型的构造函数传递参数。
因此实践中很少会单独使用原型链
2) 借用构造函数
也称 "伪造对象" 或 "经典继承",在子类型构造函数的内部调用超类型构造函数。函数不过是在特定环境中执行代码的对象,因此通过apply(),call()方法可以在(将来)新建对象上执行构造函数,即 在子类型对象上执行父类型函数中定义的所有对象初始化的代码。结果每个子类实例中都具有了父类型中的属性以及方法
function Animal(name){
this.name = name;
this.colors = ["red","gray"];
}
function Dog(name){
//继承了Animal
Animal.call(this,name);
this.color = "gray";
}
Animal.prototype.sayName = function(){
alert(this.name);
}
var dog = new Dog();
dog.colors.push("hhh");
console.log(dog);
var animal = new Animal();
console.log(animal);
//如果将函数定义在构造函数中,函数复用无从谈起
dog.sayName();
//在超类型的原型中定义的方法,对于子类型而言是无法看到的
3) 组合函数
也称"伪经典继承",将原型链和借用构造函数的技术组合在一起。原理是:使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承。
function Animal(name){
this.name = name;
this.colors = ["red","gray"];
}
function Dog(name){
//继承了Animal(属性)
Animal.call(this,name);
this.color = "gray";
}
Animal.prototype.sayName = function(){
alert(this.name);
}
//继承方法
Dog.prototype = new Animal();
Dog.prototype.constructor = Animal;
var dog = new Dog();
dog.colors.push("hhh");
console.log(dog);
var animal = new Animal();
console.log(animal);
dog.sayName(); //可以调用
JavaScript--面向对象