理解JS的面向对象,搞懂prototype,__proto__,constructor

1,390 阅读3分钟

函数本身就是构造函数

当做类用的函数本身也是一个函数,而且他就是默认的构造函数。我们想让Person函数能够设置实力的年龄,只要让他接收参数就行了。

        function Person(age){
            this.age=age
        }
        const person=new Person(18)

注意上面代码的this,被作为类使用的函数里面this总是指向实例化对象,也就是Person。这么设计的目的就是让使用者可以通过构造函数给实例对象设置属性。

实例方法用prototype

上面我们实现了类和构造函数,但是类方法呢,JS给出的解决方案是给方法添加一个prototype属性,挂在在这上面的方法,在实例化的时候会给实例对象。我们想要Person能说话,就需要往Person.prototype添加说话的方法.

person.prototype.say=function(){
  console.log(this.age)
}

使用new关键字产生的实例都有类的prototype上的属性和方法,我们在Person.prototype上添加的say方法,Person就可以说话了。

实例方法查找用__proto__

person为什么能调用say方法呢。我们把person打印出来,发现这个对象上并没有say, 这就该__proto__上场了。当访问一个对象上没有的属性石,比如person.say,对象会去__proto__查找。 __proto__的值就等于父类的prototype。proson.__proto__指向了Person.prototype。 如果你访问的属性在Person.prototype也不存在,那又会继续往Person.prototype.__proto__上找,这时候其实就找到了Object.prototype了,Object.prototype再往上找就没有了,也就是null,这其实就是原型链。 instanceof方法判断数据类型就是通过一层一层的比对__proto__是否跟实例化对象的prototype完全相等,这里判断数据类型也是不准确的,因为原型链可以被修改。

constructor

我们说的constructor一般指类的prototype.contructor。prototype.contructor是prototype上的一个保留属性,这个属性就指向了类函数本身,用于指示当前类的构造函数。 Person.prototype.constructor===Person true

既然prototype.constructor是指向构造函数的一个指针,那我们是不是可以通过它来修改构造函数呢?

我们修改prototype.constructor只是修改了这个指针而已,并没有修改真正的构造函数。这里也说明了通过constructor来判断数据类型会不准确,因为consturctor的指针可以修改。

总结

  1. JS中的函数可以作为函数使用,也可以作为类使用。
  2. 作为类使用的函数实例化时需要使用new关键字
  3. 为了让函数具有类的功能,函数都具有prototype属性。
  4. 为了让实例化出来的对象能够访问到prototype上的属性和方法,实例对象的__proto__指向了类的prototype。所以prototype是函数的属性,不是对象的。对象拥有的是__protot__,是用来查找prototype的。
  5. prototype.constructor指向的是构造函数,也就是类函数本身,改变这个指针并不能改变构造函数。
  6. 对象本身并没有constructor属性,你访问到的是原型链上的prototype.constructor。
  7. 函数本身也是对象,也具有__proto__他指向的是JS内置对象Function的原型Function.prototype。所以你才能调用function.call这些方法,你调用的其实是Function.prototype.call
  8. prototype本身也是对象,所以他也有__proto__,指向了他父级的prototype。__proto__和prototype的这种链式指向构成了JS的原型链。原型链的最终指向是Object的原型。Object上面原型链是Null,即Object.protottype.proto===null。