代码改变世界

【JavaScript】之面向对象的程序设计

2017-03-22 23:05  SiestaKc  阅读(188)  评论(0编辑  收藏  举报

最近几天跟着视频利用原生的JavaScript做了几个小插件,有图片画廊和图片轮转,实践的过程中,发现对JS的面向对象部分很是陌生,今日重看了《JavaScript高级程序设计》的面向对象部分,并结合了IMOOC网上的《JS深入浅出》教程在此做一下知识的梳理和总结。

一、什么是对象

ECMA-262中把对象定义为:“无序属性的组合,每个属性包含基本值、对象或者函数。

  • 这就相当于说对象是一组没有特定顺序的值,每个属性或方法都有一个名字key,和对应的value。
  • 每一个对象都有一个原型[prototype]
  • 每一个对象有一个[class],表示属于哪一个种类
  • 每一个对象有一个[extensible],表示对象是否允许继续增加属性

 

二、创建对象

对象可以在代码执行过程中创建和增强,因此具有动态性而非严格定义的实体,可以采用下列模式创建对象:

1.构造函数:

可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法;

可以像创建内置对象实例一样使用new操作符;

function Person(name,age,job){
      this.name=name;
      this.age=age;
      this.job=job;
      this.sayName = function(){
               alert(this.name);
          };
   }
var person1= new Person("Siesta",21,"Student");
var person2 = new Person("Greg",27,"Doctor");

缺点:构造函数的每个成员都无法得到复用,包括函数。由于函数可以不局限于任何对象,因此没有理由不在多个对象共享函数。

2.原型模式:

使用构造函数的prototype属性来指定那些应该共享的属性和方法。

  • 理解原型对象:

 

  • 利用isPropertyOf()方法 Object.getPrototype()方法可以测试实例person1和person2
//isPropertyOf()方法
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true // Object.getPrototype()方法 alert(Object.getPrototypeOf(person1 == Person.prototype) ;//true alert(Object.getPrototypeOf(person1).name); //"Siesta"
  • 利用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。
  • 原型与in操作符

单独使用in操作符时,可以通过对象能够访问给定属性时返回true,无论该属性是存在于实例中还是原型中。

function Person(){

}
Person.prototype.name="Nicholas";
Person.prototype.age=29;
Person.prototype.job="SoftwareEngineer";
Person.prototype.sayName=function(){
    alert(this.name);  
};

var person1=new Person();
var person2=new Person();

alert(person1.hasOwnProperty("name"));
alert("name" in person1);  //true

person1.name="Greg";
alert(person1.name);  //"Greg"来自实例
alert(perosn1.hasOwnProperty("name")); //true
alert("name" in person1);//true

alert(person2.name);//"Nicholas"
alert(person2.hasOwnProperty("name"));//false
alert("name" in person2);//true

delete person1.name;
alert(person1.name);//"Nicholas"
alert(person1.hasOwnProperty("name"));//false
alert("name" in person1);//true
  • 同时使用hasProperty()方法和in操作符,就可以判断该属性到底是存在于对象中,还是存在于原型中
    function hasPrototypePrototype(object,name){
           return !object.hasOwnProperty(name) && (name in object);
    }

    返回true,则表示存在原型中,返回false,则存在对象中

  • 使用Object.getOwnPropertyNames()方法获取实例的所有属性,无论configurable是否可配置,是否可枚举
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys);

3.组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。这样,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用。

//组合使用构造函数模式和原型模式
//定义了实例属性的构造函数
 function Person(name,age,job){
     this.name=name;
     this.age=age;
     this.job=job;
     this.friends=["Shelby","Court"];  
 }
// 定义了共享属性和方法的原型模式

 Person.prototype={
     constructor:Person,
     sayName:function(){
         alert(this.name);
     }
 }

 var person1=new Person("Nicholas",29,"Software Engineer");
 var person2=new Person("Greg",27,"Doctor");

 person1.friends.push("Van");
 alert(person1.friends);// "Shelby,Court,Van"
alert(person2.friends);//"Shelby,Count"
alert(person1.friedns == person2.friends); //false
alert(person1.sayName == person2.sayName);//true

三、继承

JavaScript主要通过原型链实现继承。

原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类继承很相似。

原型链的问题是对象实例共享所有继承的属性和方法,所以需要利用构造函数,即在子类型构造函数的内部调用超类型构造函数,做到每个实例都具有自己的属性,同时还能保证只使用构造函数来定义类型。

function SuperType(){
    this.property=true;
}
SuperType.prototype.getSuperValue=function(){
    return this.property;
 };
//SuperType类型拥有一个属性和一个方法
function SubType(){
    this.subproperty=false;
}
//继承了Supertype,通过创建SuperType的实例
SubType.property = new SuperType();
SubType.prototype.getSubValue=function(){
    return this.subproperty;
};

var instance=new SubType();
alert(instance.getSuperValue);

调用instance.getSuperValue()会经历三个搜索步骤:(1)搜索实例;(2)搜索SubType.prototype;(3)搜索SuperType.prototype,最后才会找到该方法,如果找不到属性或方法的情况下,搜索过程总是要一环环地前行到原型链的末端才会停下来。

  • 默认的原型链

SubType继承了SuperType,而SuperType继承了Object。当调用instance.toString时,实际上调用的是保存在Object.prototype中的方法。

  • 确定原型与实例的关系
  1. 使用instanceOf操作符,只要用这个操作符测试实例与原型链中出现过的构造函数,结果就会返回true
alert(instance instanceof Object); //true
alert(instance instanceof SuperType);//true
alert(instance instanceof SubType);//true

2.使用isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。

alert(Object.prototype.isPrototypeOf(instance);//true
alert(SuperType.prototype.isPrototypeOf(instance);//true
alert(SubType.prototype.isPrototypeOf(instance);//true
  • 借用构造函数

在子类型构造函数内部调用超类型构造函数,通过call()和apply()方法在新创建的对象上执行构造函数

function SuperType(){
    this.colors=["red","blue","green"];
}

function SubType(){
    //继承了SuperType
    SuperType.call(this); //SuperTyep.apply(this);在新创建的SubType实例的环境下调用了SuperType构造函数

}

var instance1=new SubType();
instance1.colors.push("black");
alert(instance1.colors);//"red,blue,green,black"

var instance2=new SubType();
alert(instace2.colorss);//"red,blue,green"

传递参数

相对原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数。

function SuperType(name){
     this.name = name;
}
function SubType(){
    //继承了SuperType,同时还传递了参数
     SuperType.call(this,Nicholas);

    this.age=29;
}
var instance = new SubType();
alert(instance.name);//"Nicholas";
alert(instance.age);//29
  • 组合继承方式

也成伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,其想法是使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承,

//SuperType函数定义了两个属性,name属性和colors
function SuperType(name){
    this.name=name;
    this.colors=["red","green","blue"];
}
SuperType的原型定义了一个方法sayName();
SuperType.prototype.sayName:function(){
    alert(this.name);
};
SubType构造函数在调用SuperType构造函数时传入了name参数
function SubType(name,age){
    SuperType.call(this.name);
    this.age=age;
}
//继承方法
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
    alert(this.age);
};

var instance1=new SubType("Nicholas",29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName();//"Nicholas"
instance1.sayAge();//29

var instance2=new SubType("Greg",27);
alert(instance1.colors);//"red,blue,green"
instance1.sayName();//"Greg"
instance1.sayAge();//27