Js高等编程笔记--面向对象的程序设计

明白对象

属性范例

1.数据属性

特征:

  • Configurable : 示意可否经由过程 delete 删除属性,可否修正属性特征,可否把属性改成接见器属性
  • Enumerable : 示意可否经由过程 for in 轮回返回
  • Writable : 示意可否修正属性的值
  • Value : 包括属性的值,读取或写入现实上操纵的是这个值

2.接见器属性

特征:

  • Configurable : 示意可否经由过程 delete 删除属性,可否修正属性特征,可否把属性改成接见器属性
  • Enumerable : 示意可否经由过程 for in 轮回返回
  • Get : 读取时挪用的参数.默许值为 undefined
  • Set : 写入时挪用的参数。 默许值为 undefined

3.注重:

  • 接见器属性不能直接定义,必需运用 Object.defineProperty()定义。
  • 修正属性默许的特征,必需运用 Object.defineProperty()要领
  • get,set,并不一定要定义,只定义 get 为只读,只定义 set 为只写不可读。
  • 定义多个属性可以运用 Object.defineProperties()要领
  • 读取属性的特征,运用 Object.getOwnPropertyDescriptor()

建立对象

1.工场形式

定义一个要领接收参数,用于建立对象,并将其返回

function createPerson(name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    return o;
}
var person1 = createPerson('andy_chen', 18);
var person2 = createPerson('andy_chen', 18);

工场形式可以建立多个相似对象的题目,却没处理对象辨认的题目。比方person1的范例是什么

2.组织函数形式 :

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function() {
        alert(this.name);
    };
}
var person1 = new Person('andy_chen', 18);
var person2 = new Person('andy_chen', 18);
person1.sayName();
person2.sayName();

运用 new 操纵符。现实上有以下 4 个步骤:

  • 建立一个新对象
  • 将组织函数的作用域赋给对象(使 this 指向新对象)
  • 实行组织要领(为这个对象增加属性)
  • 返回新对象

组织函数的题目在于,每一个要领都要在每一个实例中从新建立一遍。即例子中,person1和person2的sayName的不相等的。

然则,完成一样的功用的要领,却每一个实例都要建立一遍,这明显不合理,所以,又涌现了下面的原型形式

3.原型形式:

明白原型对象

一图胜千言:
《Js高等编程笔记--面向对象的程序设计》

  • 只需建立了一个新函数,就会依据一组特定规则为该函数建立一个 prototype,这个属性指向函数的对象原型。
  • 对象原型中,则默许有一个 constructor 属性,指向该新函数。
  • 经由过程新函数建立的实例,有一个[[prototype]]属性(在 chrome,firefox,safari 中该属性即为proto),指向了新函数的 prototype。

    注重:该属性仅仅是实行组织函数的 prototype,也即是说,他们与组织函数没有直接联系了

  • 读取某个对象的属性时,会先在实例上找,假如没找到,则进一步在实例上的 prototype 属性上找
  • 为实例增加属性的时刻会屏蔽掉原型上属性。这个时刻纵然置为 null 也没法接见到原型上的属性,只要经由过程 delete 删掉以后才可以
  • XXX.prototype.isPrototype(xxx), 可以用这个要领剖断对象是不是是该实例的原型对象
  • Object.getPrototypeOf() 用这个可以猎取实例对应的原型对象 (ES5 新增要领)

in 操纵符

  • 零丁运用时: in 操纵符 可以肯定属性是不是存在于对象上(不管是存在于实例上照样原型上)
  • 用于 for 轮回中时,返回的是一切可以经由过程对象接见的,可罗列的属性。(IE8 中,假如开发者自定义 toString 相似的体系不可罗列的要领,浏览器照样不会将它遍历出来)

ES5:Object.keys() 可以返回一个包括一切可罗列属性的字符串数组

Object.getOwnPropertyNames() 可以返回一切实例属性,不管是不是可罗列

//原型形式的完成:
function Person() {}

Person.prototype.name = 'andy chen';

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

更简朴的原型语法

重写全部 prototype,不过会致使 constructor 转变。所以须要从新指定 constructor.

//更简朴的原型语法
function Person() {}
Person.prototype = {
    constructor: Person, //因为这类写法会覆蓋掉本来的Person.prototype,须要从新为constructor赋值
    name: 'andy chen',
    sayName: function() {
        alert(this.name);
    }
};
var person1 = new Person();
var person2 = new Person();

原型形式的题目:一切实例都同享一个prototype,相似上面的例子,person1,person2的name属性是同享的。假如修正个中一个,会致使另一个也受影响。所以,才会涌现下面组织函数与原型形式组合运用

4.组合运用组织函数和原型形式

建立自定义范例最常见的体式格局就是组合运用组织函数和原型形式

组织函数定义实例属性,而原型形式用于定义要领和同享的属性. 所以,上面的例子可以改写成如许:

function Person(name) {
    this.name = name;
}
Person.prototype = {
    constructor: Person,
    sayName: function() {
        alert(this.name);
    }
};
var person1 = new Person('andy chen');
var person2 = new Person('andy chen');

除了运用组合形式建立对象,另有以下几种体式格局,可以针对差别的状况挑选。

5.动态原型形式

在组织要领中,推断是不是是第一次进入运用组织要领,假如是,则增加一系列的要领到原型上

6.寄生组织函数形式

类基本思想是建立一个函数,该函数的作用仅仅是封装建立对象的代码然后再返回新建立的对象。

7.稳妥组织函数形式:

稳妥对象

指的是没有大众属性,而且其要领也不援用 this 对象。最合适用于一些平安的环境或许在防备数据被其他顺序修改时运用

稳妥组织函数

遵照与寄生组织函数相似的形式,但有两点差别:

  • 新建立的对象实例不援用 this.
  • 不运用 new 操纵符挪用组织函数

继承

OO 言语平常具有两种继承体式格局:接口继承(只继承要领署名)以及完成继承(继承现实要领)
ES 没法像其他 OO 言语一样支撑接口继承,只能依托原型链完成 完成继承

1. 原型链

要相识原型链的观点,先回忆一下组织函数,原型和实例之间的关联(参考图 6-1)

  • 每一个组织函数都有一个原型对象,原型对象包括一个指向组织函数的指针.
  • 每一个实例都包括一个指向原型对象的内部指针的内部属性(在 chrome 中平常为proto属性)

那末,假如我们有个新的组织函数,并让它的原型对象即是另一个范例的实例,效果会如何.

关于这个新的组织函数,它的原型对象就变成了另一个范例的实例,而这个实例中,又包括一个内部属性,指向了另一个原型对象(该原型对象内部 constructor 指向另一个组织函数),假如这个原型对象又是另一个范例的实例,则它又包括了一个内部属性,继承指向上层的原型对象。如许层层递进,就形成了原型链。

如下图:
《Js高等编程笔记--面向对象的程序设计》

特性

  • 在实例中搜刮属性的时刻,就是基于原型链来搜刮的,先搜刮实例,再在原型链上一层层往上搜,直到找到或许到原型链末尾才会停下来
  • 因为一切援用范例都继承了 Object,所以原型链的最顶层是 Object
  • 运用原型链完成继承时,不能运用对象字面量建立原型要领,因为如许会重写原型链

原型链完成继承的体式格局:

function Animal() {
    this.name = 'animal';
}
Animal.prototype.getName = function() {
    return this.name;
};

function Cat() {
    this.catName = 'cat';
}
Cat.prototype = new Animal();

var cat1 = new Cat();
var cat2 = new Cat();
alert(cat1.getName()); //因为第10行,将Cat的原型指向Animal的实例,因为实例中有指向Animal.prototype的指针。所以,这里可以接见到getName()

cat1.name = 'changed name';
alert(cat2.getName());

原型链的题目:

  • 运用原型链,因为是运用新的实例作为子范例的原型,实例中却包括了父范例的属性,所以本来父范例的属性,就都到了子范例的原型上了。这就会形成子范例的差别实例会同享同个属性.如上例子中,第 15 行,转变 cat1 实例的 name 属性影响到了 cat2 的 name 属性
  • 建立子范例的时刻,不能向父范例通报参数

2. 借用组织函数

因为原型链存在题目,所以便涌现了借用组织函数的要领
在子范例的组织要领中,挪用父范例的组织要领:SuperType.call(this); 将父范例的属性增加到子范例上,而且可以通报参数给父范例

借用组织函数完成继承的体式格局:

function Animal() {
    this.name = 'animal';
}
function Cat() {
    Animal.call(this);
}
var cat1 = new Cat();
var cat2 = new Cat();
cat1.name = 'changed name';
alert(cat1.name); //changed name
alert(cat2.name); //animal //借用组织函数的体式格局,各实例之间的属性便不会相互影响

借用组织函数题目:

相似建立对象纯真运用组织要领一样,也会形成公有的要领没法公用。所以平常也很少零丁运用此体式格局

3. 组合继承

组合原型链以及借用组织函数

  • 运用原型链完成对原型属性和要领的继承
  • 借用组织函数来完成对实例中属性的继承。
function Animal() {
    this.name = 'animal';
}
Animal.prototype.getName = function() {
    return this.name;
};

function Cat() {
    Animal.call(this); //借用组织函数
}
Cat.prototype = new Animal(); //原型链体式格局
Cat.prototype.constructor = Cat;

//这里可以
var cat1 = new Cat();
var cat2 = new Cat();
cat1.name = 'changed name';

alert(cat1.getName()); //changed name
alert(cat2.getName()); //animal

组合继承的题目:

父类的属性会存在于子范例的原型上,致使被差别实例同享。虽然因为借用组织函数以后,致使实例上又重写了这些属性,所以每一个实例有各自的属性。

别的,instanceof 和 isPrototypeOf 可以辨认基于组合继承建立的对象

**组合继承,并不圆满
因为我们只须要继承父范例原型上的属性罢了,不须要父范例实例的属性。
另有更好的要领,但我们首先要先相识一下其他继承体式格局**

4. 原型式继承

//假如o为某个对象的prototype,则object返回的 对象,包括了该对象原型上的一切要领
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

Es5 新增的 Object.create() ,相似如许。 在没有必要建立组织函数,只想让一个对象与另一个对象坚持相似的状况下,原型式继承是完全可以胜任的。不过,包括援用范例值的属性始终会同享

5. 寄生式继承

在复制新对象后,继承以某种体式格局加强对象,即为寄生式继承。

function createAnother(original) {
    var clone = object(original);
    clone.sayHi = function() {
        doSomeThing();
    };
    return clone;
}

在重要斟酌对象而不是自定义范例和组织函数的时刻,合适运用寄生式继承

瑕玷: 相似纯真的组织函数形式运用,函数不能复用

6. 寄生组合式继承

经由过程原型式继承,继承父类的原型要领。再经由过程组织函数要领,继承父类的属性。

function Animal() {
    this.name = 'animal';
}
Animal.prototype.getName = function() {
    return this.name;
};

function Cat() {
    Animal.call(this); //借用组织函数
}

//原型继承体式格局
function object(superProto) {
    function F() {}
    F.prototype = superProto;
    return new F();
}

Cat.prototype = object(Animal.prototype); //经由过程一个空的函数作为序言,将空函数的原型指向父范例原型,并将子范例的原型指向这个空函数的实例。便只继承父类原型上的属性及要领
Cat.prototype.constructor = Cat;
//这里可以以后增加子类的要领
Cat.prototype.run = function() {
    alert('cat run');
};

var cat1 = new Cat();
var cat2 = new Cat();
cat1.name = 'changed name';
alert(cat1.getName()); //changed name
alert(cat2.getName()); //animal

末了,寄生组合式继承是援用范例最理想的继承范式。
上述代码还能再进一步优化。

//原型继承体式格局
function object(superProto) {
    function F() {}
    F.prototype = superProto;
    return new F();
}
//公用的继承要领
function inheritPrototype(subType, superType) {
    subType.prototype = object(superType.prototype);
    subType.prototype.constructor = subType;
}

function Animal() {
    this.name = 'animal';
}
Animal.prototype.getName = function() {
    return this.name;
};

function Cat() {
    Animal.call(this); //借用组织函数
}

inheritPrototype(Cat, Animal); //挪用此要领继承原型

//这里可以以后增加子类的要领
Cat.prototype.run = function() {
    alert('cat run');
};
var cat1 = new Cat();
var cat2 = new Cat();
cat1.name = 'changed name';
alert(cat1.getName()); //changed name
alert(cat2.getName()); //animal

小结

这是 js 对象的建立以及继承,es6 中新增了关键字classextend。轻易我们举行面向对象的编程。

然则明白背地的继承道理对我们编程过程当中也是极有协助的

:)

喜好就珍藏或许点个赞呗 !!

    原文作者:chen4342024
    原文地址: https://segmentfault.com/a/1190000017711988
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞