javascript 面向对象

javascript对象

创建对象方法

1.使用构造函数创建:

var cat = new Object();
cat.name = "hello kitty";
cat.age = 3;
cat.call=function(){
    alert("miao~~");
}

2.使用对象字面量创建一个对象

var cat {
    name: "hello kitty",
    age = 3,
    call:function(){
        alert("miao~~");
    }
}

因ECMAScript中无法创建类,虽然能通过Object函数或对象字面量来创建单个对象,但使用同一个接口创建很多对象,会产生大量代码。
—>工厂模式

3.通过工厂模式创建对象

抽象了创建对象的具体过程,用函数来封装以特定接口创建对象的细节。

funtion createPerson(name , age , job) {
    var o = new Object();
    o.name=name;
    o.age=age;
    o.job=job;
    o.sayname=funtion(){
        alert(this.name);
    }
    return o;
}
var person1 = createPerson("zhangsan",12,"Student");
var person2 = createPerson("Lisi",44,"Teacher");

工厂模式并未解决对象识别问题 —>构造函数模式

4.通过构造函数模式创建对象

(补充:对象冒充

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name);
    }
}
//1.构造函数没有new Object ,但后台会自动var obj = new Object
//2.this 相当于 obj
//3.构造函数无需返回对象引用个,它是后台自动返回的
var person1 = new Person("Xusan", 12, "Student");
var person2 = new Person("Lisi", 44, "Teacher");

//对象冒充
var o = new Object();
Person.call(o,'Micy',34,"Doctor");
alert(o.name);//o得到Person的所有
function Box(){}
var box = new  Box();

//解决了对象识别问题
alert(person1 instanceof Person);//true
alert(person2 instanceof Person);//true
alert(box instanceof Person);//false
alert(box instanceof Box);//true


alert(person1.sayName()==person2.sayName());//true 构造函数体内的方法的值是相等的
alert(person1.sayName==person2.sayName);//false  构造函数体内的方法的引用地址是不相等的(若想相同,可转为全局)

构造函数也是函数,第一个字母大写。
创建Person对象的新实例必须用 “new”操作符(如不通过new操作符,将和普通函数无异)

构造函数模式方式创建函数(sayName)会导致不同的作用域链和标识符解析,其每个对象实例都指向不同的方法实例。然而而创建多个完成同样任务的Function实例并无多大意义,把函数定义转移到构造函数外部,(等于设置成全局函数),但要让它只能被某个对象调用,其全局作用域又有点名不符实,且如果对象需定义多个方法,都设置成全局则无封装性可言 —–> 原型模式

5.通过原型模式创建对象

创建每一个函数 都有一个prototype(原型)属性(默认,自动生成的),该属性又是一个对象,它用于包含可以由特定类型的所有实例共享的属性和方法。原型prototype是函数的一个自带属性。可以看作是构造函数在实例化时创作的那个对象。
好处:让所有对象实例共享它所包含的属性和方法,不用在构造函数中定义对象信息,直接将这些信息添加在原型中

function Box(){}//当前构造函数体内什么都没有,若有则称其为实例属性,实例方法
//原型属性,原型方法:
Box.prototype.name="Lee";
Box.prototype.age=33;
Box.prototype.run=function(){
    return this.name+" "+this.age+" running";
}
var box1 = new Box();
alert(box1.name);
alert(box1.run());
//如果是实例方法,不同的实例化,其方法地址不同,唯一
//如果是原型方法,其地址是共享的
var box2 = new Box();
alert(box1.run==box2.run);//true
alert(box1.prototype);//undefined 其为函数的一个属性,使用对象实例无法访问prototype
alert(box1.__proto__);//[object object] 其为原型对象的指针
alert(box1.constructor);//function Box(){} 可以获取构造函数本身

//isPrototypeOf()
var obj = new Object();
alert(Box.prototype.isPrototypeOf(box1)); //true
alert(Object.prototype.isPrototypeOf(box1)); //true
//实例属性和方法不共享,原型属性和方法共享
box1.name="Milk";
alert(box1.name);//milk
alert(box2.name);//Lee


//判断实例中是否存在指定属性  hasOwnProperty()
alert(box1.hasOwnProperty("name"));//true
alert(box2.hasOwnProperty("name"));//false

//判断原型及实例中是否存在指定属性  in操作符
alert('name' in box2);  //true

//判断原型中是否存在指定属性:结合上述两种方法 

delete box1.name;//删除实例中的属性
delete Box.prototype.name;//删除原型中的属性


//原型模式的执行流程:(若function Box(){name="Jack"},box1.name会打印Jack)
//1.先查找构造函数实例里的属性或方法,若有,立刻返回
//2.如果构造函数实例中没有,则去其原型对象中找,有则返回

构造函数 : 有prototype属性 —–> (指向) 原型对象
原型对象 : 有constructor属性 ——> 对应的构造函数
实例对象 : 有__proto__属性 ——-> 对应的原型对象
则有:

alert(Box.prototype.constructor === Box);//true
alert(Box.prototype===box1.__proto__);//true

构造函数方式:

javascript 面向对象_第1张图片

原型模式方式:
javascript 面向对象_第2张图片
(解释:在原型模式声明中,多了两个属性(__proto__ 和constructor)),这两个属性都是创建对象时自动生成的,proto属性
是实例指向原型对象的一个指针,它可以指向构造函数的原型属性constructor。constructor是原型的一个属性,可以得到构造函数,可被原型指针定位,然后得到构造函数本身,起到连接对象实例和对应原型对象的作用。

使用字面量方式创建原型对象:

function Box(){}
Box.prototype = {   //{}相当于创建对象 new Object()
    name:'Lee',
    age:100,
    run:function(){
        return this.name+" "+this.age+" running";
    }
}
var box = new Box();
alert(box.constructor);     //function Object(){[native code]}

使用字面量创建对象和使用构造函数创建原型对象在使用上基本相同。但字面量创建的方式使用constructor不会指向实例对象的构造函数,而会指向Object,构造函数创建原型对象方式则相反。

可通过强制指向,让字面量方式的constructor指向实例对象的构造函数
补充:原型的声明是有先后顺序的,重写的原型会切断之前的原型

function Box(){}
Box.prototype = {   
    constructor:Box,
    name:'Lee',
    age:100,
    run:function(){
        return this.name+" "+this.age+" running";
    }
}
//var box = new Box();
//alert(box.constructor);       //function Box(){[native code]}

Box.prototype = {
    age:200 //切断了原来原型对象和构造函数对象实例的关系
};
var box = new Box();
alert(box.run);//box.run is not a function

原生对象的原型:

var box = [5,6,234,3,7,8,9];
alert(box.sort());
//查看sort是否是Array原型对象里的方法
alert(Array,prototype.sort); //function sort(){[native code]}

//内置引用类型的功能扩展
String.prototype.addString=function(){
    return this+" is  adding ...";
}
alert('Lee'.addString());//Lee is  adding ...

原型模式创建对象的缺点:
省略了构造函数传参初始化这一过程,其初始值都一致,它最大的缺点就是它最大的优点,即 共享
为解决其构造传参和共享的问题,可以组合构造函数+原型模式

function Box(name,age){ //不共享的  使用构造函数
    this.name=name;
    this.age=age;
    this.family=['Dad','Mon','Sister'];
}
Box.prototype = {
    constructor:Box,
    run:function(){
        return this.name+" "+this.age+" "+this.family+" running..."
    }
}
var box1=new Box('Lee',100);
var box2 = new Box('Jack',200);
alert(box1.run());//Lee 100 Dad,Mon,Sister running...
alert(box2.run());//Jack 200 Dad,Mon,Sister running...

原型模式:无论是否调用了原型的共享方法,它都会初始化原型的方法,并且在声明一个对象时,构造函数+原型 没有很好的封装。
—》动态原型模式

6.通过动态原型模式创建对象

function Box(name,age){ //不共享的  使用构造函数
    this.name=name;
    this.age=age;
    this.family=['Dad','Mon','Sister'];
    if(typeof this.run != "function"){//避免原型初始化多次
        Box.prototype.run = function(){
            return this.name+" "+this.age+" "+this.family+" running..."
        }
    }
}
var box1=new Box('Lee',100);
var box2 = new Box('Jack',200);

注意:不可再使用字面量的方式重写原型,会切断实例和新原型之间的联系

7.通过寄生构造函数模式创建对象

寄生构造函数模式=工厂模式+构造函数模式

function Box(name,age){
    var obj = new Object();
    obj.name=name;
    obj.age=age;
    obj.run=function(){
        return this.name+" "+this.age+" running...";
    }
    return obj;
}
var box1 = new Box("Lee",32);
alert(box1.run());

8.通过稳妥构造函数模式创建对象

稳妥构造函数模式类似寄生构造函数模式,它适合在一些安全环境中。(安全环境:禁止使用this和new , this 指 禁止在构造函数中使用this, new 指不使用new操作符调用构造函数)

function Box(name,age){
    var obj = new Object();
    obj.name=name;
    obj.age=age;
    obj.run=function(){
        return this.name+" "+this.age+" running...";
    }
    return obj;
}
var box1 = Box("Lee",32);
alert(box1.run());

继承

ECMAScript只支持继承,不支持接口实现。
所有的对象都继承自Object

1.继承 通过原型链实现

function Box(){     //被继承的函数称为超类型(父类,基类)
    this.name ='Lee';
}
function Desk(){    //继承的函数叫做子类型(子类,派生类)
    this.age=100;
}
function Table(){
    this.level = 'AAAAA';
}
//通过原型链继承:将超类型实例化后的对象实例赋值给子类型的原型属性
//new Box()会将Box构造函数里的信息和原型里的信息都交给Desk
//Dest的原型将得到Box的构造+原型里的信息
Desk.prototype = new Box();
Table.prototype = new Desk();

var desk = new Desk();
alert(desk.name);
var table = new Table ();
alert(table.name+" "+table.age);

//补充:执行流程同上
Box.prototype.name='Jack'; 
alert(desk.name);//Lee

//子类型从属于自己或者它的超类型
alert(desk instanceof Object);//true
alert(table instanceof Box);//true
alert(desk instanceof Table);//false

原型链继承流程图:
javascript 面向对象_第3张图片

2.借用构造函数(对象冒充):

为了解决引用共享和超类无法传参的问题,可采用一种叫 借用构造函数的技术,或者称为对象冒充(伪造对象,经典继承)。

//使用对象冒充继承
function Box(name,age){
    this.name=name;
    this.age=age;
}
Box.prototype.family='family';
function Desk(name,age){
    Box.call(this,name,age);//对象冒充
}
var desk = new Desk('Lee',100);
alert(desk.name);//Lee
alert(desk.family);//undefined  对象冒充只能继承构造里的信息

3.组合继承

通过原型链+借用构造函数
解决传参+方法共享

//对象冒充+原型链
function Box(age) {
    this.name = 'Lee';
    this.age = age;
}
Box.prototype.run = function(){
    return this.name + " " + this.age +  " running..."
};

function Desk(age) {
    Box.call(this, age);    //对象冒充 ,第二次调用Box
}
Desk.prototype = new Box();     //原型链继承,第一次调用Box

var desk = new Desk( 100);
alert(desk.run());

4.原型式继承

借助原型并基于已有对象来创建新对象,同时还不必因此创建自定义类型

//临时中转函数
function obj(o) { //o为将要传入的对象
    function F() {} //F构造是一个临时新建对象,用于存储传递过来的对象
    F.prototype = o; //将o对象实例赋值给F构造的原型对象,其实相当于原型链继承
    return new F(); //返回得到传递过来对象的对象实例

}
//字面量方式创建对象
var box = {
    name: 'Lee',
    age: 100,
    family: ['Dad', 'Mom', 'Sister']
};
var box1 = obj(box);
alert(box1.family);//Dad.Mom,Sister
box1.family.push('Brother');
alert(box1.family);//Dad.Mom,Sister,Brother

var box2 = obj(box);
alert(box2.family);//Dad.Mom,Sister,Brother,实现了共享

5.寄生式继承:

= 原型式+工厂模式
解决了组合继承两次调用构造函数的问题

//临时中转函数
function obj(o) { 
    function F() {} 
    F.prototype = o; 
    return new F(); 
}
//寄生函数
function create(o){
    var f = obj(o);
    //可以对f进行扩展
    f.run = function(){
        return this.name+"方法";
    }
    return f;
}
var box = {
    name: 'Lee',
    age: 100,
    family: ['Dad', 'Mom', 'Sister']
};
var box1 = create(box);
alert(box1.name);
alert(box1.run());

6.寄生组合继承

通过调用构造函数来继承属性,通过原型链混成形式继承方法,与组合继承不同之处在于子类型只继承了超类型原型的一个副本,并未继承其构造函数。因此只需要调用一次超类型构造函数。

//临时中转函数
function obj(o) { 
    function F() {} 
    F.prototype = o; 
    return new F(); 
}
//寄生函数
function create(box,desk){
    var f = obj(box.prototype);
    f.construtor=desk;
    desk.prototype=f;
}
function Box(name,age){
    this.name=name;
    this.age=age;
}
Box.prototype.run=function(){
    return this.name + " " + this.age +  " running..."
}
function Desk(name,age){
    Box.call(this,name,age);
}
//通过寄生组合继承来实现
create(Box,Desk);//替代Desk.prototype=new Box();
var desk = new Desk('Lee',100);
alert(desk.run());

你可能感兴趣的:(javascript)