说明:

此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。
里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的。
摘要笔记本身没有系统性,没有全面性可言,写在这里供有一定基础的前端开发者参考交流。
里面的知识点、例子大部分来源于本书,或者延伸出来的,都经过我的测试是对的,但是没办法保证100%正确,如果有人看到错误的地方,希望指出来,谢谢。


对象原型的其它模式

1. 更简单的原型语法

a. 重写构造函数的 prototype 属性

用这种方法如果不设置原型的 constructor 属性,则原型的 constructor 属性跟普通对象一样,指向 Object,
如:

var Person = function () {}
Person.prototype = {
  name: 'abc',
  sayHello: function () {
    console.log('hello')
  }
}
Person.prototype.constructor === Person // false
Person.prototype.constructor === Object // true

var ming = new Person()
ming.constructor === Person // false
ming.constructor === Object // true
ming.__proto__ === Person.prototype // true
ming instanceof Person // true
b. 设置原型的 constructor 属性

之后的表现跟在原生的原型对象上添加属性一样,
如:

var Person = function () {}
Person.prototype = {
  constructor: Person,
  name: 'abc',
  sayHello: function () {
    console.log('hello')
  }
}
Person.prototype.constructor === Person // true

var ming = new Person()
ming.constructor === Person // true
ming.__proto__ === Person.prototype // true
ming instanceof Person // true

但是,重设 constructor 属性会导致它的 [[Enumerable]] 特性被设置成 true,变成可枚举的。
这个问题可以用 Object.defineProperty() 来改正。
如:

var Person = function () {}
Person.prototype = {
  name: 'abc',
  sayHello: function () {
    console.log('hello')
  }
}
Object.defineProperty(Person.prototype, 'constructor', {
  enumerable: false,
  value: Person
})

2. 原型的动态性

如果不重设构造函数的 prototype 原型对象,那么,我们对原型对象的任何修改都能够反映到实例上,即使先创建实例后修改原型。

如果重设构造函数的 prototype 原型对象,那么,会切断新的原型对象和任何之前已经存在的构造函数实例之间的联系,它们引用的仍然是最初的原型。

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

创建自定义类型的最常用方式,就是组合使用构造函数模式和原型模式。构造函数用于定义实例属性,原型模式用于定义方法和共享的属性。

好处:每个实例都会有自己的实例属性的副本,同时又共享着对方法的引用,最大限度的节省了内存。
还支持向构造函数传递参数。
如:

function Person (name, age) {
  this.name = name
  this.age = age
  this.friends = ['Ming', 'Li']
}
Person.prototype = {
  constructor: Person,
  sayName: function () {
    console.log(this.name)
  }
}

var zhang = new Person('zhang', 12)
var yang = new Person('yang', 15)
zhang.friends.push('wang')
console.log(zhang.friends) // ['Ming', 'Li', 'wang']
console.log(yang.friends) // ['Ming', 'Li']
zhang.sayName === yang.sayName // true

4. 动态原型模式

如:

function Person (name, age) {
  this.name = name
  this.age = age
  if (typeof this.sayName !== 'function') {
    Person.prototype.sayName = function () {
      console.log(this.name)
    }
  }
}
// 注意在用这种模式时,不能使用对象字面量重写原型。

5. 寄生构造函数模式

这种模式的基本思想就是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。但从表面看,这个函数很像构造函数。
如:

function Person (name, age) {
  var person = {
    name: name,
    age: age,
    sayName: function () {
      console.log(this.name)
    }
  }
  return person
}

var ming = new Person('ming', 12)
ming.sayName() // 'ming'
ming instanceof Person // false
ming instanceof Object // true
ming.__proto__ === Object.prototype // true
ming.constructor === Object // true

注意:这个模式除了使用new操作符并把包装的函数叫做构造函数外,跟工厂模式是一摸一样的。
说明:返回的对象与构造函数或者与构造函数的原型属性没有关系。

6. 稳妥构造函数模式

(因为不使用new操作符,所以函数名称就不首字母大写了)

稳妥构造函数模式与寄生构造函数类似,但有两点不同:一是新创建的实例方法不引用this;二是不使用new操作符调用构造函数。
如:

function person (name, age) {
  var o = {}
  o.sayName = function () {
    console.log(name)
  }
  return o;
}
var ming = person('ming', 12)
ming.sayName() // 'ming'
ming instanceof person // false
ming instanceof Object // true

注意:在这里除了调用 sayName 方法,没有其它办法访问 name 的值
说明:同寄生构造函数模式,返回的对象与构造函数或者与构造函数的原型属性没有关系。

关于对象原型部分结束。下一篇是继承相关的内容

wfc_666
643 声望31 粉丝

专注全栈