面向对象的编程---创建对象

一.理解对象

    面向对象的语言有一个标志,那就是它们都有类的概念,通过类可以创建多个含有相同属性和方法的对象。ECMAScript中没有类的概念,所以它的对象和基于类的语言中的对象有所不同。
    ECMAScript中对于对象的定义:'无序属性的集合,其属性可以包含基本值,对象或函数'。严格来说,对象是一组没有特定顺序的值。对象的每个属性和方法都有一个名字,每个名字映射的就是一个值。

二.创建对象

最为原始的创建一个对象,然后给这个对象新建属性和方法。
var box = new Object();                 //创建一个Object对象
    box.name = 'hcd';                       //创建一个name属性并赋值
    box.age = 24;                           //创建一个age属性并赋值
    box.run = function () {                 //创建一个run()方法并返回值
        return this.name + this.age + '运行中...';
    };
    console.log(box.run());                     //hcd24运行中...,输出属性和方法的值
    上面创建了一个对象,并且创建属性和方法,在run()方法里的this,就是代表box对象本身。这种是JavaScript创建对象最基本的方法,但有个缺点,想创建一个类似的对象,就会产生大量的代码。
    var box2 = box;                         //得到box的引用
    box2.name = 'Jack';                     //直接改变了name属性
    console.log(box2.run());                //Jack,用box.run()发现name也改变了
    var box2 = new Object();
    box2.name = 'Jack';
    box2.age = 200;
    box2.run = function () {
        return this.name + this.age + '运行中...';
    };
    alert(box2.run());                      //这样才避免和box混淆,从而保持独立
为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。

1.工厂模式

    function createPerson(name,age,job){
        var o = new  Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.say = function(){
            console.log(this.name)
        };
        return o;
    }
    var person1 = createPerson("hcd",10,"it");
    var person2 = createPerson("james",23,"footer");
    console.log(person1);                   //Object {name: "hcd", age: 10, job: "it"}
    console.log(person2);                   //Object {name: "james", age: 23, job: "footer"}
    createPerson函数可以接受3个含所有必要信息的参数,通过无数次的调用产生无数个函数听筒属性和方法的对象。工厂模式虽然解决了创建相似对象的的问题,但是没有解决对象识别的问题(怎么知道一个对象的类型)

2.构造函数

ECMAScript中的构造函数可以创建特定类型的对象。
    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.say = function(){
            console.log(this.name)
        }
    }
    var person1 = new Person("hcd",20,"work");
    var person2 = new Person("james",24,"footer");
    person1.say();                        //hcd
Person函数代替了createPerson函数,除此外还有不同:
    1.没有显示的创建对象
    2.直接建属性和方法赋值给this对象
    3.没有return语句
注意:构造函数的头字母我们习惯性大写
要创建Person的新实例,必须使用new操作符。这种调用方式经历下面4种过程:
    1.当使用了构造函数,并且new 构造函数(),那么就后台执行了new Object();
    2.将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this就代表new Object()出来的对象。
    3.执行构造函数内的代码;
    4.返回新对象(后台直接返回)。
上面的两个实例person1和person2分别保存着一个不同的Person实例,这两个实例有一个constructor(构造函数)属性,该属性指向Person构造函数。
console.log(person1.constructor);
console.log(person2.constructor);

1.将构造函数当作函数

    构造函数也是函数,只是调用的方法不同。构造函数通过new来调用,也可以通过别的方法调用。

    //当做构造函数使用
    var person1 = new Person("hcd",20,"work");
    person1.say();

    //作为普通函数调用
    Person("name","age");
    window.say();
    //在全局环境下调用函数时,this指向Global对象(浏览器中就是window对象),因此可以用window调用say()方法。


    //在另外一个对象的作用域中调用,this指向o对象
    var o = new Object();
    Person.call(o,"namep","age");
    o.say()

2.构造函数的问题

    每个方法都要在每一个实例中创建一遍,也就是说比如上面的例子,2个实例,就创建了2个say方法。
    解决方法:将say方法移到构造函数的外面
        function Person(name,age){
             this.name = name;
             this.age = age;
             this.say = say;
        }
        function say(){
            alert(this.name)
        }
        var person1 = new Person("name","age");
        person1.say();
    在Person中调用了全局的函数say,这样一来不管创建多少个实例他们的say方法都将指向一个全局函数。
    但是还有问题,就是如果有多个方法的话,就要在全局定义多个方法,完全没有了封装的含义。

3.原型模式

创建每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象。而这个独享包含所有的共享属性和方法。
也就是说,每一个实例都有原型对象,并且可以共享原型对象的属性和方法。
    function Person(){}
     Person.prototype.name = "hcd";
     Person.prototype.say = function(){
        alert(this.name)
     }
     var person1 = new Person();
     person1.say();//hcd
     var person2 = new Person();
     person2.say();//hcd
     alert(person1.say==person2.say)//true

1.理解原型

    Person   
        有prototype属性,指向原型对象
    原型对象
        有constructor属性,指向构造函数Person
    person1
        实例没有prototype属性,但是有我们无法访问的内部属性[[prototype]]属性,指向原型对象。并且可以直接使用原型对象的属性和方法。
        alert(Person.prototype.isPrototypeOf(person1))//true,A.isPrototypeOf(B)表示A是B的原型
        还有一个Object.getPrototype();可以返回[[prototype]]的值
        alert(Object.getPrototypeOf(person1) == Person.prototype)//true
    在我们读取某一实例的属性或方法时,都会先在该实例下寻找,有则返回属性的值或方法。如果没有就会找实例的原型对象,有则返回。
        实例不能更改原型的属性和方法
            function Person(){}
            Person.prototype.name = "hcd";
            Person.prototype.say = function(){
                alert(this.name)
            }
            var person1 = new Person();
            var person2 = new Person();
            person1.prototype.name = "pppp";//会报错的,Cannot set property 'name' of undefined
            alert(person1.name)
        实例只能增删改自己的属性和方法
            function Person(){}
             Person.prototype.name = "hcd";
             Person.prototype.say = function(){
                alert(this.name)
             }
             var person1 = new Person();
             var person2 = new Person();
             person1.name = "ppp";   
             alert(person1.name)//"ppp",来自perosn1
             alert(person2.name)//"hcd",来自原型对象
             delete person1.name;//删除person1的name属性
             alert(person1.name)//"hcd",来自原型对象
             alert(person1.hasOwnProperty("name"))//false
        简写原型对象
             function Person(){}
             Person.prototype={
                name:"hcd",
                age:29,
                say:function(){
                    alert(this.name)
                }
             }
             var person1 = new Person();
             person1.name = "ppp";   //"hcd"
        可以随时改原型对象的属性但是要注意顺序
            function Person(){}
             Person.prototype={
                name:"hcd",
                age:29,
                say:function(){
                    alert(this.name)
                }
             }
             var person1 = new Person();
             alert(person1.name);//"hcd"
             Person.prototype={
                like:"person"
             }
            var person2 = new Person();
            alert(person2.name);            //undefined
            alert(person2.like);            //"person"      
            alert(person1.name);            //"hcd"
            alert(person1.like);            //"hcd"
            因为Person.prototype已经没有了name,age,say属性和方法,为重新定义的like属性。但是person1的原型对象还是原来的,是有name属性的
    问题:
        所有的实例的属性在默认情况下是一样的,
            function Person(){}
             Person.prototype={
                name:"hcd",
                age:29,
                friends:["a"],
                say:function(){
                    alert(this.name)
                }
             }
             var person1 = new Person();         
             var person2 = new Person();
             person1.friends.push("p");
             console.log(person2.friends)//["a","p"]
             因为共享资源,所以导致向person1.friends里面push,person2.friends会受到影响

4.组合使用原型和构造函数

    function Person(name,age){
            this.name = name;
            this.age = age;
        }
        Person.prototype = {
            say:function(){
                alert(this.name);
            }
        }
        var person1 = new Person("hcd",20);
        person1.say();
    这样一来,每一个实例的属性是自己的,不会互相影响而方法是公用的,不用每一个实例创建一个方法

5.动态原型模式

其他oo语言开发者看到独立的构造函数和原型时会非常的困惑,动态原型模式就是为解决这个问题的。动态原型模式将所有的信息都封装在了构造函数中,只有在需要时才会初始化原型。
    function Person(name,age){
        this.name = name;
        this.age = age;
        if(typeof this.say != "function"){
            Person.prototype.say = function(){
                console.log(this.name);
            };
        }
    }
    var person1 = new Person('hcd',29);
    person1.say();
从上面的代码中可以看出只有在第一次调用构造函数时才会在原型中创建say方法。

6.寄生构造模式

以上讲解了各种方式对象创建的方法,如果这几种方式都不能满足需求,可以使用一开始那种模式:寄生构造函数。
    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());

7.稳妥构造函数

在一些安全的环境中,比如禁止使用this和new,这里的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:稳妥构造函数和寄生类似。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值