什么叫做面向对象?
面向对象是一种编程思想,JavaScript本身就是基于面向对象构建出来的,例如:JavaScript中有很多内置类(Array,Object,Number,Function,Promise....)等等,使用最简单的例子来说Array,我们可以基于new Array来创建一个属于Array的实例,来使用Array的一些原型上的方法,比如sort,slice,splice等等,平常开发的时候都是创建他们的实例来进行操作的。
JavaScript中的面向对象和其他编程语言还是有略微不同的,JS中类和实例是基于原型和原型链机制来处理的。
面向对象包含什么?
面向对象包含了类的封装,继承和多态,下面会一一解答
封装-低耦合高内聚
就是可以将复用的代码块单独提炼出来作为一个方法,避免了代码的复杂程度以及多重使用时的ctrl c+ctrl v
继承->子类使用父类的方法和属性(目的是让子类的实例可以使用父类的属性和方法)
1、原型继承->让父类中的方法在子类的实例的原型链上
CHILD.prototype = new PARENT()
CHILD.constructor = CHILD
function Parent (x){
this.parentX = x
}
Parent.prototype.getParentX = function (){
console.log(this.parentX)
}
function Child (y){
this.childY = y
}
// 将子类的原型作为父类的实例
Child.prototype = new Parent(200)
// 可以不写,但是为了保证原型链的完整性,最好让子类的原型上添加一个constructor
Child.constructor = Child;
Child.prototype.getChildY = function (){
console.log(this.childY)
}
// 缺点 -> 子类直接往父类的原型上加了一个addfun()的方法,其他子类也可以调用
Child.prototype.__proto__.addfun= function (){}
var child1 = new Child(100)
console.log(child1.childY,child1.parentX) //->输出100,200
总结:
1.不是拷贝方法供子类使用,父类如果修改私有方法和属性,子类使用的属性和方法同样会改变
2.子类可以重写父类的方法,会导致父类其他实例也受到影响
3.父类中私有和共有的属性方法,最后都会变为子类的公有属性和方法
2、Call继承->在子类中将父类当做普通函数执行,让父类的this指向子类的实例,相当于给子类的实例设置了很多私有的属性和方法
function Parent (x){
this.parentX = x
}
Parent.prototype.getParentX = function (){
console.log(this.parentX)
}
function Child (y){
// 使用call将父类的this指向指到子类中,那父类的私有属性和方法就成为了子类的私有属性和方法
Parent.call(this,200)
this.childY = y
}
Child.prototype.getChildY = function (){
console.log(this.childY)
}
var child1 = new Child(100)
console.log(child1.childY,child1.parentX) //->输出100,200
总结:
1.只能继承父类私有的属性和方法(因为是把父类当做普通函数来执行,和其原型上的属性和方法没有关系)
2.父类私有的属性和方法变成子类的私有的属性和方法
3、寄生组合继承->Call继承+类似于原型继承
function Parent (x){
this.parentX = x
}
Parent.prototype.getParentX = function (){
console.log(this.parentX)
}
function Child (y){
// 使用call将父类的this指向指到子类中,那父类的私有属性和方法就成为了子类的私有属性和方法
Parent.call(this,200)
this.childY = y
}
// Object.create() 创建一个空对象,让空对象的原型链(__proto__)指向第一个参数
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child;
Child.prototype.getChildY = function (){
console.log(this.childY)
}
var child1 = new Child(100)
console.log(child1.childY,child1.parentX) //->输出100,200
但是问题同样来了,如果不考虑IE浏览器的话这样是没问题的,但是如果要考虑IE浏览器就会发现有问题,因为IE浏览器并没有proto,所以我们的Object.create()方法就不能用了,所以就需要自己重写一个create方法
Object.create = function (obj) {
function Fn (){};
Fn.prototype = obj;
return new Fn()
}
总结:
父类私有和共有的分别是子类实例的私有和共有属性方法
4、ES6中的继承->//ES6中继承使用extends
语法 class Child extends Parent{} 实际结果相当于Child.prototype.proto = Parent.prototype
class Parent {
constructor (x){
this.x = x;
}
getx() {
return this.x;
}
}
// 然后constructor中需要加入一个super
class Child extends Parent{
// 子类继承父类可以不写constructor,一旦写了constructor中的第一句话必须是super
// 如果不写constructor,浏览器会自己默认创建
// constructor(...args){super(...args)}
constructor (y){
super(y) // 相当于Parent.call(this,200)
this.y = y
}
gety() {
return this.y
}
getx(){
return 0
}
}
// Child.prototype = Object.create(Parent.prototype) 不允许重定向原型的指向
let child1 = new Child(100)
console.log(child1.x,child1.y,child1.gety(),child1.getx());
总结:
父类私有和共有的分别是子类实例的私有和共有属性方法
多态->类的重载和重写
重载->java的定义就是方法名称相同,但是根据参数的不同去拆分一些复杂代码。而js中的重载是方法名字一样,但是根据参数的不同在当前作用域中进行代码的处理。
实际上js中是不存在真正意义上的重载比如:
function sum(x,y){
console.log('1')
}
function sum(x,y,z){
console.log('2');
}
sum() //->输出打印 2
因为js中是有变量提升这个概念的,所以当方法名字一样的时候下面的方法会将上面方法覆盖掉。所以在js中进行重载的话需要用到 arguments 这个参数或者es6中的扩展运算符 ...arg,然后根据arguments中的参数进行判断操作
function fn(){
for( let i= 0;i<arguments.length;i++){
if(typeof(arguments[i])=='string'){
console.log(arguments[i])
}
}
}
function fn1(...arg){
arg.forEach(item=>{
if(typeof(item)=='string'){
console.log(item)
}
})
}
fn(1,2,'a') // ->输出a
fn1(1,2,'b')// ->输出b
文采不好,请见谅,有错误的请及时留言联系我修改。望大家批评指正!