JavaScript面向对象编程

来源:互联网 发布:linux打开根目录 编辑:程序博客网 时间:2024/05/29 16:05

创建对象

创建对象可以利用Object构造函数创建新对象

var stu =  new Object(); 

或者 用字面量去创建对象

var stu = {}; 

这两种方式存在问题是会产生大量重复代码

因此,有了工厂模式
工厂模式:其实就是定义一个函数,函数中声明了一个对象,这个对象的相关属性是由这个函数传进来的参数来进行初始化。最后将这个对象作为这个函数的返回值返回。

function Student(name, age){    var stu = new Object();    stu.name = name;    stu.age = age;    stu.hello = function(){        console.log("Hello,"+stu.name);    }    return stu;}var Jack = Student('Jack',22);

问题是,没有解决对象识别的问题,也就是说没办法知道一个对象是什么类型的。

因此,有了构造函数模式
构造函数模式:也是一个函数,但是这个函数与工厂模式不同是,在这个函数内部没有显示创建对象,直接把属性都赋给了this对象,而this指向将指向将要创建的对象。没有return语句。

function(name,age){    this.name = name;    this.age = age;    this.hello = function (){        console.log("Hello,"+this.name);    }} var Jack = Student('Jack',22);var Max = Student('Max',23);

当用new关键字来调用这个构造函数时,先是创建了一个新对象,将构造函数的作用域赋给新对象(因此this就指向这个新对象),然后返回this所指向的这个对象。如果像普通函数来调用的话,与其他没有定义return的普通函数一样,返回的是undefined。相关属性和方法会挂在对应执行环境的全局对象上。在严格模式下,this指向的是undefined。
构造函数模式的问题在于,构造函数创建新对象的话,构造函数里的方法都会在每个新的对象上重新创建一次。可以验证利用构造函数创建的Jack和Max的hello方法不是同一个。如果单独把这些函数单独拎出来,放到成全局函数,在构造函数中再指向这个全局函数,虽然减少了重复的函数,但散失封装性。

因此,有了原型模式
在每创建一个新函数的时候,就会按照一定规则为其添加一个prototype属性,这个属性是一个指针,指向一个对象,这个对象用途是包含可以由特定类型的所有实例共享的属性和方法。这个原型对象和这个函数都是同时产生的。构造函数的prototype属性指向了构造函数对应的原型对象,这个原型对象的constructor属性指向构造函数。以这个对象为原型对象的对象继承了这个原型对象的的constructor属性,并且这些对象有一个属性[[prototype]](无法访问)或者proto (firefox、chrome、safari)指向的是原型对象。

function Student(){}Student.prototype.name = 'Jack';Student.prototype.age = 23;Student.prototype.hello = function (){    console.log("Hello, "+this.name);}/*上面做法赘余代码很多,更常见的做法如下*/function Student(){} //原本在每创建一个函数的时候就会同时创建它prototype指向的对象 //而这个对象的constructor([[Enumerable]]默认为false)属性会默认指向这个函数Student.prototype = { //这种做法相当于重写了Student的默认对象,其他结果是相同的,但是constructor不再指向Student    constructor:Student,//有需要的话需要重新设定constructor的指向([[Enumerable]]为true)    name:'Jack',    age:23,    hello:function(){        console.log("Hello, "+this.name);    }}//如果想要让constructor的[[Enumerable]]为false,可以用Object.defineProperty()Object.defineProperty(Student.prototype,"constructor",{    enumerable:false,    value:Student});

构造函数和实例对象之间没有直接的连接,这种连接是存在与实例与构造函数的原型对象之间。在访问对象的某个属性时,是从该对象开始沿着原型链向上查找,原型链的终点是null。示意图如下,是截自廖雪峰博客,感觉意思到了就懒得自己画了。
图片是截了廖雪峰博客里的懒得画了

实例可以访问原型的属性和方法,但是不能改写。如果有重名的属性和方法,也只是在实例中创建同名属性或方法,并屏蔽原型中的。如果将实例中这个重名属性重新赋值为null,也不会更改实例的这个属性的指向重新回到原型对象,只用利用delete关键字才可以实现。
原型动态性:可以随时为原型添加属性或方法,这些属性或方法都能在实例中反映出来。

var Mike = new Student();Student.prototype.bye = function(){    console.log("Bye");}Mike.bye(); //bye

但是如果是更改了整个原型的对象,情况就不一样了

function Student(){}var Lyn = new Student();Student.prototype = {    sayHello:function(){        console.log("Hello");    }}Lyn.sayHello();//error,sayHello这个函数没有被定义

原因是Lyn这个实例的[[Protoyepe]]指向的是原来的原型对象,重写原型对象会切断现有原型对象与之前所创建实例之间的联系。

在理解原型的这些内容的时候,我觉得清楚每个部分的指向是很重要的。
原型存在的问题是,原型的所有属性和方法都被实例所共享,虽然对于一般属性值来说,可以在实例中定义同名属性去屏蔽原型中的属性,对于引用类型来说,所有实例都会用同一个引用类型。

function Student(){}Student.prototype = {    constructor:Student,    name:'Lyn',    friends:['Tom','Jack']}var stu1 = new Student();var stu2 = new Student();var stu3 = new Student();stu1.friends=['Rose'];stu2.friends.push('Lily');console.log(stu1.friends); //['Rose']console.log(stu2.friends); //['Tom','Jack','Lily']console.log(stu3.friends); //['Tom','Jack','Lily']

被广泛使用和认可的还是构造函数和原型混合,也就是利用构造函数模式定义实例属性,将实例的属性通过传参的方式进行定义,而原型模型用于定义方法和共享的属性。

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 鱼图 九鱼荷花图 九鱼图壁画 九鱼图十字绣图纸 十字绣九鱼图大全 九鱼荷花图寓意 油画九鱼图 餐厅九鱼图 八鱼图 鱼的图 家和万事兴九鱼图 九鱼图的寓意 玄关画九鱼图 六鱼图 图鱼 九鱼图十字绣图案大全 九鱼图十字绣成品价格 鱼九 九鲤湖风景区 九鲤湖 九鲤湖风景区旅游 莆田九鲤湖 九鲤湖门票 九鲤湖一日游攻略 九鲤湖门票多少钱 福建九鲤湖风景区 九鲤湖一日游旅游攻略 鲤溪 九鹿王 九鹿王男装 九皇山猿王洞价格 九黄山旅游攻略 绵阳九黄山风景区 九月黄山旅游 我有一个九黎壶 九黎壶 奶酪味的你 黎九幕 主角身居九黎血脉激活了蚩尤 伊宫黎歌 唐黎心宫宸全本免费看 聂飞苏黎马晓燕