JavaScript面向对象

知识内容:python

1.理解对象app

2.建立对象函数

3.继承this

 

参考资料:《JavaScript高级程序设计》spa

 

 

 

1.理解对象prototype

JavaScript中的面向对象和其余语言中的面向对象不同,JavaScript中没有类的概念,ECMA-262把对象定义为无序属性的集合,其属性能够包含基本值、对象或函数设计

JavaScript中的对象都是基于一个引用类型建立的,这个引用类型能够是原生类型,也能够是开发人员定义的类型指针

简单建立一个对象:code

 1 var person  = new Object()
 2 // 定义属性
 3 person.name = "wyb"
 4 person.age = 21
 5 person.job = "Software Enginner"
 6 
 7 // 定义方法
 8 person.sayName = function (){
 9     console.log(this.name)
10 }

上述建立对象也能够这样写:对象

 1 var person = {
 2     // 定义属性
 3     name: "wyb",
 4     age: 21,
 5     job: "Software Enginner",
 6     
 7     // 定义方法
 8     sayName: function(){
 9          console.log(this.name)
10     }   
11 }

 

 

2.建立对象

虽然使用上述两种方法均可以建立对象,可是这些方式有个明显的缺点:使用同一个接口建立不少对象,会产生大量的重复代码;为解决这些问题,因而提出了一下的方式:

(1)工厂模式

工厂模式抽象建立具体对象的过程,解决了建立多个类似对象的问题的,可是没有解决对象识别的问题(即如何知道一个对象的类型)

 1 function createPerson(name, age, job){
 2      var o = new Object()
 3      o.name = name
 4      o.age = age
 5      o.job = job
 6      o.sayName = function() {
 7          console.log(this.name)
 8      }
 9      return o
10 }
11 
12 var person1 = createPerson("wyb", 21, "IT")
13 var person2 = createPerson("alex", 29, "boss")

注:关于this,this代指对象,this至关于python中的self

 

(2)构造函数模式

使用构造函数的主要问题就是每一个方法都要在每一个实例上从新建立一遍,例如像下面构造函数中的sayName没有必要重复定义,这个重复定义的问题能够经过后面的原型模式解决

 1 // 构造函数:
 2 function Person(name, age, job){
 3     this.name = name
 4     this.age = age
 5     this.job = job
 6     this.sayName = function() {
 7          console.log(this.name);
 8     }
 9 }
10 
11 var person1 = new Person("wyb", 21, "IT") 
12 var person2 = new Person("alex", 29, "boss")

 

(3)原型模式

prototype就是经过构造函数而建立的那个对象实例的原型对象,使用原型对象的好处就是可让全部对象实例共享它所包含的属性和方法(不用在构造函数中定义对象实例的信息而是把这些信息直接添加到原型对象中)

prototype基本示例:

 1 function Person(){
 2 }
 3 
 4 // 让全部对象实例共享的属性和方法:
 5 Person.prototype.name = "wyb"
 6 Person.prototype.age = 21
 7 Person.prototype.job = "IT"
 8 Person.prototype.sayName = function() {
 9     console.log(this.name)
10 }
11 
12 var person1 = new Person()
13 var person2 = new Person()
14 person1.sayName()  // wyb
15 person2.sayName()  // wyb
16 console.log(person1.sayName() == person2.sayName())  // true

注:上述person1和person2中的属性和方法均是原型对象中的属性和方法,是全部对象实例通用的

关于prototype:

  • 不使用prototype属性定义的对象方法,是静态方法,只能直接用类名进行调用!另外,此静态方法中没法使用this变量来调用对象其余的属性!
  • 使用prototype属性定义的对象方法,是非静态方法,只有在实例化后才能使用!其方法内部能够this来引用对象自身中的其余属性!

 

原型实现面向对象:

 1 //  构造函数
 2 function Foo (name, age) {
 3     this.Name = name;
 4     this.Age = age;
 5 }
 6 
 7 //  指定原型
 8 Foo.prototype.getInfo = function(){
 9      return this.Name + this.Age
10 }
11 Foo.prototype.func = function(arg){
12      return this.Name + arg
13 }
14 
15 obj1 = new Foo("wyb", 21)
16 obj2 = new Foo("xxx", 23)
17 console.log(obj1.getInfo())  // wyb21
18 console.log(obj2.getInfo())  // xxx23

上面的代码和下面的代码同理:

 1 //  构造函数
 2 function Foo (name, age) {
 3     this.Name = name
 4     this.Age = age
 5 }
 6 
 7 //  指定原型
 8 Foo.prototype = {
 9     getInfo: function(){
10          return this.Name + this.Age
11     },
12     func: function(arg){
13         return this.Name + arg
14     },   
15 }
16 
17 obj1 = new Foo("wyb", 21)
18 obj2 = new Foo("xxx", 23)
19 console.log(obj1.getInfo())  // wyb21
20 console.log(obj2.getInfo())  // xxx23

以上实现过程底层原理:

 

 

3.继承

(1)两种继承

大多OO语言都支持两种继承方式: 接口继承和实现继承 ,而ECMAScript中没法实现接口继承(因为函数没有签名),ECMAScript只支持实现继承,并且其实现继承主要是依靠原型链来实现

  • 接口继承:
  • 实现继承:

后面是JavaScript实现继承的几种方法的详细说明

 

(2)原型链

核心: 将父类的实例做为子类的原型

基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法

构造函数,原型,实例之间的关系:每一个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针

原型链实现继承例子:

 1 var SuperType = function() {
 2     this.property = true;
 3 };
 4 
 5 SuperType.prototype.getSuperValue = function() {
 6     return this.property;
 7 }
 8 
 9 function SubType() {
10     this.subproperty = false;
11 }
12 
13 //继承了SuperType
14 SubType.prototype = new SuperType();
15 SubType.prototype.getSubValue = function (){
16   return this.subproperty;
17 }
18
19 var instance = new SubType();
20 console.log(instance.getSuperValue()); // true
21
console.log(instance.getSubValue()); // false

说明:

上述继承是经过建立SuperType的实例,并将该实例赋给SubType.prototype实现,实现的本质上重写原型(实例都包含一个指向对象内部的指针,把实例赋给原型就是用实例对象来重写原型)

换句话说,原来存在于SuperType的实例中的全部属性和方法如今也存在于SubType.prototype中了。以后在肯定继承关系以后,咱们给SubType.prototype添加了一个方法,而后用Subtype来建立对象,这个实例对象不只仅继承了SuperType的属性和方法,也添加了一个新方法,在这个例子中的实例以及构造函数和原型之间的关系以下:

特色:

  • 很是纯粹的继承关系,实例是子类的实例,也是父类的实例
  • 父类新增原型方法/原型属性,子类都能访问到
  • 简单,易于实现

缺点:

  • 要想为子类新增属性和方法,必需要在new SuperType()这样的语句以后执行,不能放到构造器中(不能使用字面量来添加新属性和新方法,这样会引发原型链断裂,切断SubType和SuperType的链接)
  • 没法实现多继承
  • 来自原型对象的引用属性是全部实例共享的,这会致使对一个实例的修改会影响另外一个实例(详细请看下面的代码实例)
  • 建立子类实例时,没法向父类构造函数传参
 1 function SuperType(){
 2       this.colors=["red", "blue", "green"];
 3 }
 4 
 5 function SubType(){
 6 }
 7 
 8 //继承了SuperType
 9 SubType.prototype=new SuperType();
10 
11 var instance1=new SubType();
12 instance1.colors.push("black");
13 console.log(instance1.colors);   //red,blue,green,black
14 
15 var instance2=new SubType();
16 console.log(instance2.colors);    //red,blue,green,black

 

(3)借用构造函数

在解决原型中包含引用类型值所带来的问题中,使用借用构造函数技术来解决,另外借用构造函数还能够在子类型构造函数中向基类构造函数传递参数

借用构造函数的基本思想,即在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,所以经过使用apply()和call()方法能够在新建立的对象上执行构造函数

实例:

 1 function SuperType(){
 2       this.colors=["red", "blue", "green"]
 3 }
 4 
 5 function SubType(){
 6       // 继承SuperType
 7       SuperType.call(this)      // 调用SuperType的构造函数
 8 }
 9 
10 var instance1=new SubType()
11 instance1.colors.push("black")
12 console.log(instance1.colors)      // red,bllue,green,black
13 
14 var instance2=new SubType()
15 console.log(instance2.colors)      // red,blue,green

传递参数:

 1 function SuperType(name){
 2       this.name=name
 3 }
 4 function SubType(){
 5       // 继承了SuperType,同时还传递了参数
 6       SuperType.call(this,"wyb")
 7       // 实例属性
 8       this.age=22
 9 }
10 var instance=new SubType()
11 console.log(instance.name)       //wyb
12 console.log(instance.age)        //22

借用构造函数存在两个问题:

  • 没法避免构造函数模式存在的问题,方法都在构造函数中定义,所以没法复用函数
  • 在超类型的原型中定义的方法,对子类型而言是不可见的。所以这种技术不多单独使用

 

(4)组合继承

组合继承:将原型链和借用构造函数的技术组合到一块儿

思路:使用原型链实现对原型方法的继承,而经过借用构造函数来实现对实例属性的继承。这样,既经过在原型上定义方法实现了函数的复用,又可以保证每一个实例都有它本身的属性

实例: 

 1 function SuperType(name){
 2       this.name = name
 3       this.colors = ["red", "blue", "green"]
 4 }
 5 
 6 SuperType.prototype.sayName = function(){
 7       console.log(this.name)
 8 }
 9 
10 function SubType(name, age){
11       //继承属性    使用借用构造函数实现对实例属性的继承
12       SuperType.call(this,name)
13       this.age = age
14 }
15 
16 //继承方法     使用原型链实现
17 SubType.prototype = new SuperType()
18 SubType.prototype.constructor = SubType
19 SubType.prototype.sayAge = function(){
20       console.log(this.age)
21 }
22 
23 var instance1 = new SubType("wyb", 21)
24 instance1.colors.push("black")
25 console.log(instance1.colors)   // red,blue,green,black
26 instance1.sayName()  // wyb
27 instance1.sayAge()  // 21
28 
29 var instance2 = new SubType("greg", 25)
30 console.log(instance2.colors)   // red,blue,green
31 instance2.sayName()  // greg
32 instance2.sayAge()   // 25

说明:上述代码中的两个实例既分别拥有本身的属性,包括colors属性,又可使用相同的方法

组合继承避免了原型链和借用构造函数的缺点,融合了他们的优势,是JavaScript中最经常使用的继承模式

 

(5)原型式继承

基本想法:借助原型能够基于已有的对象建立新对象,同时还没必要须所以建立自定义的类型。

原型式继承的思想可用如下函数来讲明:

1 function object(o) {
2     function F(){}
3     F.prototype = o;
4     return new F();
5 }

实例(下面的代码须要先运行上面的代码):

 1 var person = {
 2     name: "EvanChen",
 3     friends: ["Shelby","Court","Van"];
 4 }
 5 
 6 var anotherPerson = object(person)
 7 anotherPerson.name = "Greg"
 8 anotherPerson.friends.push("Rob")
 9 
10 var yetAnotherPerson = object(person)
11 yetAnotherPerson.name = "Linda"
12 yetAnotherPerson.friends.push("Barbie")
13 console.log(person.friends)  // "Shelby","Court","Van","Rob","Barbie"

ECMAScript5经过新增Object.create()方法规范化了原型式继承,这个方法接收两个参数:一个用做新对象原型的对象和一个做为新对象定义额外属性的对象(可选),实例以下:

 1 var person = {
 2     name: "EvanChen",
 3     friends: ["Shelby","Court","Van"]
 4 }
 5 
 6 var anotherPerson = Object.create(person)
 7 anotherPerson.name = "Greg"
 8 anotherPerson.friends.push("Rob")
 9 
10 var yetAnotherPerson = Object.create(person)
11 yetAnotherPerson.name = "Linda"
12 yetAnotherPerson.friends.push("Barbie")
13 console.log(person.friends)  // "Shelby","Court","Van","Rob","Barbie"

缺点:和原型继承同样,来自原型对象的引用属性是全部实例始终都会共享的值

 

(6)寄生式继承

基本思想:建立一个仅用于封装继承过程的函数,该函数在内部以某种方式来加强对象,最后再像真正是它作了全部工做同样返回对象。 

实例:

 1 function createAnother(original) {
 2     var clone = object(original)         // 经过调用函数建立新对象
 3     clone.sayHi = function () {          // 以某种方式来增长对象
 4         console.log("hi")
 5 }
 6     return clone                               // 返回对象
 7 }
 8 
 9 var person = {
10     name: "EvanChen",
11     friends: ["Shelby","Court","Van"]
12 }
13 
14 var anotherPerson = createAnother(person);
15 anotherPerson.sayHi()  // "hi"

说明:上述实例中的代码中基于person返回了一个新对象anotherPerson,新对象不只具备person中的全部属性和方法,并且还有本身的sayHi方法

缺点:不能作到函数复用,这一点和借用构造函数来实现继承相似

 

(7)寄生组合式继承 -> 集寄生式继承和组合继承的优势于一身,是实现基于类型继承的最有效方式

基本思想:经过借用函数来继承属性,经过原型链的混成形式来继承方法

基本模型:

1 function inheritProperty(subType, superType) {
2     var prototype = object(superType.prototype)  // 建立对象
3     prototype.constructor = subType  // 加强对象
4     subType.prototype = prototype    // 指定对象
5 }

实例:

 1 function SuperType(name){
 2     this.name = name
 3     this.colors = ["red","blue","green"]
 4 }
 5 SuperType.prototype.sayName = function (){
 6     console.log(this.name)
 7 }
 8 
 9 function SubType(name,age){    
10     SuperType.call(this,name)
11     this.age = age
12 }
13 inheritProperty(SubType,SuperType)
14 SubType.prototype.sayAge = function() {
15     console.log(this.age)
16 }

说明:上述例子的高效率体如今它只调用了一次SuperType构造函数,而且所以避免了在SubType.prototype上建立没必要要的 多余的属性.与此同时,原型链还能保持不变.所以,还可以正常使用instanceof 和isPrototypeOf肯定继承关系

相关文章
相关标签/搜索