乐趣区

关于javascript:JavaScript高级程序设计原型

了解原型

无论何时,只有创立一个函数,就会依照特定的规定为这个函数创立一个 prototype 属性(指向原型对象)。默认状况下,所有原型对象主动取得一个名为 constructor 的属性,指回与之关联的构造函数。
在定义构造函数时,原型对象默认只会取得 constructor 属性,其余的所有办法都继承自 Object。每次调用构造函数创立一个新实例,这个实例的外部[[Prototype]] 指针就会被赋值为构造函数的原型对象。脚本中没有拜访这个 [[Prototype]] 个性的规范形式,但 Firefox/Safari/Chrome 会在每个对象裸露 __proto__ 属性,通过这个属性能够拜访对象的原型。

实例与构造函数原型有间接的分割,但实例与构造函数之间没有

这种关系不好可视化,但能够通过上面的代码来了解原型的行为

    function Person() {}
    // 申明之后,构造函数就有了一个与之关联的原型对象
    console.log(typeof Person.prototype);
    console.log(Person.prototype)
    // 如前所述,构造函数有一个 prototype 属性
    // 援用其原型对象,而这个原型对象有一个 constructor 属性,援用这个构造函数
    // 换句话说,两者循环援用
    console.log(Person.prototype.constructor === Person)  // true
    // 失常的原型链都会终止于 Object 的原型对象
    // Object 原型的原型是 null
    console.log(Person.prototype.__proto__ === Object.prototype) // true
    console.log(Person.prototype.__proto__.constructor === Object) // true
    console.log(Person.prototype.__proto__.__proto__ === null) // true

    let person1 = new Person()
    let person2 = new Person()
    // 构造函数、原型对象和实例时 3 个齐全不同的对象
    console.log(person1 !== Person) // true
    console.log(person1 !== Person.prototype) // true
    console.log(Person.prototype !== Person) // true

    // 实例通过__proto__链接到原型对象,它理论指向暗藏个性[[Prototype]]
    // 构造函数通过 prototype 属性链接到原型对象
    // 实例与构造函数没有间接分割,与原型对象有间接分割
    console.log(person1.__proto__ === Person.prototype) // true
    console.log(person1.__proto__.constructor === Person) // true
    
    // 同一个构造函数创立的两个实例,共享同一个原型对象
    console.log(person1.__proto__ === person2.__proto__) // true
    
    // instanceof 查看实例的原型链中是否蕴含指定构造函数的原型
    console.log(person1 instanceof Person) // true
    console.log(person1 instanceof Object) // true
    console.log(Person.prototype instanceof Object) // true


下面的图片展现了 Person 构造函数、Person 的原型对象和 Person 现有两个实例之间的关系。
留神:Person.prototype 指向原型对象,而 Person.prototype.constructor 指回 Person 构造函数。
原型对象蕴含 constructor 属性和其余起初增加的属性。
Person 的两个实例 person1 和 person2 都只有一个外部属性指回 Person.prototype,而且两者都与构造函数没有间接分割。
另外要留神,尽管这两个实例都没有属性和办法,但 person1.sayName()都能够失常调用,这是因为对象属性查找机制的起因。
尽管不是所有实现都对外暴漏了 [[Prototype]],但能够应用 isPrototypeOf() 办法确定两个对象之间的这种关系,实质上,isPrototypeOf()会在传入参数的 [[Prototype]] 指向调用它的对象时返回 true,如下所示

    console.log(Person.prototype.isPrototypeOf(person1) // true
    console.log(Person.prototype.isPrototypeOf(person2)) // true

这里通过原型对象调用 isPrototypeOf()办法查看了 person1 和 person2. 因为这两个例子外部都有链接指向 Person.prototype,所以后果都返回 true。
ESMAScript 的 Object 类型有一个办法叫 Object.getPrototypeOf(), 返回参数的外部个性 [[Prototype]] 的值。例如:

    console.log(Object.getPrototypeOf(person1) === Peron.prototype // true
    console.log(Object.getPrototypeOf(person1).name // '66'

第一行代码简略确认了 Object.getPrototypeOf()返回的对象就是传入对象的原型对象。第二行代码则获得了原型对象上 name 属性的值,即 ’66’。应用 Object.getPrototypeOf()能够不便地获得一个对象的原型,而这在通过原型实现继承时显得尤为重要。
能够通过 Object.create()来创立一个新对象,同时为其指定原型:

    let biped = {numLegs: 2}
    let person = Object.create(biped)
    person.name = '66'
    console.log(person.name) // '66'
    console.log(person.numLegs) // 2
    console.log(Object.getPrototypeOf(person) === biped) // true

原型层级

TODO...
退出移动版