js面向对象编程

2018-06-27 09:10:04来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

javascript面向对象编程

在面向对象编程里,程序是由对象构成的。js的面向对象编程中,对象是由属性构成的,没有类的概念。

1.对象

定义:无序属性的集合,其属性可以包含基本值、对象或者函数。

1.1对象

var person = new Object(); 
person.name = "zs"; 
person.age = 29; 

person.sayName = function(){ 
 alert(this.name); 
}; 
//用new object创建对象

var person = {
name: "zs",
age: 29,

sayName: function(){
alert(this.name);
}
};

//用对象字面量创建对象

1.2属性类型

ECMAScript 中有两种属性:数据属性和访问器属性。

 数据类型:

[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,这个特性默认值为 true。

[[Enumerable]]:(可枚举)表示能否通过 for-in 循环返回属性,这个特性默认值为 true。

[[Writable]]:表示能否修改属性的值,这个特性默认值为 true。

[[Value]]:包含这个属性的数据值,这个特性的默认值为 undefined。

访问器类型:

[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,这个特性默认值为 true。

[[Enumerable]]:(可枚举)表示能否通过 for-in 循环返回属性,这个特性默认值为 true。

[[Get]]:在读取属性时调用的函数,默认值为 undefined。

[[Set]]:在写入属性时调用的函数,默认值为 undefined。

1.2.1

要修改属性默认的特性需要通过Object.defineProperty()方法,这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。

一次定义多个属性可以使用Object.defineProperties()方法,第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。

1.2.2

读取属性点特性:Object.getOwnPropertyDescriptor()方法,这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。

2.创建对象

2.1工厂模式

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
o.job = job; o.sayName = function() { alert(this.name); }; return o; } var person1 = createPerson('zs', 29, 'Software Engineer'); var person2 = createPerson('lisi', 27, 'Doctor');

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

2.2构造函数模式

function Person(name, age, job) {
    this.name = name;
    this.age = age;
   this.job = job; this.sayName = function() { alert(this.name); }; } var person1 = new Person('zs', 29, 'Software Engineer'); var person2 = new Person('lisi', 27, 'Doctor');

2.2.1构造函数的不同之处:

1.没有显示地创建对象;

2.直接将属性和方法赋给this对象;

3.没有return语句。

(按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。)

*使用new操作符创建实例会经过以下4个步骤:

1.创建一个新对象;

2.将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);

3.执行构造函数中的代码(为这个新对象添加属性);

4.返回新对象。

(不使用new操作符时只会经历第三个步骤)

2.2.2构造函数的优缺点:

构造函数模式相对于工厂模式的优点:1.代码简单 2.明确标识了对象的类型

缺点:每个方法都要在每个实例上重新创建一遍。

2.3原型模式

我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

function Person(){ 
} 
Person.prototype.name = "zs"; 
Person.prototype.age = 29; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function(){ 
 alert(this.name); 
}; 
var person1 = new Person(); 
person1.sayName(); //"zs" 
var person2 = new Person(); 
person2.sayName(); //"zs" 
原型模式的格式如下:
1.创建一个空的构造函数
2.将所有的属性都添加到构造函数原型上
3.new构造函数创建对象
原型对象:
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个
constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。

//这是上一个函数的构造函数,构造函数原型和构造函数实例之间的关系

这里说一下

ECMAScript 5 增加了一个新方法,叫 Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。(返回参数对象的原型方法)
//如:

  alert(Object.getPrototypeOf(person1) == Person.prototype); //true
  alert(Object.getPrototypeOf(person1).name); //"zs"

*屏蔽:

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。

function Person(){ 
} 
Person.prototype.name = "zs"; 
Person.prototype.age = 29; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function(){ 
 alert(this.name); 
}; 

var person1 = new Person(); 
var person2 = new Person(); 
person1.name = "lisi"; 
alert(person1.name); //"lisi"——来自实例
alert(person2.name); //"zs"——来自原型

  当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,它不会因属性值设置为null而消失,不过使用delete操作符可以完全删除实例属性,从而重新访问原型中的属性。

......
person1.name = "lisi"; 
alert(person1.name); //"lisi"——来自实例
alert(person2.name); //"zs"——来自原型
delete person1.name; 
alert(person1.name); //"zs"——来自原型

  使用 hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法(不要忘了它是从 Object 继承来的)只在给定属性存在于对象实例中时,才会返回 true,而 in 操作符只要通过对象能够访问到属性就返回 true,所以可以通过这两个方法判断属性的位置。

2.3.2简单的原型语法

*用一个包含所有属性和方法的对象字面量来重写整个原型对象。

function Person(){ 
} 
Person.prototype = { 
constructor : "Person", name : "zs", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };

2.3.3原型对象的缺点

将所有的属性都生命在原型对象上,然而其实很多的属性实例的属性,也就是说每一个实例应该有一个具体存储这个属性的内存,但原型对象却把这个本该属于实例的内存分配到共享的原型对象上,就不合适了,原型模式滥用了共享。

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

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

function Person(name, age, job){          //私有的属性定义在构造函数中
 this.name = name; 
 this.age = age; 
 this.job = job; 
} 
Person.prototype = {            //方法和共用的属性定义在原型中,原型的constructor属性指向构造方法
 constructor : Person, 
 sayName : function(){ 
 alert(this.name); 
 } 
} 
var person1 = new Person("zs", 29, "Software Engineer"); 
var person2 = new Person("lisi", 27, "Doctor");

  这种构造函数与原型混成的模式,是目前在 ECMAScript 中使用最广泛、认同度最高的一种创建自定义类型的方法。

2.5动态原型模式

动态原型模式将所有信息封装在了构造函数中,而通过构造函数中初始化原型(仅第一个对象实例化时初始化原型),这个可以通过判断该方法是否有效而选择是否需要初始化原型。

2.6寄生构造函数模式

除了使用 new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的.

function Person(age,name) {
    var o=new Object();
    o.age=age;
    o.name=name;
    o.sayName=function(){
        alert(this.name);
    }
    return o;
}
var person=new Person(22,"zs");
    person.sayName();

 

2.7稳妥构造函数模式

稳妥构造函数与寄生构造函数模式类似,但是也有两点区别:

1、稳妥模式不使用new操作符调用构造函数

2、新创建对象的实例方法不引用this

function Person(name,age) {
    //创建要返回的对象
    var o=new Object();
    //可以在这里定义私有变量和函数
    //添加方法
    o.sayName=function(){
        alert(name);
    }
    //返回对象
    return o;
}
var person=Person("张三",22);
    person.sayName();  //使用稳妥构造函数模式只能通过其构造函数内部的方法来获取里面的属性值

3.继承

在面向对象编程中,有相同属性和方法的对象属于同一个类型,在js中对象的私有属性应该在构造函数中定义,共有的属性和方法在构造函数的原型对象中定义。按这种思路实现项目的时候,会发现有的不同类型的对象也有共有的属性和方法,此时不同类型的构造函数定义中存在重复代码。可以将不同类型函数的重复代码抽取出来,定义在一个新类型的构造函数中,需要使用这些重复代码的构造函数可以通过几次;来实现重用。

 3.1原型链

ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

function SuperType(){ 
 this.property = true; 
} 
SuperType.prototype.getSuperValue = function(){ 
 return this.property; 
}; 

function SubType(){ 
 this.subproperty = false; 
} 

//继承了 SuperType 
SubType.prototype = new SuperType(); 
SubType.prototype.getSubValue = function (){ 
 return this.subproperty; 
}; 

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

 

上图是实现原型链的一个实例,它们之间的关系按照下图来表明:

所有函数的默认原型都是 Object 的实例,因此默认原型都会包含一个内部指针,指向 Object.prototype。

 

这是一个相对完整的原型链。

*原型链的缺点:

1.重写子类的原型 等于 父类的一个实例,(父类的实例属相变成子类的原型属性)如果父类包含引用类型的属性,那么子类所有实例都会共享该属性 

2.在创建子类型的实例时,不能向超类型的构造函数中传递参数。

 3.2继承

继承:多个构造函数如果有相同的属性和方法,可以将相同的属性和方法定义在一个父类型的构造函数中,需要包含这些相同属性和方法的构造函数通过继承就可以直接使用

Parent.call(this,a,b,c); //Parent是父类构造函数,a,b,c是继承于父类的属性

组合继承:

  指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。它的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

//1.创建父类型构造函数
function Parent(a,b,c){//a,b,c就是子类型都需要的私有的属性
this.a = a;
this.b = b;
this.c = c;
}

Parent.prototype={
constructor:Parent,
fn:function(){}//这是子类型共有的方法
}
//2.创建子类型构造函数
function A(a,b,c,d){
Parent.call(this,a,b,c); //
this.d = d;//子类型相对其他父类型特有的属性
}
A.prototype = new Parent();
A.prototype.constructor = A;
A.prototype.fnB=function(){};//子类型特有的方法

3.3静态方法和实例方法

1.定义在构造函数上的方法是静态方法

静态方法不能被实例对象调用;

2.定义在实例上的方法是实例方法,实例方法必须要通过实例来调用

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:js面试试题总结一

下一篇:AngularJS移动端页面input无法输入