💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
面向对象是很多高级程序程序设计语言的核心。面向对象的标志就是类、对象、继承、派生等。js严格意义上来讲并不是面向对象的语言,因为它没有类这个概念(即便es6出现了class关键字)。但是js可以用自己的方式来模拟面向对象。 <br> ###何谓对象 在js中对象就是一组无序属性的集合,一个对象可以含有任何类型的属性和方法,每一个属性和方法都有一个自己的名字,通过"."或者"[ ]"来调用。 <br> ###一、创建一个对象 ####1. 使用Object类型创建 js的一大好处就是可以动态去添加属性和方法 ~~~ var people = new Object(); people.name = "SunnyChuan";//添加一个属性 people.age = 22; people.getAge = function() {//添加一个方法 return this.name; } ~~~ ####2. 使用字面量方式 ~~~ var people = { name : "SunnyChuan"; age : 22; getAge : function() { return this.name; } } ~~~ 当然,你也可以这样创建一个对象 ~~~ var people = {}; people.name = "SunnyChuan"; people.age = 22; people.getAge = function() { return this.name; } ~~~ <br> ###二、更优雅地创建对象 使用Object构造函数和字面量可以很轻松地创建对象,但是这两种方法都有一个致命的缺点———代码冗余,如果单单创建一个简单的对象使用这两种方式没有任何问题,但是如果我要创建一百个学生对象呢? 这时候就出现了很多"高级"的方式来创建对象。 ####1. 工厂模式 顾名思义,这种方式就像工厂一样,提供了底层的对象模板,在产生新的对象时只需要根据需求在这个模板里添加值即可。 ~~~ function createPeople(name, age) { var obj = new Object(); obj.name = name; obj.age = age; obj.getAge = function() { return this.age; } } var people = createPeople("SunnyChuan",22); console.log(people.name); console.log(people.age); ~~~ ####2. 构造函数 支持面向对象的高级程序设计语言都支持构造函数模式,在创建对象的时候为这个对象初始化成员变量和成员方法,js模拟了这一功能。 ~~~ function People(name, age) { this.name = name; this.age = age; this.getAge = function() { return this.age; }//不需要返回 } var people = new People("SunnyChuan",22);//注意和工厂模式的区别 ~~~ 和工厂模式不同,构造函数内部并不是显示地创建对象所以不需要返回,这也就意味着在创建对象时需要new。 ####3. 原型模式 要说js里面最神奇的东西无外乎是原型。什么是原型?我理解为一个对象的本来面目,可以把它理解成对象的财产。这个原型属性其实是一个指针,这个指针指向所有该对象的实例。实例共享原型的属性和方法。 ~~~ function People() { People.prototype.name = "SunnyChuan"; People.protptype.age = 22; People.prototype.getAge = function() { return this.age; } } var people1 = new People(),people2 = new People(); console.log(people1.name + " " + people1.age); console.log(people2.name + " " + people2.age); ~~~ 上述代码运行的结果是什么?你也许会因为控制台的两条完全一致的信息而困惑,这是因为原型模式为People对象添加了原型,这个原型上有name、age属性和getAge方法。这些东西都是被实例共享的。 来看一段有意思的代码 ~~~ var people = new People(); console.log(people.name);//SunnyChuan people.name = "DannyQin"; console.log(people.name);//DannyQin delete people.name; console.log(people.name);//SunnyChuan ~~~ 原型允许我添加一个自己独有的属性去覆盖原型的属性,一旦添加,原型的同名属性就会被覆盖掉,直到这个独有的属性被删除,原型的属性又会暴露出来。这就好比我和一个朋友共有一本书,但是我嫌这本书太旧,于是我就自己买了一本一模一样的新书。直到有一天我新买的书丢了,我又迫不得已去和朋友一起使用那本旧书了。 ps:这里有一个坑,当我修改原型后实例也会动态去更改,但是如果重写原型,那么已经创建的实例不会受到影响。 ####4. 组合构造函数和原型模式 原型很强大,但是缺点也很明显:属性共享,我相信在实际开发过程中没有哪一个对象的所有实例的名字都叫"SunnyChuan",这时候一个真正强大的模式就产生了。组合模式使用构造函数定义属性,使用原型定义方法。 ~~~ function People(name, age) { this.name = name; this.age = age; } People.prototype.getAge = function() { return this.age; }//如果方法比较多也可以使用字面量的形式 var people1 = new People("SunnyChuan",22); var people2 = new People("DannyQin",21); console.log(people1.name + " "+ people2.name); ~~~ 5. 还有很多模式,诸如动态原型模式、寄生构造函数模式等等 <br> ###三、继承 有类就会有继承,就像有父亲必有儿子一样。js既然可以模拟类,那么同样可以模拟继承。 ####1. 原型链 什么是原型链?原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法,让原型对象等于另一个类型的实例,上述关系层层递进形成原型链。简单来说就是一个引用类型a是另一个引用类型b的原型,而b又是c的原型,这样就构成了a到b到c的一条原型链。在查找属性时通过原型链一层层地向上查找该属性,如果找到了则返回。举个不恰当的例子,假如儿子欠了一笔钱,如果儿子没钱债主就会向他的老爸要钱,如果老爸也没有,那就向他的爷爷要钱,一直向上追债直到把钱要回来。 ~~~ function Father() { this.name = "SunnyChuan"; } Father.prototype.getName = function() { return this.name; } function Son() { this.name = "DannyQin";//覆盖父类的name } Son.prototype = new Father();//父类作为子类的原型 var s = new Son();//也可以重写父类的方法 console.log(s.getName());//DannyQin ~~~ 子类重写父类的方法时,由于方法是使用原型实现,所以如果使用字面量的形式重写会破坏整个继承关系(因为会重写整个原型,导致子类的原型不再是父类)。 ####2. 借用构造函数 构造函数模式通过给this的属性赋值来达到创建对象的目的,那么如果通过改变this的指向是不是就可以实现继承了?答案是肯定的,那么什么方法可以改变this的指向呢? ~~~ function Father(name) { this.name = name; } function Son(name, age) { Father.call(this, name); this.age = age; } var s = new Son("DannyQin",22); console.log(s.name + " " + s.age); ~~~ ####3. 组合继承 原型链模式和构造函数模式继承都有各自的缺点,与创建对象策略相同,使用组合继承的方式可以弥补二者的缺点。 ~~~ function Father(name) { this.name = name; } Father.prototype.getName = function() { return this.name; } function Son(name, age){ Father.call(this, name); this.age = age; } Son.prototype = new Father(); //可以重写父类的getName() Son.prototype.getAge = function() { return this.age; } ~~~ <br> ###总结 js的面向对象是一个很复杂而又实用的技术,通过面向对象可以实现类似于私有变量等等模仿类的各种功能,我会继续挖掘下去。