当前位置: 首页 > 工具软件 > Construct > 使用案例 >

constructor属性

白腾
2023-12-01

原型链

所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所有它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型......

一层层追溯的话,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。Object.prototype的原型是null。因此,原型链的尽头是null。

读取对象的某个属性时,Javascript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到顶层的Object.prototype还是找不到,则返回undefined。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。

注意,一级级向上,在整个原型链上寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

举例来说,如果让构造函数的prototype属性指向一个数组,就意味着实例对象可以调用数组方法。

    var MyArray = function () { }
    MyArray.prototype = new Array()
    MyArray.prototype.constructor = MyArray
    var mine = new MyArray()
    mine.push(1, 2, 3)
    console.log(mine.length) // 3
    console.log(mine instanceof Array) // true

上面代码中,mine是构造函数MyArray的实例对象,由于MyArray.prototype指向一个数组实例,使得mine可以调用数组方法(这些方法定义在数组实例的prototype对象上面)。最后那行instanceof表达式,用来比较一个对象是否为某个构造函数的实例,结果就是证明mine为Array的实例

constructor属性

prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

    function P(){}
    console.log(P.prototype.constructor === P) // true

由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。

    function P(){}
    var p = new P()
    console.log(p.constructor === P) // true
    console.log(p.constructor === P.prototype.constructor) // true
    console.log(p.hasOwnProperty('constructor')) // false

上面代码中,p是构造函数P的实例对象,但是p自身没有constructor属性,该属性其实是读取原型链上面的P.prototype.constructor属性。

constructor属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。

    function F(){}
    var f = new F()
    console.log(f.constructor === F) // true
    console.log(f.constructor === Array) // false

上面代码中,constructor属性确定了实例对象f的构造函数是F,而不是Array。

另一方面,有了constructor属性,就可以从一个实例对象新建另一个实例。

    function Constr() { }
    var x = new Constr()
    var y = new x.constructor()
    console.log(y instanceof Constr) // true

上面代码中,x是构造函数Constr的实例,可以从x.constructor间接调用构造函数。这使得在实例方法中,调用自身的构造函数成为可能。

    Constr.prototype.createCopy = function(){
      return new this.constructor()
    }

上面代码中,creatrCopy方法调用构造函数,新建另一个实例。

constructor属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor属性,防止引用的时候出错

    function Person(name) {
      this.name = name
    }
    console.log(Person.prototype.constructor === Person) // true
    Person.prototype = {
      method: function () {

      }
    }
    console.log(Person.prototype.constructor === Person) // false
    console.log(Person.prototype.constructor === Object) // true

上面代码中,构造函数Person的原型对象改掉了,但是没有修改constructor属性,导致这个属性不再指向Person。由于Person的新原型是一个普通对象,而普通对象的constructor属性指向Object构造函数,导致Person.prototype.constructor变成了Object。

所以,修改原型对象时,一般要同时修改constructor属性的指向。

    function Person(name) {
      this.name = name
    }
    Person.prototype.getName = function () {
      console.log('2222')
    }
    console.log(Person.prototype.constructor === Person) // true
    Person.prototype = {
      constructor: Person,
      method: function () {

      }
    }
    var p1 = new Person()
    console.log(p1.getName) // undefined
    console.log(p1.method) // f(){}
    console.log(Person.prototype.constructor === Person) // true
    console.log(Person.prototype.constructor === Object) // false

    // 坏的写法
    C.prototype = {
      method1: function (...) {... },
      // ...
    }

    // 好的写法
    C.prototype = {
      constructor: C,
      method1:function(...){...},
      // ...
    }

    //更好的写法
    C.prototype.method1 = function(...){...}

上面代码中,要么将constructor属性重新指向原来的构造函数,要么只在原型对象上添加方法,这样可以保证instanceof运算符不会失真。

如果不能确定constructor属性是什么函数,还有一个办法:通过name属性,从实例得到构造函数的名称

    function Foo(){}
    var f = new Foo()
    console.log(f.constructor.name) // Foo

 

 类似资料: