面向对象、原型、class
面向对象解决的问题 并不是:封装、继承、多态
而是写代码的套路问题(减少思考)
对象与对象的关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var person = { name: 'xu' , age: 18 , isAdult: true , say: function ( ) { console .log('person xu' ) } } var person2 = { name: 'chen' , age: 17 , isAdult: false , say: function ( ) { console .log('person chen' ) } } var person3 = {...}var person4 = {...}
我们可以看到这些对象太类似了,于是我们发明了构造函数(返回对象的函数)
1 2 3 4 5 6 7 8 9 10 function createPerson (name, age ) { var obj = {} obj.name = name obj.age = age obj.isAdult = age >= 18 obj.say = function ( ) { console .log('person' + $(this .name)) } return obj }
对象与函数的关系 JS 中对象与函数没有关系,JS 之父使用 this 强行绑定两者
封装 我现在想要两百块,那么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var hundred1 = { id: 'AA00000000' , value: 100 , unit: '元' , color: '红' , form: '纸币' , issue: '2019-01' , 兑换等值商品: function ( ) {}, 换两张50 块: function ( ) {} } var hundred2 = { id: 'AA00000001' , value: 100 , unit: '元' , color: '红' , form: '纸币' , issue: '2019-01' , 兑换等值商品: function ( ) {}, 换两张50 块: function ( ) {} }
于是我就可以用这两百块钱去吃椰子鸡了。
构造函数 & 原型对象
哎呀,椰子鸡好吃,可是钱也花光了。不行,我得再造钱来用,但我不想让别人看到我是怎么造的!所以我要做一个百元印钞机,把这些过程放百元进印钞机里,我只要输入每张钞票的 ID ,再按印刷键,就可以刷刷刷出百元大钞 。同时,这个印钞机要智能一点,不要老是一个个重复得去刻面额、单位、喷颜色等,直接给它引用个百元钞模版,这样能省点墨水 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function HundredYuan (id ) { var money = {} money.__proto__ = HundredYuan.prototype money.id = id return money } HundredYuan.prototype = { value: 100 , unit: '元' , color: '红' , form: '纸币' , issue: '2019-01' , 兑换等值商品: function ( ) {}, 换两张50 块: function ( ) {} } var wallet = []for (var i = 2 ; i < 7 ; i++) { var id = 'AA0000000' + i wallet.push(HundredYuan(id)) } wallet wallet[0 ].value
这个百元印钞机,就是我们所说的「构造函数」,它能生成一个实例对象(也就是造出一张张百元钞),同时它还用 prototype 属性来引用一个存放共同属性的「原型对象」(百元钞模版)。把造钱的过程放进这个百元印钞机,就是「封装」。这样我们就能很好地理解了封装的意义了——隐藏某一方法的具体运行步骤。
new & this
从上面的例子我们可以知道,构造函数的套路就是:
创建一个空的新对象
为这个新对象添加公有属性,即 新对象.__proto__
属性指向构造函数的原型( 构造函数.prototype
引用的对象)
为这个新对象添加自有的属性
返回这个新对象。
既然套路都一样,那为什么不把这个套路变得贴心一点呢?于是有了 new
操作符,它可以帮我们省去 1、2、4 这三个动作,我们只需专注于添加对象的自有属性即可。同时, JS 之父还帮我们规定好,创建的空对象统一命名为 this
。于是,上面的函数我们可以写成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function HundredYuan (id ) { this .id = id } HundredYuan.prototype = { value: 100 , unit: '元' , color: '红' , form: '纸币' , issue: '2019-01' , 兑换等值商品: function ( ) {}, 换两张50 块: function ( ) {} } new HundredYuan('AA00000007' )
继承 嘿嘿,有了百元印钞机,富得流油不再是梦 。 但是新的问题来了,整天带着一大沓百元大钞出门,要么可能会被乞丐围着,要么可能会有生命危险!不行不行,还是低调点,我需要点小钞。例如五角硬币机:
1 2 3 4 5 6 7 8 9 function FiveJiao ( ) {}FiveJiao.prototype = { value: 5 , unit: '角' , color: '金' , form: '硬币' , issue: '2019-01' , 兑换等值商品: function ( ) {} }
哇,跟百元印钞机好像哦。说不定把它们整一下,就可以弄出一元印钞机、十元印钞机…… 那么我就弄个造钱机,它造的钱都有 兑换等值商品 的功能,我要让 N 元印钞机和 N 角硬币机在不需要自己定义的情况下就能直接用到它爸爸造钱机的 兑换等值商品 功能以及一些属性。这就是我们所谓的「继承」——让子类实例能够拥有父类实例的所有方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function Money (options ) { this .form = options.form this .issue = options.issue } Money.prototype.兑换等值商品 = function ( ) { } function HundredYuan (id ) { Money.call(this , { form : '纸币' , issue : '2019-01' }) this .id = id } function tempMoney ( ) { } tempMoney.prototype = Money.prototype HundredYuan.prototype = new tempMoney() HundredYuan.prototype.value = 100 HundredYuan.prototype.unit = '元' HundredYuan.prototype.color = '红' HundredYuan.prototype.换两张50 元 = function ( ) { } new HundredYuan('AA00000001' )
Object.create()
Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 proto 。 让子类原型继承父类原型的操作看起来太麻烦了,更人性化及正确的写法如下:
1 2 3 4 5 6 7 8 9 function HundredYuan (id ) { Money.call(this , { form : '纸币' , issue : '2019-01' }) this .id = id } HundredYuan.prototype = Object .create(Money.prototype) HundredYuan.prototype.constructor = HundredYuan HundredYuan.prototype.value = 100
类 ES6 引入的「类」是一个特殊的函数,它可以帮助我们进一步简化继承的操作,但它依旧是「基于原型」的而不是引入新的面向对象继承模型,类语法只不过是一种语法糖。 上面的例子用类改造如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Money { constructor (options ) { this .form = options.form this .issue = options.issue } 兑换等值商品 () { } } class HundredYuan extends Money { constructor (id ) { super ({ form : '纸币' , issue : '2019-01' }) this .id = id this .value = 100 this .unit = '元' this .color = '红' } 换两张50 元 () { } } new HundredYuan('AA00000001' )
使用类方法确实简洁明了了许多,但是有个问题就是,它明明是个函数,但却不能被调用:
要用类还是用构造函数,你来决定,这里只是提出这一个现象。 希望看完本文的你,能理解到 JavaScript 中的面向对象思想。