一、理解原型对象

- 实例上的属性和原型上的属性的关系
- 读取对象属性时,会先看实例上是否有这个属性,如果没有再看原型上是否有这个属性
- 实例只能访问原型对象上的值,而不能通过对象实例重写原型中的值
- 在对象实例上定义属性,会屏蔽原型对象中保存的同名属性
- 只有使用delete删除对象实例上的属性,才能访问到原型对象上的属性
操作属性的一些方法
isPrototypeOf():判断实例的[[prototype]]是否指向某个函数的原型对象1Person.prototype.isPrototypeOf(person1) // truehasOwnProperty():判断一个属性是否存在于实例中12person1.hasOwnProperty('name') //falseperson2.hasOwnProperty('name') //truein操作符:判断实例对象是否有某个属性(属性可以在实例上或原型对象上)for-in:会返回实例对象和原型对象上所有可枚举的对象Object.key():只会返回实例对象上的所有可枚举的对象Object.getOwnPropertyNames():返回实例对象上所有对象(不管是否可枚举)
给prototype赋值的影响
123456789101112131415161718function Person(){}// 以下写法,prototype的指向变化Person.prototype = {name: 'TT',age: 12}// 声明实例后,实例的类型还是Person,但是constructor的已经不再指向构造函数var person = new Person()person instanceof Person // trueperson.constructor == Person // falseperson.constructor == Object // true// 修正:可以定义constructor属性,让其指向Person// 但是这样会将constructor的`[[Enumerable]]`属性设置为`true`。// 默认的constructor是不可枚举的。可以用`defineProperty()`定义constructor属性Object.defineProperty(Person.prototype, 'constructor', {enumerable: false,value: Person})
二、创建对象
| 方法 | 优缺点 |
|---|---|
| Object构造函数或对象字面量 | 如果创建多个相似的对象,会产生大量的重复代码 |
| 工厂模式 | 封装了创建对象的细节,但是没有解决对象类型识别的问题(创建的所有对象类型都只是object) |
| 构造函数模式 | 每个方法都要在每个实例上重新创建一遍 |
| 原型模式 | 属性和方法由所有实例共享,但是如果共享属性是引用类型,这个属性的值会被所有实例修改 |
| 组合使用构造函数模式和原型模式 | 构造函数模式定义实例属性,原型模式定义方法和共享的属性 |
| 动态原型模式 | 动态给原型对象添加方法 |
| 寄生构造函数模式 | 用于拷贝一个对象的副本,创建的对象与构造函数的原型对象没有任何关系,不能确定实例对象的类型 |
| 稳妥构造函数模式 | 不适用this和new关键字,用于一些安全环境中 |
1.工厂模式
|
|
2.构造函数模式
- 使用构造函数模式创建对象,必须使用new操作符,其创建的过程为
- 1)创建一个新对象
- 2)将构造函数的作用域赋给新对象(因此this指向这个新对象)
- 3)执行构造函数中的代码(为这个新对象添加属性)
- 4)返回新对象
constructor属性:使用new操作符创建的对象都有这个属性,这个属性指向创建这个对象所用的构造函数12345678910111213141516function Person (name, age) {this.name = namethis.age = agethis.sayName = function () {console.log(this.name)}/*在创建实例时,上面sayName的创建逻辑上是如下创建的,所以每个实例上sayName方法不相等this.sayName = new Function('console.log(this.name)')*/}var person3 = new Person('CC', 12) //{ name: 'CC', age: 12, sayName: [Function] }var person4 = new Person('MM', 12) //{ name: 'MM', age: 12, sayName: [Function] }person3.constructor == Person // trueperson4 instanceof Object // trueperson4 instanceof Person // true
3.原型模式
- 每个函数都有一个
prototype属性,这个属性是一个指针,指向一个对象(这个对象被称为原型对象,它的所有属性和方法被所有实例共享)12345678910function Person () {}Person.prototype.name = 'FF'Person.prototype.age = 12Person.prototype.sayName = function () {console.log(this.name)}var person1 = new Person2()var person2 = new Person2()person2.name = 'CC'
4.组合使用构造函数模式和原型模式
- 构造函数模式定义实例属性,原型模式定义方法和共享的属性
- 创建实例的过程
- 1)创建一个新对象
- 2)将构造函数的作用域赋给新对象(因此this指向这个新对象)
- 3)执行构造函数中的代码(为这个新对象添加属性)
- 4)返回新对象(这个新对象的[[prototype]]指向构造函数的原型对象)123456789function Person3 (name, age) {this.name = namethis.age = age}Person3.prototype.sayName = function () {console.log(this.name)}const person8 = new Person3('KK', 12)console.log(person8)
5.动态原型模式
- 通过检查某个方法是否存在,来决定是否要在原型上添加该方法1234567function Person4 (name, age) {this.name = namethis.age = ageif (typeof this.sayName !== 'function') {Person.prototype.sayName = function () {}}}
6.寄生构造函数模式
- 使用
new 操作符,如果函数没有返回值,使用新对象作为其返回值。该函数有返回值,就使用返回值。 - 由于该返回值与构造函数的原型对象没有任何关系,所以无法确定对象的类型12345678function Person5 (name, age) {var o = new Object()o.name = nameo.age = ageo.sayName = function () {}return o}var person10 = new Person5('MM', 12)
7.稳妥构造函数模式
- 稳妥对象:没有公共属性,而且其方法不引用this的对象
- 与寄生构造函数模式不同之处:
- 实例方法不引用this
- 不适用new操作符调用构造函数12345678910111213function Person6 (name, age) {var o = new Object()// 定义私有变量或函数var money = 100000// 暴露出去的方法不引用thiso.sayName = function () {console.log(name)}return o}// 变量person12中保存着一个委托对象,除了调用sayName外,没有别的方式可以访问其他数据成员const person12 = Person6('XX', 12)person12.sayName()