• 面向对象与原型


    实例化方法:

    创建一个对象,然后给这个对象新建属性和方法。

    var box = new Object(); //创建一个Object对象

    box.name = 'Lee'; //创建一个name属性并赋值

    box.age = 100; //创建一个age属性并赋值

    box.run = function () { //创建一个run()方法并返回值

    return this.name + this.age + '运行中...';

    };

    alert(box.run()); //输出属性和方法的值

    上面创建了一个对象,并且创建属性和方法,在run()方法里的this,就是代表box对象本身。这种是JavaScript创建对象最基本的方法,但有个缺点,想创建一个类似的对象,就会产生大量的代码。

    工厂模式:

    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('Lee', 100); //第一个实例

    var box2 = createObject('Jack', 200); //第二个实例

    alert(box1.run());

    alert(box2.run()); //保持独立

    工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,因为根本无法搞清楚他们到底是哪个对象的实例。

    alert(typeof box1); //Object

    alert(box1 instanceof Object); //true

    构造函数模式:

    function Box(name, age) { //构造函数模式

    this.name = name;

    this.age = age;

    this.run = function () {

    return this.name + this.age + '运行中...';

    };

    }

    var box1 = new Box('Lee', 100); //new Box()即可

    var box2 = new Box('Jack', 200);

    alert(box1.run());

    alert(box1 instanceof Box); //很清晰的识别他从属于Box

    使用构造函数的方法,即解决了重复实例化的问题,又解决了对象识别的问题,但问题是,这里并没有new Object(),为什么可以实例化Box(),这个是哪里来的呢?

    使用了构造函数的方法,和使用工厂模式的方法他们不同之处如下:

    1.构造函数方法没有显示的创建对象(new Object())

    2.直接将属性和方法赋值给this对象;

    3.没有renturn语句。

    构造函数的方法有一些规范:

    1.函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和普通函数)

    2.通过构造函数创建对象,必须使用new运算符。

     原型:

    prototype通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法

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

    PSIE浏览器在脚本访问__proto__会不能识别,火狐和谷歌浏览器及其他某些浏览器均能识别。虽然可以输出,但无法获取内部信息。

    alert(box1.__proto__); //[object Object]

    判断一个对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试。

    alert(Box.prototype.isPrototypeOf(box)); //只要实例化对象,即都会指向

    in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

    alert('name' in box); //true,存在实例中或原型中

    原型模式的执行流程:

    1.先查找构造函数实例里的属性或方法,如果有,立刻返回;

    2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;

    图解

    原型模式创建对象也有自己的缺点,它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优点,那就是共享。

    原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以。但如果属性包含引用类型,就存在一定的问题:

    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()); //共享带来的麻烦,也有'哥哥'

    PS:数据共享的缘故,导致很多开发者放弃使用原型,因为每次实例化出的数据需要保留自己的特性,而不能共享。

    为了解决构造传参和共享问题,可以组合构造函数+原型模式

    function Box(name, age) { //不共享的使用构造函数

    this.name = name;

    this.age = age;

    this. family = ['父亲', '母亲', '妹妹'];

    };

    Box.prototype = { //共享的使用原型模式

    constructor : Box,

    run : function () {

    return this.name + this.age + this.family;

    }

    };

    PS:这种混合模式很好的解决了传参和引用共享的大难题。是创建对象比较好的方法。

    原型模式,不管你是否调用了原型中的共享方法,它都会初始化原型中的方法,并且在声明一个对象时,构造函数+原型部分让人感觉又很怪异,最好就是把构造函数和原型封装到一起。为了解决这个问题,我们可以使用动态原型模式

    function Box(name ,age) { //将所有信息封装到函数体内

    this.name = name;

    this.age = age;

    if (typeof this.run != 'function') { //仅在第一次调用的初始化

    Box.prototype.run = function () {

    return this.name + this.age + '运行中...';

    };

    }

    }

    var box = new Box('Lee', 100);

    alert(box.run());

    当第一次调用构造函数时,run()方法发现不存在,然后初始化原型。当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会再初始化了。这样及得到了封装,又实现了原型方法共享,并且属性都保持独立。

    if (typeof this.run != 'function') {

    alert('第一次初始化'); //测试用

    Box.prototype.run = function () {

    return this.name + this.age + '运行中...';

    };

    }

    var box = new Box('Lee', 100); //第一次创建对象

    alert(box.run()); //第一次调用

    alert(box.run()); //第二次调用

    var box2 = new Box('Jack', 200); //第二次创建对象

    alert(box2.run());

    alert(box2.run());

    PS:使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系。

    以上讲解了各种方式对象创建的方法,如果这几种方式都不能满足需求,可以使用一开始那种模式:寄生构造函数。

    function Box(name, age) {

    var obj = new Object();

    obj.name = name;

    obj.age = age;

    obj.run = function () {

    return this.name + this.age + '运行中...';

    };

    return obj;

    }

    寄生构造函数,其实就是工厂模式+构造函数模式。这种模式比较通用,但不能确定对象关系,所以,在可以使用之前所说的模式时,不建议使用此模式。

    在什么情况下使用寄生构造函数比较合适呢?假设要创建一个具有额外方法的引用类型。由于之前说明不建议直接String.prototype.addstring,可以通过寄生构造的方式添加。

    function myString(string) {

    var str = new String(string);

    str.addstring = function () {

    return this + ',被添加了!';

    };

    return str;

    }

    var box = new myString('Lee'); //比直接在引用原型添加要繁琐好多

    alert(box.addstring());

    在一些安全的环境中,比如禁止使用thisnew,这里的this是构造函数里不使用this,这里的new是在外部实例化构造函数时不使用new。这种创建方式叫做稳妥构造函数。

    function Box(name , age) {

    var obj = new Object();

    obj.run = function () {

    return name + age + '运行中...'; //直接打印参数即可

    };

    return obj;

    }

    var box = Box('Lee', 100); //直接调用函数

    alert(box.run());

    PS:稳妥构造函数和寄生类似。

  • 相关阅读:
    SQL Server 性能优化之——T-SQL TVF和标量函数
    SQL Server 性能优化之——T-SQL 临时表、表变量、UNION
    Posix消息队列实现机制
    进程间通信基础知识整理
    进程间通信——FIFO(多个客户进程,一个服务进程)
    VMware+CentOS7+jdk1.7+hadoop2.4.1
    Java并发编程
    java并发编程
    读书笔记——Java IO
    读书笔记——异常
  • 原文地址:https://www.cnblogs.com/sunnychen/p/6160202.html
Copyright © 2020-2023  润新知