首页 > Web开发 > 详细

js高程3--面向对象的程序设计--创建对象

时间:2019-08-17 19:03:36      阅读:88      评论:0      收藏:0      [点我收藏+]

创建对象


js高程3--第6章面向对象的程序设计--第二节创建对象,批量创建对象有很多种模式,每一种模式都有自己的优点与缺点,搞清楚它们出现的历史原因,优缺点,我们才能使用的更加游刃有余!

本片文章并没有将细节的挖的特别深,重点关注的是每种模式的优缺点,怎么形成的。细节会另写博客总结。

正文开始

我们都知道创建单个对象有两种方法,构造函数字面量的形式。

var obj = new Object(); //构造函数
var obj1 = {};          //字面量 

如果我们想要创建多个对象这两种形式就有明显的缺点:每一个新对象都要手写生成,会产生大量重复的代码!
为了解决这个问题,聪明的程序员开始使用工厂模式

工厂模式

用函数来封装以特定接口创建对象的细节

function createPerson(name,age){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sayName = function(){
        alert(this.name);
    }
    return obj;
}
var zhangsan = creatPerson('zhangsan',18);

优点:解决了创建多个相似对象,代码重复问题
缺点:没有解决对象识别问题,即不知道当前对象的类型,一直都是Object类型
随着javascript的发展,又一个新的模式出现了!

构造函数模式

ECMAScript中的构造函数可以用来创建特定类型的对象。
类似Object,Array这种原生的构造函数,我们可以使用new Object(),new Array()来创建对象类型或者数组类型的对象。
同样我们可以构建自定义类型的构造函数,定义自定义类型的属性和方法。

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert(this.name);
    }
}

var zhangsan = new Person('zhangsan',18);

优点:可以使用instanceof,isPrototypeOf用来识别对象的类型
缺点 :每个方法都要在每个实例上重新创建一遍,浪费内存。
有人觉得既然实例的sayName函数的作用相同,就没有必要提前把函数绑定到构造函数中,可以这样

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = sayName;
}
function sayName(){     //变为全局函数
    alert(this.name);
}
var zhangsan = new Person('zhangsan',18);

看似很好的解决了每次都重新创建的问题,但是这样又延伸出一个新的让人无法接受的问题,全局变量污染,这样根本没有封装性可言。
very lucky!这些问题都可以通过原型模式解决。

原型模式

function Person(){}

Person.prototype.name = 'zhang';
Person.prototype.age = 18;
Person.prototype.sayName = function(){
    alert(this.name);
}

这样每一个实例的__proto__属性引用Person的原型属性地址,共用一个原型对象。
但是上面的写法每次添加属性都要Person.prorotype很麻烦,可以使用下面字面量的形式

function Person(){}

Person.prototype = {
    name:'zhang',
    age:18,
    sayName:function(){
        alert(this.name);
    }
}

var zhang = new Person();

但是字面量的写法会切断原型链,尽管这里使用instanceofisPrototypeOf仍然可以判断实例的类型,
但是我们通过zhang.constructor得到的是Object,而不再是Person了,因为我们改变了Person的原型属性默认的地址,新地址constructor属性是不存在的。
所以为了是原型链完整,可以使用下面的形式

function Person(){}

// 也可在这里直接添加constructor,
// 只不过constructor属性特征跟默认的(不可枚举)是不一样的
Person.prototype = {
    name:'zhang',
    age:18,
    sayName:function(){
        alert(this.name);
    }
}

// 手动添加构造函数属性,保证原型链完整,
Object.defineProperty(Person.prototype,'constructor',{
    value:Person,
    enumerable:false,
})

var zhang = new Person();

但是,这样还是会有问题,所有的实例都是一样的属性和方法,没有私人属性和方法,这当然不是我们想要的。

解决这个问题很简单,把构造函数模式原型模式混合使用

function Person(name,age){
    this.name = name;
    this.age = age;
}

Person.prototype = {
    sayName:function(){
        alert(this.name);
    }
}
var zhang = new Person();

// 手动添加构造函数属性,保证原型链完整,
Object.defineProperty(Person.prototype,'constructor',{
    value:Person,
    enumerable:false,
})

Perfect!!!这种组合构造函数和原型模式是目前使用最为广泛的模式,可以说是创建自定义类型的默认模式了。
但是,-_-,哈哈 心中一万只神兽,竟然还有但是!!!
严格面向对象的语言使用者看见这种形式会非常懵逼,说好的封装,竟然不把所有信息封装在构造函数里面,竟然还有独立的原型,wtf!
程序员嘛,多少会有点的强迫症。

动态原型模式

这个模式非常的巧妙,第一次看见非常的兴奋。

function Person(name,age){
    this.name = name;
    this.age = age;
    // 第一次初始化构造函数的时候执行,之后不再执行
    // 这里的属性判断随便一个原型属性都可以
    if(typeof this.sayName != 'function'){
        Person.prototype = {
            sayName:function(){
                alert(this.name);
            }
        }
    }
}

var zhang = new Person();

当得了当~~~,如果不加这个判断,每次实例化都执行一遍原型是不现实的,加个判断完美解决了这个问题,这种解决问题的思想是值得非常借鉴的,可以应用到我们的实际开发中。

寄生构造函数模式

还没有搞明白,待续

稳妥构造函数模式

还没有搞明白,待续

补充

要真正搞清楚对象这一块,需要清楚一下几个知识点:
Object
instanceof
getPrototypeOf
isPrototypeOf
hasOwnProperty
defineProperty
in
本人能力有限,理解的不对的欢迎指正!

js高程3--面向对象的程序设计--创建对象

原文:https://www.cnblogs.com/zhanghaoqi/p/11369727.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!