面向对象与原型一

时间:2022-06-23 18:59:15

面向对象和原型

创建对象

var box=new Object();
box.name='chen';
box.age=26;
box.run=function() {
    return this.name+this.age;
}
alert(box.run());

var box2=new Object();
box2.name='jack';
box2.age=24;
box2.run=function(){
    return this.name+this.age;
}
alert(box2.run());

在以上情景模式中存在多个类似对象声明的问题

工厂模式

工厂模式优点:就是为了解决实例化对象产生大量重复的问题。创建一个集中实例化函数

function createObject(name,age){
    var obj=new Object();
    obj.name=name;
    obj.age=age;
    obj.run=function(){
        return this.name+this.age;
    }
    return obj;
}
var box1=createObject('chen',26);
var box2=createObject('jack',24);
alert(box1.run());//创建第一个实例化对象
alert(box2.run());//创建第二个实例化对象

在创建一个createObject2()

function createObject2(name,age){
    var obj=new Object();
    obj.name=name;
    obj.age=age;
    obj.run=function(){
        return this.name+this.age;
    }
    return obj;
}
alert(box1 instanceof Object);//true
alert(box2 instanceof Object);//true
alert(box3 instanceof Object);//true

box3box1box2明显属于不同的函数不同的对象,但又都是Object的对象,这样一来就无法区分谁到底是谁的对象

工厂模式缺点:无法区分谁到底是哪个对象的实例

构造函数

构造函数优点既解决重复实例化的问题也解决对象识别问题

构造函数与工厂函数的不同点:
* 造函数方法没有显示的创建对象(new Object())
* 直接将属性和方法赋值给 this对象没有return语句

然通过构造函数可以创建对象,那么这个对象是哪里来的, new Object()在什么地方
执行了?执行的过程如下

  • 1、当使用了构造函数并且new构造函数,那么后台自动执行了new Object()
  • 2、将构造函数作用域给新对象创建出的对象(new Object()。而函数体内的this就代表new Object出来的对象
  • 3、执行构造函数内的代码
  • 4、返回新对象
function Box(name,age){
    this.name=name;
    this.age=age;
    this.run=function(){
        return this.name+this.age;
    }
}
function Desk(name,age){
    this.name=name;
    this.age=age;
    this.run=function(){
        return this.name+this.age;
    }
}
var box1=new Box('chen',26);
var box2=new Box('jack',24);
var box3=new Desk('kkk',300);
alert(box1.run());//chen26
alert(box2.run());//jack24
alert(box3.run());//kkk300

alert(box1 instanceof Object);//true
alert(box1 instanceof Box);//true
alert(box2 instanceof Box);//true
alert(box3 instanceof Box);//false 识别出box3不属于Box而应该属于Desk
alert(box3 instanceof Desk);//true 证明box3属于Desk

构造函数和普通函数的唯一区别, 就是他们调用的方式不同。 只不过, 构造函数也是函
数,必须用new运算符来调用,用普通函数调用无效,否则就是普通函数

为了方便后面学习,这里探讨下两个实例化对象的run方法是否相等

function Box(name,age){
    this.name=name;
    this.age=age;
    this.run=function(){
        return this.name+this.age;
    }
}

var box1=new Box('chen',26);//实例化后地址是1
var box2=new Box('chen',26);//实例化后地址是2
alert(box1.name==box2.name);//true
alert(box1.age==box2.age);//true
alert(box1.run()==box2.run());//true 构造函数体内的方法的值是相等的
alert(box1.run==box2.run);//false 因为他们比较的是引用地址

构造函数缺点两个实例化后的属性或方法不相等

若果想要引用地址也是一致可以这样(通过构造函数外绑定同一个函数的方法来保证引用地址的一致性)

function Box(name,age){
    this.name=name;
    this.age=age;
    this.run=other
}
function other(){
    return this.name+this.age;
}

var box1=new Box('chen',26);//实例化后地址是1
var box2=new Box('chen',26);//实例化后地址是2
alert(box1.run()==box2.run());//true
alert(box1.run==box2.run);//true

原型

每创建一个函数都有一个prototype属性,这个属性是个对象,她的用途是包含可以有特定的所有实例共享的属性和方法,也就是说不洗在构造函数中定义对象信息,而是直接将这些信息添加到原型中

function Box(){}
Box.prototype.name='Lee';//原型属性
Box.prototype.age=100;//原型属性
Box.prototype.run=function(){//原型方法
    return this.name+this.age;
}
var box1=new Box();
var box2=new Box();
alert(box1.run==box2.run);//true 此时实例化的地址是一样的

如果是实例化方法,不同的实例化,他们的方法地址是不一样的,是唯一的
如果是原型方法,那么他们的地址是共享的
构造函数方法
面向对象与原型一
原型模式方法
面向对象与原型一

alert(box1.prototype);//undefined
alert(box1.__proto__);//[object,object]

在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。proto属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor。通过这两个属性,就可以访问到原型里的属性和方法了

作用是被原型指针定位,然后得到构造函数本身,其实就是构造函数对象对应的原型对象
alert(box1.constructor);//function  Box(){}

isPrototypeOf()用来判断一个对象是否指向了该构造函数的原型对象
只要用new实例化对象,即都会指向原型

alert(Box.prototype.isPrototypeOf(box1));//true

//另外一个例子
var obj=new Object();
alert(Object.prototype.isPrototypeOf(obj));//true

原型模式的执行流程:
1、先查找构造函数实例中里的属性和方法,如果有,立刻返回;
2、如果构造函数里没有,则去它的原型对象里找,如果有,则返回。

可以用delete删除实例中的属性

hasOwnProperty()判断属性是在构造函数的实例里,还是在原型里

alert(box1.hasOwnProperty('name'));//实例里有返回true,否则返回false

in操作符判断属性是在原型里或者在实例中,无论属性是存在对象中还是存在原型中只要存在(且不管实例属性和原型属性是否存在,也是就该属性可以为空)就返回true,两边都没有就返回false

alert('name' in box);//true,存在实例中或原型中
function isProperty(obj,proterty){//封装函数判断是否在原型中存在
    return !obj.hasOwnProperty(proterty)&&proterty in obj;
}

构造函数实例属性和原型属性示意图
面向对象与原型一
为了让属性和方法更好的体现封装效果,并减少不必要的输入,原型的创建可以使用字面量的方法:

function Box(){}
Box.prototype={
    name:'Lee',
    age:100,
    run:function(){
        return this.name+this.age;
    }
}
//其中Box.prototype={}相当于new Object()
var box=new Box();
alert(box.constructor);//function Object(){}

使用构造函数创建原型对象和使用字面量创建对象在使用上基本相同,但还是有一些区别,字面量创建的方式使用constructor属性不会指向实例,而会指向Object,构造函数创建的方式则相反

function Box(){}
Box.prototype.name='Lee';//原型属性
Box.prototype.age=100;//原型属性
Box.prototype.run=function(){//原型方法
    return this.name+this.age;
}
var box=new Box();
alert(box.constructor);//function Box(){}

字面量方法为什么constructor会指向object
因为其中Box.prototype={}相当于创建了一个新对象,新对象的constructor重写了Box原来的constructor因此会指向新对象。

//重写原型,以前的name属性和run方法都被覆盖
Box.prototype={
    age:200
}

原型的声明是有先后顺序的,所以重写原型会切断之前的原型(覆盖之前所有的原型对象的属性和方法)

原型模式缺点:原型模式省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的而原型最大的有点也是它最大的缺点,就是共享。
原型中所有的属性被很多实例共享,共享对函数非常合适,对于包含基本值的属性也还可以。但如果属性包含引用类型,就存在一定的问题

function Box() {};
Box.prototype = {
    constructor : Box,
    name : 'Lee',
    age : 100,
    family : ['父亲', '母亲', '妹妹'],            //添加了一个数组属性
    run : function () {
        return this.name + this.age + this.family;
    }
};
var box1 = new Box();
box1.family.push('哥哥');//在实例中添加'哥哥'
alert(box1.run());

var box2 = new Box();
alert(box2.run());//共享带来的麻烦,也有'哥哥'了