建立对象的方式

1.直接经过字面量的方式建立

let obj = {
    name: 'john',
    age: 24,
    number: 18
  }
咱们常常会使用这种方式建立对象
复制代码

2.使用Object.create()来建立对象

该方法有两个参数:Object.create(proto, [propertiesObject])。第一个参数proto来提供新建立的对象的原型对象,第二个参数(可选)默认为undefined。若是没有指定为undefined,那么这个参数对象包含了要添加到新建立的那个对象上的可枚举属性以及对该可枚举属性的属性描述符。数组

若是propertiesObject参数是null或非原始包装对象,则抛出一个TypeError异常。 bash

let o = {
    name: 'Tom',
    address: 'china'
  };
 let obj1 = Object.create(o);
 console.log(obj1);
 
 //如下是为新建立的对象添加了可枚举属性number
 let obj2 = Object.create(o,{
   number: {
     value: 18204,
     writable: true,
     enumerable: true,
     configurable: true
   }
 });
 console.log(obj2);
复制代码

3.工厂模式建立对象

工厂模式虽然解决了多个类似对象之间的流水线似的建立问题,可是并无解决对象识别的问题。对象中的constructor最初是用来标识对象类型的(或经过instanceof判断)。下面例子中对象识别的问题是函数f不是obj1和obj2的构造函数,obj一、obj2的原型也不是f.prototype函数

function f(name,age) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sayName = function () {
      console.log(this.name);
    };
    return obj;
  }
  let obj1 = f('lb',23);
  let obj2 = f('lb',22);
  console.log(obj1.constructor === f); //false
  console.log(obj2.constructor === f); //false
  //或者用instanceof检测
  console.log(obj1 instanceof f);  //false
  console.log(obj2 instanceof f);  //false
复制代码

4.使用构造函数来建立对象

//为了解决对象没法被识别的问题,如今使用构造函数来建立对象
  function Create(name,age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
      console.log(this.name);
    }
  }
  let c1 = new Create('lb',23);
  let c2 = new Create('fjq',20);
  console.log(c1 instanceof Create); //true
  console.log(c2 instanceof Create); //true
  console.log(c1.sayName === c2.sayName); //false
复制代码

能够看到上面c1和c2都是Create的实例对象,这也就解决了对象识别的问题。可是构造函数也不是彻底没有缺陷的,使用构造函数建立对象,每个方法都会在实例对象上从新建立一遍。因为方法(函数)也是对象,这样就会形成大量的内存浪费。经过将构造函数转移到构造函数外部能够解决该问题。优化

5.构造函数建立对象的优化

function Create(name,age) {
    this.name = name;
    this.age = age;
    this.sayName = sayName
  }
  function sayName() {
    console.log(this.name);
  }
  let c1 = new Create('lb',23);
  let c2 = new Create('fjq',20);
  console.log(c1.sayName === c2.sayName); //true
复制代码

以上将sayName函数转移到构造函数外部能够看到,两个实例对象都共享了全局做用域中的sayName函数。可是新问题又出现了,在全局做用域中定义的函数只能被某个对象调用,这让全局做用域下的函数有点名存实亡。其次,大量在全局做用域中定义方法,也不熟是一个好的作法。这些问题,能够经过原型模式解决。ui

6.原型模式建立对象

每个函数都有prototype属性,该属性是一个指针,指向一个对象,咱们将这个对象称为原型对象。在原型对象上的属性和方法全部实例对象均可以共享。所以,没必要在构造函数中添加属性和方法,直接写在原型对象上便可。this

function Fn() {

  }
  Fn.prototype.name = 'jack';
  Fn.prototype.age = 22;
  Fn.prototype.sayName = function () {
    console.log(this.name);
  };
  //或者直接用字面量的方式写
  // Fn.prototype = {
  //   name: 'jack',
  //   age: 22,
  //   sayName: function () {
  //     console.log(this.name);
  //   },
  //   constructor: Fn
  // }
  var fn = new Fn();
  fn.sayName(); //jack
复制代码

以上经过原型模式建立的实例对象,均可以访问其原型对象上的方法和属性。而原型模式也会出现问题。首先,它省略了构造函数传递初始化参数这一环节,结果全部实例都共享了相同的属性和方法,而这正不是咱们所但愿的。因为原型中的属性和方法是共享的,对于包含引用类型的时候就会出问题,以下。spa

function Student() {

  }
  Student.prototype = {
    name: 'jack',
    age: 29,
    color: ['red','yellow'],
    constructor: Student,
    sayName () {
      console.log(this.name);
    }
  };
  let stu1 = new Student();
  let stu2 = new Student();
  stu1.color.push('orange');
  console.log(stu1.color); //["red", "yellow", "orange"]
  console.log(stu2.color); //["red", "yellow", "orange"]
 将stu1.color所引用的数组修改了,其结果也会反应在stu2.color数组上,而有时候这样正不是咱们所但愿的
复制代码

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

构造函数模式用于定义实例的属性,原型模式用于定义共享的属性和方法。这种方式避免了构造函数建立对象的缺点,即全部的属性和方法都会在实例对象上。又避免了原型模式建立对象的缺点,即把全部的属性和方法都写在原型对象上,省略了构造函数初始化参数这一环节。prototype

function Student(name,age) {
    this.name = name;
    this.age = age;
    this.color = ['yellow','red']
  }
  Student.prototype = {
    constructor: Student,
    sayName () {
      console.log(this.name);
    }
  };
  let s1 = new Student('jack',19);
  let s2 = new Student('john',29);
  s1.color.push('orange');
  console.log(s1.color); //["yellow", "red", "orange"]
  console.log(s2.color); //["yellow", "red"]
复制代码
相关文章
相关标签/搜索