js的面向对象
1.面向对象语言都有一个标志,那就是类的概念。通过类可以创建任意多个具有相同属性的方法的对象。但是在ECMAScript中
没有相关类的概念。但是我们可以通过其他方式来模拟面向对象中的类。
-工厂模式:不细说
-构造函数模式:比如像ECMAScript中的Array,Object,Date等都是通过构造函数来创建的。
-动态原型模式
-寄生构造函数模式
-稳妥构造函数模式
<script type="text/javascript" charset="utf-8">
var obj = new Object();
obj.name = 'zs';
obj.sex = 'male';
obj.sayName = function() {
alert('my name is zs');
};
// 类的概念
// 第一种形式 工厂模型 通过这个方法用来创建实例
function createPerson(name, sex, age) {
var obj = new Object();
obj.name = name;
obj.sex = sex;
obj.age = age;
obj.sayName = function(){
alert(this.name);
};
return obj;
}
var p1 = createPerson(ls, male, 24);
// 第二种形式,构造函数式, 函数的第一个字母大写,约定俗成的表示为一个类的模板
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayName = function() {
alert(this.name);
};
}
// 构造一个对象,一般使用new 关键字,并且传递参数 执行模板代码 返回对象
var p2 = new Person('ww', 'famale', 20);
alert(p2.constructor == Person); // 返回结果为true
alert(p2 instanceof Person); // 返回结果为true
alert(p1 instanceof Object); // 返回结果为true
// 创建对象的方式
// 1.当做构造函数去使用 即new的方式
var p3 = new Person('w1', 'male', 10);
// 2.作为普通函数去调用 //在全局环境里定义属性并复制 直接定义在window上
var p4 = Person('w2', 'famale', 30);
// 3.在另一个对象的作用域中调用
var o = new Object();
Person.call(o, '11', 'male', 30);
</script>
2.原型
-我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途包含可以由特定类型
的所有实例共享的属性和方法。
-原型对象实际就是一个构造函数的实例对象,与普通的实例对象没有什么本质上的区别,js中每一个对象都有一个原型对象。不过
它比较特殊,该对象所包含的所有属性和方法能够供构造函数的所有实例共享,也就是其他编程语言所说的继承,而js通过原型对象来
继承,简称原型继承。静态原型继承:Object.prototype.[method field];
-简单实现Array中的push,pop方法。
<script type="text/javascript" charset="utf-8"> // 原型 prototype // 构造函数方式 function Person(name, age){ this.name = name; this.age = age; this.sayName = function() { alert('i am name'); }; } function sayName() { alert(this.name); } var p1 = new Person('zs', 20); var p2 = new Person('ls', 21); alert(p1.sayName == p2.sayName); //返回结果为false alert(p1.name == p2.name); //false p1.sayName(); alert(p1.sayName == p2.sayName); // 将Person中的sayName方法去掉之后,返回结果为true // 但是定义过多的全局变量 显然是不好的,于是我们采用下面的方法。 // 创建每一个函数的时候 都有一个Prototype属性,这个属性其实是一个指针。而这个指针总是指向一个对象。 // (这个对象的用途就是将特定的属性和方法 包含在内,起到一个所有实例所共享的作用) function Person1() { } var obj = Person1.prototype; alert(obj.constructor); // 返回值为function Person(){} obj.name = 'zs'; obj.age = 20; obj.sayName = function() { alert(this.name); }; var p3 = new Person(); var p4 = new Person(); alert(p3.name); //zs alert(p4.name); //zs alert(p3.age); // 20 alert(p4.age); // 20 // 构造函数 原型对象 实例对象 // 构造函数.prototype = 原型对象 // 原型对象.constructor = 构造函数 // 实例对象.prototype = 原型对象 obj.isPrototypeOf(p3); // 结果为true </script>
3.原型的详细方法
<script type="text/javascript" charset="utf-8">
// isPrototypeOf() 判断原型的方法
// Object.getPrototypeOf();根据实例对象获得原型对象
function Person() {
}
Person.prototype.name = 'zs';
Person.prototype.age = 20;
Person.prototype.sayName = function() {
alert('我是原型对象的方法');
};
var p1 = new Person();
var prototypeObj = Object.getPrototypeOf(p1); // 得到原型对象
var p2 = new Person();
p2.name = 'w5';
// 每次代码读取一个对象的属性的时候,会先进行一次搜索,搜索实例对象。如果没有,再去实例所对应的原型对象里搜索。
alert(p2.name); // 返回值为w5
// 如果非要获得原型对象中的属性,可以将实例中的属性删除掉。
delete p2.name;
// 判断一个对象是原型属性还是实例属性
var p3 = new Person();
p3.hasOwnproperty('name'); //返回false 因为是原型属性
// in操作符:for-in
// in操作符是判断属性是否存在于实例对象和原型对象中。如果有一个存在,则返回true,如果都不存在,才返回false
var p5 = new Person();
alert('name' in p5); // 返回true 证明在p5对象中有name属性
var p6 = new Person();
p6.name = 'w3';
alert('name' in p6); //返回true
// 判断一个属性是否存在原型中。第一个参数是当前对象,第二个参数是要判断的属性
function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty('name') && name in object;
}
// ECMA5新特性 Object.keys
// 可以将对象中所有的属性找到,并返回一个数组
var m1 = new Person();
m1.name = 'zs';
m1.age = 20;
var attribute = Object.keys(m1);
alert(arrtibute); // 返回name和age
var attribute = Object.keys(Person.prototype); //返回 name age sayName
// ECMA5中 constructor属性 不可以被枚举
// Object.getOwnPropertyNames 方法的作用是 枚举出对象的全部属性,无论该属性能否被枚举
alert(Object.getOwnPropertyNames(m1));// constructor name age sayName
</script>
4.模拟each方法
<script type="text/javascript" charset="utf-8">
// Array each方法
// ECMA5中,forEach方法。
var arr = [1,2,3,4,5]; //只适合遍历一维数组
arr.forEach(function(item, index, array) {
alert(item);
});
// 自己实现一个Array的Each方法,可以遍历多维数组
var arr1 = [1,2,3,[4,[5,[6]]]];
Array.prototype.each = function(fn){
try{
// 1.目的,遍历数组的每一项
// 计数器,记录当前遍历的元素位置
this.i || (this.i = 0);
if (this.length > 0 && fn.constructor == Function) {
// 循环遍历
while(this.i < this.length){
// 获取数组的每一项元素
var e = this[this.i];
// 如果当前元素获取到了,并且是一个数组
if (e && e.constructor == Array) {
// 递归
e.each(fn);
} else {
fn.call(e, [e]);
}
this.i++;
}
// 释放内存,垃圾回收
this.i = null;
}
}catch(ex){
// do something
}
return this;
};
arr1.each(function(item){
alert(item);
});
</script>
5.简单原型的使用
直接通过对象字面来重写整个对象(会改变原型对象的构造器)。
ECMA5中的Object.defineProperty()方法可以为原型对象重新加入构造器。
原型的动态性(注意原型和创建实例的先后顺序)
<script type="text/javascript" charset="utf-8">
// 简单原型
function Person() {
}
Person.prototype = {
name : 'zs',
age : 18,
job : 'doctor',
say: function(){
alert('aa');
}
};
var p1 = new Person();
alert(p1.name);
p1.say();
alert(Person.prototype.constructor); //不加constructor : Person这句话,Object的构造函数,加上之后可以枚举(这样不对)
// ECMA5给远行对象重新设置构造器方法
// 3个参数 参数1:重设构造器的对象 参数2:设置什么属性 参数3:option配置项
Object.defineProperty(Person.prototype, 'constructor', {
enumerable : false,
value : Person
});
// 原型动态特性
function A() {
}
var a = new A();
A.prototype.say = function(){
alert('bbb');
};
a.say();//返回bbb 没有问题
function B() {
}
var b = new B();
B.prototype = {
name : 'zs',
age : 18,
job : 'doctor',
say: function(){
alert('aa');
}
};
b.say(); // say is not a function 必须把var b = new B(); 拿到后面去写(实例对象必须在原型对象之后创建)
</script>
6.原型对象存在的问题
-原型对象虽然可以对所有实例的属性和方法共享,但是他的局限性也是很明显的。正因为共享的特性,也导致了原型存在
的最大问题。
-我们一般组合使用构造函数式和原型模式,在实际开发中,这种模式也是应用的最稳广泛。
-动态原型模式:就是把信息都封装到函数中,这样体现了封装的概念。
-稳妥构造函数式:所谓稳妥模式就是没有公共属性,而且其他方法也不引用this对象。稳妥模式最适合在安全的环境中
使用。如果你的程序对于安全性要求很高,那么非常适合这种模式。
<script type="text/javascript" charset="utf-8">
// 原型概念:原型对象里的所有属性和方法,被所有构造函数实例化出来的对象所共享
function Person(){
}
Person.prototype = {
constructor:Person,
name:'zs',
age:20,
job:'doctor',
friends:['ls', 'ww'],
sayName:function(){
alert('my name!');
}
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push('zl');
// p1增加friend之后,由于共享性,p2的friend也增加了,这样会存在问题。
alert(p1.friends);
alert(p2.friends);
// 组合使用原型和构造函数式(开发常用定义类的方式)
function Person1(name, age, friends, job) {
this.name = name;
this.age = age;
this.friends = friends;
this.job = job;
}
Person.prototype = {
constructor : Person,
sayName : function() {
alert(this.name);
}
};
// 动态原型模式:(让代码 所有属性和方法都封装到一起)
function Person2(name, age, friends, job) {
this.name = name;
this.age = age;
this.friends = friends;
this.job = job;
if(typeof sayName != 'function'){
Person.prototype.sayName = function() {
alert(this.name);
};
}
}
// 稳妥构造函数式:durable object(稳妥对象)
// 1.没有公共属性
// 2.不能使用this
function Person3(name, age, job) {
// 创建一个要返回的对象
var obj = new Object();
var name = name;
obj.sayName = function(){
alert(name);
};
return obj;
}
</script>
7.继承
- 我们都知道构造函数,原型和实例的关系,如果我们让原型对象等于另一个类的实例,此时的原型对象将
包含一个指向另一个原型的指针,相应的另一个原型也包含着一个指向另一个构造函数的指针。
-原型链:利用原型让一个引用类型来继承另一个引用类型的属性和方法。
-简单继承
-类继承(模板继承或者借用构造函数继承)
-混合使用继承
<script type="text/javascript" charset="utf-8">
// js中如何实现继承 采用原型链的概念
// 构造函数 原型对象 实例对象
// 构造函数 .prototype = 原型对象
// 原型对象.constructor = 构造函数模板
// 原型对象.isprototypeOf(实例对象) 判断实例对象原型是否是该原型对象
// 如果我们让原型对象等于另一个类的实例
function Super(name) {
this.name = name;
}
Super.prototype = {
constructor : Sup,
sayName : function() {
alert(this.name);
}
};
function Sub(age){
this.age = age;
}
// 子类原型对象 等于父类的实例
// 子类的原型对象的构造器 变成了父类的构造器
Sub.prototype = new Sup();
</script>
<script type="text/javascript" charset="utf-8">
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.id = 10;
function Boy(sex){
this.sex = sex;
}
Boy.prototype = new Person('zs');
var b = new Boy();
alert(b.name);
alert(b.id);
// 类继承,只继承模板,不继承原型(借用构造函数方式继承)
function Person1(name, age){
this.name = name;
this.age = age;
}
Person1.prototype.id = 10;
function Boy1(name, age, sex) {
// call apply
Person1.call(this, name, age);
this.sex = sex;
}
var b1 = new Boy1('zs', 20, 'male');
alert(b1.name);
alert(b1.id); // undefined 父类的原型对象并没有继承,这就是类继承的缺点
// 混合继承 通过原型继承 借用构造函数继承 = 混合继承
// 类继承,只继承模板,不继承原型(借用构造函数方式继承)
function Person2(name, age){
this.name = name;
this.age = age;
}
Person2.prototype.id = 10;
Person2.prototype.sayName = function(){alert(this.name);};
function Boy2(name, age, sex) {
// call apply
Person2.call(this, name, age);
this.sex = sex;
}
// 不传参数 ,父类的实例和子类的原型对象的关联
// 目的只是为了继承父类的原型对象
Boy2.prototype = new Person2();
</script>
8.模拟extjs底层继承方式
<script type="text/javascript" charset="utf-8">
// 混合继承:原型继承和借用构造函数继承
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype = {
constructor : Person,
sayHello : function(){alert('hello world');}
};
function Boy(name, age, sex) {
Person.call(this, name, age); // 也可以写成Boy.superclass.constructor.call(this, name, age);
this.sex = sex;
}
Boy.prototype = new Person();//这里其实也继承了父类的模板,而且继承了父类的原型对象。
// 混合继承 做了3件事, 继承了两次父类模板,继承了一次原型对象。
// 混合模板的缺点,如果父类的属性特别多,这样很不方便
// 继承一次父类模板,继承一次父类原型对象,这样是最好的,我们自己写一个extend方法
function extend(sub, super){
// 目的:只继承父类的原型对象
// 1.用一个空函数进行中转
var F = new Function();
// 2.空函数的原型对象和父类的原型对象转换
f.prototype = super.prototype;
// 3.将空函数的原型对象,给子类的原型对象
sub.prototype = new F();
// 4.还原子类的构造器
sub.prototype.constructor = sub;
// 5.保存父类的原型对象 原因:方便解耦 方便获得父类的原型对象
sub.superclass = sup.prototype;// 利用子类的静态属性,存储父类的原型对象
// 判断父类的原型对象的构造器
if (sup.prototype.constructor == Object.prototype.constructor) {
sup.prototype.constructor = sup;
}
}
// 给子类加一个原型对象方法
Boy.prototype.sayHello = function(){
alert('hello sub!!');
};
b.sayHello(); //返回值为hello sub
// 为了想要使用父类的sayHello方法
Boy.superclass.sayHello.call(b); // 这样就没问题了
</script>

浙公网安备 33010602011771号