zoukankan      html  css  js  c++  java
  • js的创建对象的方法

    第一种:工厂模式

    根据接收参数返回,包含参数的对象

    优点:解决创建多个对象的问题

    缺点:没法判断对象的类型

                function createPerson(name,age,job){
                    var o = new Object();
                    o.name = name;
                    o.age = age;
                    o.job = job;
                    o.sayName = function(){
                        alert(this.name);
                    }
                    return o;
                }

    第二种:构造函数模式

                function Person(name,age,job){
                    this.name = name;
                    this.age = age;
                    this.job = job;
                    this.sayName = function(){
                        alert(this.name);
                    }
                }
                let p1 = new Person('bzw',20,'stu');
                console.log(p1 instanceof Person);//true
                console.log(p1.hasOwnProperty('name'));//true

    构造函数模式与工厂模式区别:

    1.没有显式的创建函数

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

    3.没有return语句

    构造函数定义:任何函数,只要可以通过new操作符来调用,那它就可以作为构造函数

    以这种方式调用构造函数会经理以下几个步骤:

    1.创建新对象

    2.将作用域赋值给新对象

    3.执行构造函数的代码

    4.返回新对象

    缺点:每个方法都要在实例化的对象上面重新创建一遍

    第三种:原型模式(重点)

      想要知道原型模式,必须得知道什么叫做原型,每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象包括所有实例可以共享的属性和方法。

    此外还需要知道什么叫做原型对象,还是那句话,只要创建一个函数,就会有一个prototype(原型)属性,而这个prototype恰恰指向这个函数的原型对象,这个原型对象包括所有实例可以共享的属性和方法。

    每创建一个实例,这个实例会有一个[[prototype]]指针,专门指向构造函数的原型对象,每个原型对象又会有一个constuctor属性,它是一个指向prototype属性所在函数的指针

           function Person1(){
                    
                }
                Person1.prototype.name = 'bzw';
                Person1.prototype.age = 20;
                Person1.prototype.job = 'stud';
                console.log(Person.prototype);//{constructor: ƒ}
                console.log(Person.prototype.constructor);//指向Person函数
                let p2 = new Person1();
                let p3 = new Person1();
                console.log(Person1.prototype.isPrototypeOf(p2));//true
                console.log(Person1.prototype.isPrototypeOf(p3));//true
                
                console.log(Object.getPrototypeOf(p2) === Person1.prototype);//true
                console.log(p2.hasOwnProperty('name'));//false

      in与hasOwnProperty的区别:
    in会在实例化对象里面找有没有这个属性,如果没有就去它的原型里面去找,hasOwnProperty只会在实例化对象里去找有没有这个属性

                console.log('name' in p2);//true
                console.log(p2.hasOwnProperty('name'));//false                            

      我们可以写个函数专门用来判断某个对象只有原型里面有某个属性

                function hasPrototypeProperty(obj,key){
                    return !obj.hasOwnProperty(key) && (key in obj);
                }
                console.log(hasPrototypeProperty(p2,'name'));//true
                p2.name = 10;
                console.log(hasPrototypeProperty(p2,'name'));//false

      给原型添加方法每次都需要写一个Person.prototype这样写太麻烦,我们可以下面这样写

                function Person2(){
                    
                }
                Person2.prototype = {
                    name:'bzw',
                    age:20,
                    job:'stu'
                }

      这样写挺方便但是,有个缺点就是原型对象的constructor会指向Object,这不是我们想看到的

                 console.log(Person2.prototype.constructor);//Object()
            console.log(Object.keys(Person2.prototype))//["name", "age", "job"]

      我们可以指定它的constructor,这样就更改过来了,

                Person2.prototype = {
                    constructor:Person2,//constructor的enumerable默认为false不可遍历,但是这种方法会让它变为true,即可以遍历
                    name:'bzw',
                    age:20,
                    job:'stu'
                }

      这里可能大家有个问题,为什么这样要指定constructor了,因为我们前面说过,一个构造函数的constructor需要指向prototype属性的所在的函数

      我们可以打印一下的Person2.prototype.constructor

     console.log(Person2.prototype.constructor);//person2()

      结果如下:

      或者可以用下图来表达上述的关系

      

      当然这样写还是有点弊端

                //Object.keys()会获取对象中所有可以遍历的属性
                console.log(Object.keys(Person2.prototype))//["constructor", "name", "age", "job"]

      constructor竟然可以被遍历出来,这是不对的,我们发现这个constructor可以遍历,那么上面的方法就不是特别好,我们可以用下下面的方法,即指定了Person2的constructor指向问题,又解决constructor可以遍历的问题,可谓一举两得

                Object.defineProperty(Person2.prototype,'constructor',{
                    enumerable:false,
                    value:Person2
                });
                console.log(Object.keys(Person2.prototype))//["name", "age", "job"]
                console.log(Person2.prototype.constructor);//Person2();

      原型的动态性

      我们可以利用原型的动态性给原型添加属性和方法

            function Person3(){
                    
                }
                let friend = new Person3();
                Person3.prototype.a = 1;
                console.log(friend.a);//1 即使实例已经创建好了, 但是当我给原型添加属性的时候,实例还是原型后面添加的方法
                Person3.prototype = {
                    constructor:Person3,
                    name:'bzw'
                }
                //实例化对象里面会有一个指针指向原型,而不是指向构造函数,这里把原型修改为另一个对象,就等于切断了最初原型与构造函数之间的关系
                console.log(friend instanceof Person3)//false
                console.log(friend.name)//undefined

    下图可以反映上述的情况

      原型模式的缺点

                function Person4(){
                    
                }
                Person4.prototype={
                    constructor:Person4,
                    name:[4],
                }
                let p4 = new Person4();
                console.log(p4.name);//[4]
                p4.name.push(5);
                let p5 = new Person4();
                console.log(p4.name,p5.name);//[4, 5],[4, 5]

      我们发现实例竟然可以对更改原型的值,这个不太好,于是就有其他的对象创建方法

    第四种:组合构造函数和原型模式

           function Person5(name,age,job){
                    this.name = name;
                    this.age = age;
                    this.job = job;
                    this.friend = ['tom','jerry'];
                   
                }
                Person5.prototype={
                    constructor:Person5,
                    sayName:function(){
                        console.log(this.name);
                    }
                }
                let p6 = new Person5('bzw',20,'stu');
                let p7 = new Person5('bzw1',19,'stu1');
                p6.friend.push('bob');
                console.log(p6.friend);// ["tom", "jerry", "bob"]
                console.log(p7.friend);//["tom", "jerry"]

    第五种:动态原型

                function Person6(name,age,job){
                    this.name = name;
                    this.age = age;
                    this.job = job;
                    this.friend = [];
                    if(typeof this.sayName != 'function'){
                        Person6.prototype.sayName = function(){
                            return this.name;
                        }
                    }
                }
                let p8 = new Person6('bzw2',20,'stu');
                p8.friend.push('v');
                console.log(p8.sayName())//bzw2
                let p9 = new Person6('bzw3',21,'stu');
                console.log(p8.friend,p9.friend);//["v"] []

      使用动态原型不能使用对象字面量来重写原型,否则会切断构造函数与原先原型的关系,也会切断实例与新原型的关系

    第六种:寄生构造函数模式(和工厂模式很像)

    特点:返回的对象与构造函数或者构造函数的原型属性之间没有关系,也就是说,构造函数返回的对象与在构造函数在外部创建的对象没有什么不同

                function Person7(name,age,job){
                    let o = new Object();
                    o.name = name;
                    o.age = age;
                    o.job = job;
                    o.sayName = function(){
                        return this.name;
                    }
                    return o;
                }
                let p10 = new Person7('bzw1',20,'stu');
                console.log(p10.sayName());//bzw1

    第七种:稳妥构造函数模式(适合于某些安全执行环境下,这些安全环境会禁用this和new)

              function Person8(name,age,job){
                    let o = new Object();
                    //定义私有变量和函数
                    let sur = '姓名:';
                    let sum = function(){
                        return sur+name;
                    }
                    o.sayName = function(){
                        return sum();
                    }
                    return o;
                }
                let p11 = Person8('bzw');
                console.log(p11.sayName(),sur);//姓名:bzw,sur is not defined

      

  • 相关阅读:
    14、python基础
    13、Python入门
    12、运算符、流程控制
    10、Linux(六)
    Windows 分层窗口 桌面上透明 Direct3D
    Windows 进程间通信 共享内存
    Linux 库的使用
    FFmpeg 命令行
    FFmpeg 摄像头采集
    FFmpeg input与output 函数流程
  • 原文地址:https://www.cnblogs.com/MySweetheart/p/13392352.html
Copyright © 2011-2022 走看看