美文网首页
第二篇 关于对象

第二篇 关于对象

作者: Jokeryang | 来源:发表于2017-06-26 16:40 被阅读0次

    写在前面:这次来说下 JavaScript 中对象的创建,文中提到的方法均较为常用,其他方法请自行百度。

    2.1 创建对象

    2.1.1 工厂模式

    工厂模式是一种较为简单的设计模式,开发人员将创建对象的具体细节封装在一个函数中。

    // 工厂模式
    function F(name, age) {
        let o = {};
        o.name = name;
        o.age = age;
        o.arr = ['a', 1, 2];
        return o
    }
    
    let p1 = F('Devin', 34)
    p1.arr.push(5)
    let p2 = F('Joker', 22)
    p2.arr.push(10)
    console.log(p1.name, p2.name) //Devin Joker
    console.log(p1.age, p2.age)   //34 22
    console.log(p1.arr, p2.arr)   //[ 'a', 1, 2, 5 ] [ 'a', 1, 2, 10 ]
    

    工厂模式解决了创建多个类似对象的问题,但却没有解决对象识别的问题。

    2.1.2 构造器模式

    诸如Object和Array等原生构造器一样,我们可以创建自定义构造器去生成自定义对象。

    // 构造器模式
    function F(name, age) {
        this.name = name;
        this.age = age;
        this.arr = ['a', 1, 2];
    }
    
    let p1 = new F('Devin', 34)
    p1.arr.push(5)
    let p2 = new F('Joker', 22)
    p2.arr.push(10)
    console.log(p1.name, p2.name) //Devin Joker
    console.log(p1.age, p2.age)   //22 22
    console.log(p1.arr, p2.arr)   //[ 'a', 1, 2, 5 ] [ 'a', 1, 2, 10 ]
    

    与工厂模式相比,我们可以发现构造器函数存在以下几点不同:

    • 没有显式地创建对象
    • 直接将属性和方法赋给了this对象
    • 没有return语句

    使用构造器模式时,需要注意必须使用new操作符。
    构造器模式虽然好用,但也存在问题,即它没创建一个对象,所有的属性都要重新创建一边,某些情况下这种做法是并不可取的。

    // 构造器模式
    function F(name) {
        this.name = name;
        this.sayName = function () {
            return this.name
        };
    }
    
    let p1 = new F('Devin')
    let p2 = new F('Joker')
    console.log(p1.sayName()) //Devin
    console.log(p2.sayName()) //Joker
    console.log(p1.sayName === p2.sayName) //false
    

    上面的代码中sayName函数只是单纯的返回当前对象中的名字,因此重复创建这个函数本身并没有太大的意义,如果可以将sayName函数放入到一个共享的环境中就好了。我们一般会想到将sayName函数放入全局环境中,这种做法可行。

    function sayName() {
        return this.name
    }
    
    function F(name) {
        this.name = name;
        this.sayName = sayName;
    }
    
    console.log(p1.sayName === p2.sayName) //false
    

    这样就很好的解决了浪费内存的问题,但是新的问题又来了,如果有很多个这样的函数,那就不得不在全局作用域中创建很多共享函数,这么做会增加风险,且构造器本身也就没有封装性可言了。好在,这些问题我们可以用过原型模式去解决。

    2.1.3 原型模式

    我们创建的每个函数都有一个prototype属性,这个属性指向一个对象,这个对象包含所有实例共享的属性和方法。

    // 原型模式
    function F() {}
    F.prototype.name = 'Joker';
    F.prototype.age = 22;
    
    let p1 = new F()
    let p2 = new F()
    console.log(p1,p2)  //{ } { }
    console.log(p1.name, p2.name)  //Joker Joker
    p1.name = 'Devin';
    p1.age = 34;
    console.log(p1.name, p2.name) //Devin Joker
    console.log(p1.age, p2.age)   //34 22
    

    我们创建了两个不同的实例,从打印结果中可以看出,他们本身是两个空对象,但是访问各自的name属性,均可以打印出Joker,可见,在它们本身不具备该属性时,他们会到constructor的prototype中去找,当为他们定义相同的属性时,实例本身的属性会屏蔽原型上面的属性。

    原型模式也有不足之处,其根本来源是由于原型的共享性所致。

    // 原型模式
    function F() {}
    F.prototype.arr = ['a', 1, 2];
    
    let p1 = new F()
    let p2 = new F()
    p1.arr.push(5)
    p2.arr.push(10)
    console.log(p1.arr, p2.arr)  //[ 'a', 1, 2, 5, 10 ] [ 'a', 1, 2, 5, 10 ]
    console.log(F.prototype.arr) //[ 'a', 1, 2, 5, 10 ]
    

    对于prototype中的引用类型,实例中均采用指针的方式对其进行访问。任何实例对其修改都会影响到所有其他的实例对象。因此,在实际生产中,也很少单独使用原型模式。

    2.1.4 组合使用原型模式和构造器模式

    见题知意,就是将原型模式和构造器模式组合在一起使用。

    // 构造器与原型组合模式
    function F(name, age) {
        this.name = name;
        this.age = age;
        this.arr = [1, 2, 4]
    }
    
    F.prototype.sayName = function () {
        return this.name
    }
    
    let p1 = new F('Devin', 34)
    p1.arr.push(5)
    let p2 = new F('Joker', 22)
    p2.arr.push(10)
    console.log(p1) //{ name: 'Devin', age: 34, arr: [ 1, 2, 4, 5 ] }
    console.log(p2) //{ name: 'Joker', age: 22, arr: [ 1, 2, 4, 10 ] }
    console.log(p1.sayName()) //Devin
    console.log(p2.sayName()) //Joker
    

    在这个例子中,实例属性都是在构造函数中定义的,而共享属性均在prototype属性中。可以看到,修改各自的arr并不会影响到其他实例对象。

    2.1.5 其他方法

    出上述几种方法以外,还有动态原型模式、寄生构造函数模式、稳妥构造函数模式,这里不做介绍。

    相关文章

      网友评论

          本文标题:第二篇 关于对象

          本文链接:https://www.haomeiwen.com/subject/sthjcxtx.html