前言
这篇文章意在分享对JS中面向对象的理解,其中对原型对象prototype 和 构造函数constructor 都会有所涉及。
传统的面向对象编程
说起传统的面向对象编程,大家脑海中第一浮现的就是class,也就是类。对传统的面向对象编程而言,类就相当于一个围城,把这个类的属性、构造函数、方法都围在其中。我们可以直接通过 类名 对象名 = new 构造函数 的方式去新建一个实例。
但JS在ES6之前是没有class关键字的,而JS又想要支持面向对象,所以当时JS采用一种比较曲折的方式去解决这个问题。
JS中的面向对象
用函数封装代替class
ES6之前的JS虽然没有class关键字,但在传统的面向对象中,class一大作用就是封装,而这一操作用函数同样可以实现。比如我们要用JS建一个Cat的类,直接一个函数就可以搞定:
function Cat(){}
可以直接用new关键字生成实例
var cat1 = new Cat()
构造函数
- 在JS中,函数本身就是一个构造函数,所以我们刚刚声明的函数Cat()就可以当做构造函数用。个人理解构造函数即构造对象的过程被封装到一个函数中。
function Cat(name,color){
//this指向 new 过后的实例
this.name = name
this.color = color
}
let cat1 = new Cat('小红','红色')
console.log(cat1.name) //'小红'
如上面代码所示,我们想要让Cat函数设置实例的名字和颜色,只需让他接受参数即可。注意:代码块内的this指向的是new 之后的对象实例 ,也就是这里的cat1。这样,我们就能通过构造函数给实例对象设置属性值了。
那么,在JS的面向对象中,属性已经安置好了,那方法呢?
注:其实在构造函数中也可以给实例对象添加方法,但是这样做的话,每new一个对象,方法就会执行,会造成资源浪费。所以JS并没有采取这种解决方法。
原型对象prototype
上面,我们已经在JS中实现了类属性和类构造函数,而方法呢?在JS中给出的解决方法就是添加一个prototype原型对象,挂载在这上面的方法,在实例化的时候会给到实例对象。
Cat.prototype.sayHello = function(){
console.log('喵喵喵');
}
Cat.prototype.eat = function(){
console.log('jerry 来一下');
}
上面的代码中,使用new关键字产生的实例都有prototype上的属性和方法,所以给Cat.prototype添加sayHello方法,cat1就能使用该方法了。
cat1.sayHello() //喵喵喵
cat1.eat() // jerry 来一下
这里,有些小伙伴可能会有这样的疑问:既然prototype上属性和方法都能放,在JS的面向对象中,为什么不将属性和方法都放在prototype中呢,而是要用构造函数给实例添加属性,用原型对象添加方法?这就要聊到当时JS的建立初期,是有“蹭流量”的意图存在的,所以如果将构造函数完全丢掉,它就和传统的面向对象编程相差太大了。因此,使用了这么一个曲折的方法来解决JS的面向对象问题。
- 下面我们再来简单的聊几个关键字
查找实例方法__proto__
聊__proto__之前,我们不妨先看看它是怎么用的:
console.log(cat1) // { name: '小红', color: '红色' }
console.log(cat1.__proto__); //{ sayHello: [Function (anonymous)] }
之前的代码中,我们为实例cat1添加了sayHello方法。由上面代码可知,如果我们直接log出cat1,里面只有name和color属性,并没有sayHello方法。那么,sayHello是如何被cat1所用的呢?从哪里找到的呢?
这就是__proto__的用法了。在JS面向对象中,当访问一个实例对象中没有的方法或者属性时,就是这里的sayHello,对象就会使用__proto__去找。注意:__proto__指向的就是JS中构造函数的prototype,也就是原型对象。
cat1.__proto__ === Cat.prototype //true
Cat.prototype.__proto__ === Object.prototype //true
所以这里cat1.__proto__就是去Cat.prototype中找,在这里就找到了sayHello的方法,所以上面代码返回{ sayHello: [Function (anonymous)] }。
但如果在这还没找到,那么JS会继续往Cat.prototype.proto ,也就是Object.prototype找,如果在这里再没找到,那就没有了,即为null,这其实就是简单意义上的 原型链。
constructor
一般的,constructor指的是原型对象prototype的一个属性,该属性指向类的构造函数,也就是JS中的类函数本身。
Cat.prototype.constructor === Car //true
总结
这里小小的总结一下,JS的面向对象,准确的讲应该为JS基于原型式的面向对象,存在一种JS原型机制,constructor + prototype = JS面向对象。在我自己的理解中,JS的面向对象,构造函数就相当于火车头,prototype中的方法相当于一节节的车厢,组成了JS的面向对象列车。
(注:这里由衷地感谢阮一峰先生,该文章就是在仔细考究阮一峰先生的博文后的一些自己的理解和总结!!)
欢迎大佬们在评论区发表自己的见解,文章中有错误也可以在评论区指出,共同学习~