JS创建对象的7中方式

1、  工厂模式

2、  构造函数模式          [解决对象识别问题]

3、  原型模式                   [解决方法和属性共享问题]

4、  组合使用构造函数模式和原型模式

5、  动态原型模式

6、  寄生构造函数模式

7、  稳妥构造函数模式

  • 最简单的方式:创建一个Object的实例,然后为它添加属性和方法。

缺点:使用同一个接口创建很多对象,会产生大量的重复代码

1 var person = new Object();
2 person.name = 'mackxu';
3 person.age = 23;
4 person.job = 'Software Engineer';
5 
6 person.sayName = function() {
7     debug(this.name);
8 };
  • 工厂模式:这种模式抽象了创建具体对象的过程。

具体做法:用函数封装以特定接口创建对象的细节

缺点:对比上一个方法解决了创建多个相似对象的问题,但却没有解决对象识别的问题

 1     function createPerson(name, age, job) {
 2         var o = new Object();
 3         o.name = name;
 4         o.age = age;
 5         o.job = job;
 6         o.sayName = function() {
 7             debug(this.name);
 8         };
 9         return o;
10     }
  • 构造函数模式:解决了对象识别问题

使用new操作符调用构造函数经历4个步骤:

1、 创建一个新对象

2、 将构造函数的作用域赋给新对象(this指向这个新对象)

3、 执行构造函数中的代码(为这个新对象添加属性)

4、 返回新对象

1     function Emp(name, age, job) {
2        this.name = name;
3        this.age = age;
4        this.job = job;
5        this.sayName = function() {
6            debug(this.name);
7        };
9     }
var p = new Emp();
模拟过程如下:
var p = {};
Emp.apply(p);
p.__proto__ = Emp.prototype;

将构造函数当作函数

 1 var person = new Person('mackxu', 23, 'Software Engineer');
 2     person.sayName();
 3     debug(person.constructor == Person);
 4     debug(person instanceof Person);    
 5     //var person2 = Person('mack', 22, 'Doctor');
 6     //作为普通的函数
 7     Person('mack', 22, 'Doctor');
 8     sayName();        // == window.sagName();
 9     //在另外一个对象的作用域中调用
10     var o = new Object();
11     //this 指向o对象
12     Person.call(o, 'zhangsan', 23, 'Nurse');
13     o.sayName();    //zhangsan    
14     debug(o instanceof Person);        //false

问题:方法不能被实例共享

    解决方法有二:1、把方法定义到构造函数外面,但破坏了封装性

                2、用原型模式共享方法

  • 原型模式(解决属性方法共享问题)

每一个函数都有一个prototype属性,这个属性是一个对象,它的用途是保存可以由特定类型的所有实例共享的属性和方法。

 1 function Person() {
 2         //...
 3     }
 4     Person.prototype.name = 'mackxu';
 5     Person.prototype.age = 22;
 6     Person.prototype.job = 'Software Engineer';
 7     Person.prototype.sayName = function() {
 8         debug(this.name);
 9     };
10 debug(person.sayName == person2.sayName)    //true

理解原型:

 1 debug(Person.prototype.isPrototypeOf(person));    //true
 2 使用delete操作符删除实例的属性
 3 使用hasOwnProperty()方法可以检测一个属性在实例中,还是在于原型中。
 4 (准确的说:只能确定在实例中的属性,返回true)
 5     debug(person2.hasOwnProperty('name'));    //false
 6     debug('name' in person2);                //true
 7     //自定义函数判断属性在原型中定义
 8     function hasPrototypeProperty(object, name) {
 9         return !object.hasOwnProperty(name) && (name in object);
10     }
11     debug(hasPrototypeProperty(person2, 'name'));    //true
12     //会屏蔽原型中与实例中相同的属性
13     for (var property in person) {
14         debug(property);
15     }

更简单的原型语法[字面量对象]

 1 function Person() {
 2         //...
 3     }
 4     Person.prototype = {
 5         constructor: Person,
 6         name : 'mackxu',
 7         age  : 22,
 8         job  : 'Software Engineer',
 9         sayName: function() {
10             debug(this.name);    
11         }
12     };
13     var person2 = new Person();
14     debug(person2.name);
15     debug(person2 instanceof Person);        //true
16     debug(person2.constructor);            //Person{}
17     debug(Person.prototype.constructor);    //Person{}    

原型的动态性

由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来

——即使是先创建了实例后修改原型也照样如此。

 1 function Person() {
 2         //.....
 3     }
 4     var person = new Person();
 5     Person.prototype.sayHi = function() {
 6          debug('Hi');
 7 };
 8  person.sayHi();        //Hi
 9 
10     Person.prototype = {
11         name: 'mackxu',
12         sayHi: function() {
13             debug('Hi');
14         }
15     };
16     person.sayHi();        //error

原理:

 

原生对象的原型

通过原生对象的原型,不仅可以取得所有默认方法的引用,而且可以定义新方法

    //为基本包装类型String添加自定义方法

String.prototype.startWith = function(text) {
        return this.indexOf(text) == 0;
    };
    var msg = 'Hello World';
    debug(msg.startWith('Hello'));    //true

原型对象的问题:

1、 省略了为构造函数传递初始化参数的环节,

2、 其共享本性导致的(对于引用类型属性)

function Person() {
        //...
    }
    Person.prototype = {
        constructor: Person,
        students: ['aaa', 'bbb']
    };
    //students被实例所共享,违背了我们的初衷
    var person2 = new Person();
    debug(person2.students);
    var person3 = new Person();
    person2.students.push('ccc');     
    debug(person3.students);        //"aaa", "bbb", "ccc"

3、 实例:属性是自己的,方法是被共享的

  • 组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性

结果,每个实例都有都会有自己的一份实例属性的副本,同时共享着对方的方法引用

 1     function Person(name, age, job) {
 2         this.name = name;
 3         this.age = age;
 4         this.job = job;
 5         this.friends = ['zhangsan', 'lisi'];
 6     }
 7     Person.prototype = {
 8         constructor: Person;
 9         sayName: function() {
10             debug(this.name);
11         };
12     };
13     var person2 = new Person('mackxu', 22, 'Software Engineer');
14     var person3 = new Person('mackxu2', 23, 'Software Engineer');
15     person2.sayName();        //mackxu
16     person2.friends.push('wangwu');
17     debug(person2.friends);        //['zhangsan', 'lisi', 'wangwu']
18     debug(person3.friends);        //['zhangsan', 'lisi']
  • 动态原型模式

把所有信息都封装在构造函数中,通过在构造函数中初始化原型,保持了同时使用构造函数和原型的有点。可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型

 1     function Person(name, age, job) {
 2         this.name = name;
 3         this.age = age;
 4         this.job = job;
 5         this.friends = ['zhangsan', 'lisi'];
 6         
 7         if (typeof this.sayName != 'function') {
 8             Person.prototype.sayName = function() {
 9                 debug(this.name);
10             };
11             Person.prototype.sayHi = function() {
12                 debug("Hi");
13             };
14         }
15     }
16     var person2 = new Person('mackxu', 22, 'Software Engineer');
17     person2.sayName();        //mackxu
18     person2.sayHi();

    使用动态原型模式时,不能使用对象字面量重写原型。

  • 寄生构造函数模式

构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加return,可以重写调用构造函数时返回的值。

注意:返回的对象与构造函数或者与构造函数的原型属性之间没关系

 1     function Person(name, age, job) {
 2         var o = new Object();
 3         o.name = name;
 4         o.age = age;
 5         o.job = job;
 6         o.sayName = function() {
 7             debug(this.name);
 8         };
 9         return o;
10     }
11     
12     var person2 = new Person('mackxu', 22, 'Software Engineer');
13     person2.sayName();        //mackxu
14     
15     function SpecialArray() {
16         //创建数组
17         var values = new Array();
18         //添加值
19         values.push.apply(values, arguments);
20         //添加方法
21         values.toPipedString = function() {
22             return this.join('|');
23         };
24         return values;
25     }    
26     //此处new的作用是什么??
27     var colors = new SpecialArray('red', 'blue', 'green');
28     debug(colors.toPipedString());
  • 稳妥构造函数模式

禁止使用this和new,所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象

原文地址:https://www.cnblogs.com/mackxu/p/2767181.html