1. 创建对象方式

  • 字面量方式创建
    • 缺点:每创建一个对象,要写一次代码 ``javascript let person = { name: '刘德华', sayHello: function() { console.log(我的名字是${this.name}`) } } console.log(person.name) // “刘德华” person.sayHello() // “我的名字是刘德华”

let dog = { name: ‘旺财’, color: ‘black’, // ES6的语法 sayColor() { console.log(我叫${this.name},我的颜色是${this.color}) } } console.log(dog.name) // “旺财” console.log(dog.color) // “black” dog.sayColor() // “我叫旺财,我的颜色是black”

  1. - **构造函数方式创建**
  2. ```javascript
  3. // 构造函数名遵循大驼峰命名
  4. function Dog(color) {
  5. this.color = color
  6. this.sayHello = function() {
  7. console.log(`我的颜色是${this.color}`)
  8. // 构造函数自动返回,不需要写return,若写了return
  9. // 当return一个对象时,则创建的就是这个return的对象
  10. // 当return的不是一个对象时,则创建的还是原来的对象
  11. }
  12. }
  13. let dog1 = new Dog('black')
  14. dog1.sayHello() // "我的颜色是black"
  15. let dog2 = new Dog('yellow')
  16. dog2.sayHello() // "我的颜色是yellow"
  • new Dog做了哪些事

    • 创建一个空对象 {},假设名字是 tmpObj
    • 执行Dog函数,执行过程中对this操作就是对tmpObj进行操作
    • 函数执行完后返回刚刚创建的 tmpObj
    • 把 tmpObj 赋值给dog1(dog1也指向同一个对象)

      2. 存在的问题

  • 构造函数方式创建对象存在的问题

    function Star(uname, age) {
      this.uname = uname
      this.age = age
      this.sing = function() {
        console.log('我会唱歌')
      }
    }
    let ldh = new Star('刘德华', 18)
    let zxy = new Star('张学友', 19)
    ldh.sing()  //我会唱歌
    zxy.sing()  //我会唱歌
    console.log(ldh.sing === zxy.sing)    // false
    
  • 上面程序中,sing()方法是一个复杂数据类型

  • 所以构造方法Star每创建一个对象就要新开辟一个空间来存放相同的sing()方法
  • 造成内存浪费

image.png
我们希望所有的对象都是用同一个函数,怎么办??

3. 构造函数原型prototype

  • 构造函数通过原型分配的函数是所有对象所共享的
  • JavaScript规定,每一个构造函数都有一个prototype属性,默认指向一个空对象
  • 注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有
  • 我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法

    // 优化程序
    function Star(uname, age) {
      this.uname = uname
      this.age = age
    }
    Star.prototype.sing = function() {
    console.log('我会唱歌')
    }
    let ldh = new Star('刘德华', 18)
    let zxy = new Star('张学友', 19)
    ldh.sing()  //我会唱歌
    zxy.sing()  //我会唱歌
    console.log(ldh.sing === zxy.sing)    // true
    

    image.png
    问题:ldh对象中并没有sing()方法,为什么ldh可以调用sing()方法呢?

    4. 对象原型proto

  • 对象都会有一个属性 proto 指向构造函数的prototype原型对象

  • 之所以我们对象可以使用构造函数 prototype原型对象的属性和方法,就是因为对象有proto原型的存在
  • proto对象原型和原型对象prototype是等价的
  • proto对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

image.png

5. constructor构造函数

  • 对象原型( proto)和构造函数原型对象(prototype)里面都有一个属性 constructor 属性
  • constructor 我们称为构造函数,因为它指回构造函数本身
  • constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
  • 一般情况下,对象的方法都在构造函数的原型对象中设置
  • 如果有多个对象的方法,我们可以给原型对象采取对象形式赋值
  • 但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函
  • 此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数

    function Star(uname, age) {
       this.uname = uname;
       this.age = age;
    }
    // 很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数
    Star.prototype = {
    // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象
    // 必须手动的利用constructor指回原来的构造函数
     constructor: Star, 
     sing: function() {
       console.log('我会唱歌');
     },
     movie: function() {
       console.log('我会演电影');
     }
    }
    var zxy = new Star('张学友', 19);
    console.log(zxy)
    

    image.png

    6. 构造函数,实例和原型对象的关系

  • 构造函数的prototype属性指向了构造函数原型对象

  • 实例对象是由构造函数创建的,实例对象的proto属性指向了构造函数的原型对象
  • 构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数

image.png

7. 原型链和查找机制

  • 原型链
    • 每一个实例对象又有一个**proto属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__**属性,这样一层一层往上找就形成了原型链

image.png

image.png

  • 查找机制
    • 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
    • 如果没有就查找它的原型(也就是 proto指向的 prototype 原型对象)
    • 如果还没有就查找原型对象的原型(Object的原型对象)
    • 依此类推一直找到 Object.proto 为止(null)
    • proto对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路
  • Object.create()
    • 创建一个新对象,用参数作为新对象的原型proto
      let dog1 = {color: 'red'}
      let dog2 = Object.create(dog1)
      console.log(dog2.color)      // "red"
      console.log(dog2.__proto__ === dog1)  // true
      

8. instanceof

  • 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
  • 数组是Array函数的实例
  • 字面量对象是Object函数的实例
  • 函数是Function函数的实例
  • Object函数是Function函数的实例
  • 若A是个函数,A.prototype.proto === Object.prototype 的结果是true ```javascript class Dog {} let dog = new Dog() dog instanceof Dog // true [] instanceof Array // true {} instanceof Object //true (function(){}) instanceof Function //true Object instanceof Function // true

// 注意基础类型经过特殊处理 1 instanceof Number // false

**经验**

- new 一个函数,这个函数的 prototype等于创建的对象的 __proto__
- 普通的对象是Object函数创建的
- 任何函数都是由Function函数创建的
- Function创建了Function
```javascript
// 实现instanceof
function _instanceof(obj, fn) {
  let __proto__ = obj.__proto__
  while(1) {
    if(__proto__ === null) return false
    if(__proto__ === fn.prototype) return true
    __proto__ = __proto__.__proto__
  }
}
class People {}
let p = new People
_instanceof(p, People)   // true
_instanceof(p, Object)   // true