创建对象的方式

1、工厂模式

在函数里,new 一个 Object,然后根据传入的参数给该对象添加属性,最后返回该对象。问题:无法知道一个对象的类型。

2、构造函数模式

问题:每个方法都要在每个实例上重新创建一遍。解决:在全局作用域中定义全局函数。当然,这会导致封装性很差。

3、原型模式

每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象(指向该函数的原型对象),而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

缺点:原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。但是对于包含引用类型值的属性问题就突出了

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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。

5、动态原型模式

在构造函数中这么写共享的方法和属性:

// 方法
if (typeof this.sayName != 'function') {
  Person.prototype.sayName = function() {
    alert(this.name);
  };
}

6、寄生构造函数模式

基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

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

var friend = new Person('Amy', 18, 'student');
friend.sayName();    // Amy

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

用法:这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改 Array 构造函数,因此可以使用这个模式。

function SpecialArray() {
  // 创建数组
  var values = new Array();
  // 添加值
  values.push.apply(values, arguments);
  // 添加方法
  values.toPipedString = function() {
    return this.join('|');
  };
  // 返回数组
  return values;
}

var colors = new SpecialArray('red', 'green', 'blue');
alert(colors.toPipedString());    // red|green|blue

关于寄生构造函数模式,有一点需要说明:
首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。

用 new 调用构造函数实际经历了4个步骤

  • 1 创建一个新对象;
  • 2 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
  • 3 执行构造函数中的代码(为这个新对象添加属性);
  • 4 返回新对象。

理解原型对象

所有函数都有一个 prototype 属性,这个属性指向函数的原型对象

在默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性,这个属性是一个指向 prototype 属性所在函数的指针
eg. Person.prototype.constructor => Person。

当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性[[Prototype]]),指向构造函数的原型对象。注意:这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。实例 => 构造函数的原型对象

原型对象

判断某个实例的原型指针是否指向某个函数的原型对象:

Person.prototype.isPrototypeOf(person1)    // true
Object.getPrototypeOf(person1) === Person.protype    // true

hasOwnProperty() 方法:检测一个属性是存在于实例中,还是存在于原型中。只有存在于实例中时,才返回 true。
in 操作符:实例和原型中的属性都能访问到。
同时使用 hasOwnProperty() 方法和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。

Object.keys() 方法:取得对象上所有可枚举的实例属性。
Object.getOwnPropertyNames() 方法:得到所有实例属性(包括不可枚举属性)。

重写原型会怎么样?

Person.prototype = {…}:
我们将 Person.prototype 设置为等于一个以对象字面量形式创建的新对象。 最终结果相同,但有一个例外:constructor 属性不再指向 Person 了。前面曾经介绍过,每创建一个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的 prototype 对象,因此 constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person 函数。此时,尽管 instanceof 操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了。

重写原型对象

实现继承的方式

1、原型链继承


Unique111
23 声望1 粉丝

前端工程师