js对象与继承小结,原型链知识总括思维导图

By admin in 4858美高梅 on 2019年4月3日

其壹思索导图是本身对Object,原型,原型链等学问的下结论,首要参考高程1书第肆章,写完才察觉那样多,现在也许会进展简要。内容恐怕会冒出偏差,欢迎批评指正。下载==>Github

ECMAScript帮助面向对象(OO)编制程序,但不使用类或然接口。对象足以在代码执行进度中开创和狠抓,因而有着动态性而非严峻定义的实业。在并没有类的景色下,能够运用下列情势创建对象。

  • 厂子形式,使用简便的函数创立对象,为对象添加属性和措施,然后重临对象。这么些方式后来被构造函数方式所代替。
  • 构造函数方式,能够创设自定义引用类型,能够像创立内置对象实例1样使用new操作符。然则,构造函数情势也有欠缺,即它的各样成员都爱莫能助得到复用,包括函数。由于函数能够不囿于于别的对象(即与对象拥有松弛耦合的表征),由此未曾理由不在四个对象间共享函数。
  • 原型方式,使用构造函数的prototype属性来内定这一个应该共享的天性和办法。组合使用构造函数方式和原型方式时,使用构造函数定义实例属性,而选择原型定义共享的性质和艺术。

  JavaScript首要通过原型链完成再而三。原型链的营造是由此将二个档次的实例赋值给另多个构造函数的原型完成的。那样,子类型就可见访问超类型的富有属性和方法,那一点与基于类的一连很相像。原型链的标题是指标实例共享全数继续的习性和措施,因而不适合单独使用。化解那个标题标技巧是借用构造函数,即在子类型构造函数的内部调用超类型构造函数。那样就能够完结每一种实例都具有本人的习性,同时仍是能够担保只行使构造函数方式来定义类型。使用最多的继续形式是结合继承,那种方式应用原型链继承共享的性质和方式,而经过借用构造函数继承实例属性。其它,还存在下列可供选拔的继承情势。

  • 原型式继承,能够在不必预先定义构造函数的场所下促成持续,其本质是实施对给定对象的浅复制。而复制得到的副本还足以获得进一步改造。
  • 寄生式继承,与原型式继承相当相似,也是依据有些对象或一些新闻创设一个目标,然后增强对象,最终回来对象。为了消除组合继承情势由于反复调用超类型构造函数而导致的低功能难题,能够将以此格局与构成继承壹起使用。
  • 寄生组合式继承,集寄生式继承和重组继承的优点与孤单,是兑现基于项目继承的最得力办法。

 

 

4858美高梅 1

JavaScript 面向对象与原型

 ECMAScript有二种开发方式:一.函数式(进程化);二.面向对象(OOP);

壹 创制对象
壹.不乏先例的创立对象

1

2

3

4

5

6

7

8

9

// 创建一个对象,然后给这个对象新的属性和方法;

var box = new Object(); // 创建一个Object对象;

box.name = ‘lee’; // 创建一个name属性并赋值;

box.age = 100;

box.run = function(){ // 创建一个run()方法并返回值;

return this.name+this.age+’运行中…’;

}

console.log(box.run()); // 输入属性和方法的值;

// 缺点:想创建类似的对象,就会产生大量的代码;

  1. 厂子形式创制对象

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 这种方法就是为了解决实例化对象产生大量代码重复的问题;

function createObject(name,age){ // 集中创建函数体;

var obj = new Object;         // 函数体内创建Object;

obj.name = name;

obj.age = age;

obj.run = function(){

return this.name+this.age+"运行中…";

};

return obj;

}

var box1 = createObject("lee",100); // 实例化;调用函数并传参;

var box2 = createObject("jack",200); // 实例二;

console.log(box1.run()+box2.run()); // 实例保持相对独立;

// 缺点:对象与实例的识别问题;无法搞清楚它们到底是那个对象的实例;

console.log(typeof box1); // Object;

三.构造函数成立对象

1

2

3

4

5

6

7

8

9

10

11

12

// ECMAScript采用构造函数(构造方法)可用来创建特定的对象;

function Box(name,age){          // 构造函数模式;

this.name = name;           // this代表对象Box;

this.age = age;

this.run = function(){

return this.name+this.age+"运行中…";

};

}

var box1 = new Box("lee",100); // 要创建对象的实例必须用new操作符;

var box2 = new Box("jack",200); // box1和box2都是Box对象的实例;

console.log(box1 instanceof Box); // true;很清晰的识别box1从属于Box;

// 使用构造函数,即解决了重复实例化的问题,有解决了对象识别的问题;

运用构造函数与工厂格局差别之处:
js对象与继承小结,原型链知识总括思维导图。(一).构造函数主意未有展现的创立对象(new Object);
(2).直接将质量和方法赋值给this对象;
(三).未有return语句;壹 // 构造函数规范:
(1).函数名(function Box)和实例化构造名(new Box)相同且大写;
(贰).通过构造函数成立实例对象,必须利用new运算符;

1

2

3

4

5

6

7

// 构造函数和普通函数的区别:

var box = new Box(‘lee’,100); // 构造模式调用;

Box(‘lee’,200); // 普通模式调用,无效;

 

var o = new Object();

Box.call(o,’jack’,200); // 对象冒充调用;

// 将Box对象作用域扩充到对象o;Box()方法的运行环境已经变成了对象o里;

构造函数的标题:
采取构造函数创设各个实例的时候,构造函数里的主意都要在种种实例上海重机厂新创制一遍;
因为ECMAScript中的函数是指标,因而每定义三个函数,也等于实例化了3个目的;
以那种办法开创函数,会促成区别的功力域链和标识符解析;

二 原型
// 我们创制的各样函数都有四个prototype(原型)属性,那个个性是二个对象;

// 用途:包括能够由特定类型的拥有实例共享的习性和办法;

// 通晓:prototype是通过调用构造函数成立的不行指标的原型对象;

// 使用原型的益处是足以让拥有目的实例共享它所包含的习性和艺术;

//
也便是说,不必在构造函数中定义对象新闻(属性/方法),而是能够平昔将那个新闻添加到原型中;

一.原型格局(prototype添加属性和办法)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

1.原型模式

function Box(){} // 声明构造函数;

Box.prototype.name = ‘Lee’; // 在原型里添加属性和方法;

Box.prototype.age = 100;

Box.prototype.run = function() {

return this.name+this.age+’运行中…’;

};

var box1 = new Box();

var box2 = new Box();

console.log(box1.run==box2.run); // =>true;方法引用的地址保持一致;

// 在原型中多了两个属性,这两个原型属性都是创建对象时自动生成的;

// 1.__proto__:构造函数指向原型对象的一个指针;它的作用:指向构造函数的原型的属性constructor;

14// IE浏览器在脚本访问__proto__会不能识别; 15

// 判断一个实例对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试;

console.log(Box.prototype.isPrototypeOf(box)); // =>true; 只要实例化对象,即都会指向;

 

// 原型模式的执行流程:

// 1.先查找构造函数对象的实例里的属性或方法,若有,立刻返回;

// 2.若构造函数对象的实例里没有,则去它的原型对象里找,若有,就返回;

 

// 虽然我们可以通过对象实例访问保存在原型中的值,但却不能访问通过对象实例重写原型中的值;

var box1 = new Box();

console.log(box1.name); // Lee; 原型里的值;

bo1.name = ‘jack’;

console.log(box1.name); // Jack;实例自己赋的值;

var box2 = new Box();

console.log(box2.name); // Lee;原型里的值;没有被box1修改;

// 如果想要box1继续访问原型里的值,可以把构造函数里的属性删除即可;

delete box1.name; // 删除实例自己的属性;

console.log(box1.name); // Lee; 原型里原来的值;

4858美高梅 2

2.原型与in操作符

哪些判断属性是在构造函数的实例里,依然在原型里?
能够用hasOwnProperty()函数来注明;
console.log(box.hasOwnProperty(‘name’)); //
实例里若有再次回到true,不然重返false;
in操作符会在经过对象能够访问给定属性时重回true,无论该属性存在与实例中依旧原型中;
console.log(‘name’ in box); //
=>true,存在实例中或原型中;叁.更简短的原型语法(原型+字面量形式)

叁.更不难的原型语法(原型+字面量格局)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

function Box(){};

Box.prototype = { // 以字面量形式创建包含属性和方法的新对象;

name:’Lee’,

age:100,

run:function(){

return this.name+this.age+’运行中…’;

}

};

 

// 使用构造函数创建原型对象和使用字面量创建原型对象在使用上基本相同;

// 但是,使用字面量创建的原型对象使用constructor属性不会指向实例,而是指向原型对象Object;构造函数的方式则相反;

var box = new Box();

console.log(box instanceof Box);

console.log(box instanceof Object);

console.log(box.constructor == Box); // 字面量方式,返回false;

console.log(box.constructor == Object); // 字面量方式,返回true;

// 如果想让字面量方式的constructor指向实例对象:

Box.prototype = {

constructor:Box, // 直接强制指向即可;

}

 

// PS:字面量方式为什么constructor会指向Object?

// 因为Box.prototype={}这种字面量写法就是创建一个新对象;

// 而每创建一个函数,就会同时创建它的prototype,这个对象也会自动获取constructor属性;

// 所以,新对象的constructor重写了Box原来的constructor,因此指向了新对象,

// 那个新对象没有指定构造函数,那么就默认为是Object;

四.原型的动态性(重写会覆盖从前的内容)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

// 原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型;

function Box(){};

Box.prototype = {

constructor:Box,

name:’Lee’,

age:100,

run:function(){

return this.age+’运行中…’;

}

};

Box.prototype = { // 原型重写了,覆盖了之前的原型;

age:200,

run:function(){

return this.age+’运行中…’;

}

}

var box = new Box();

console.log(box.run()); // =>200运行中…;

// 重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;对象实例引用的仍然是最初的原型;

5.原生目的的原型

//
原型对象不仅能够在自定义对象的景况下接纳,而是ECMAScript内置的引用类型都得以运用那种艺术,
// 并且放置的引用类型小编也是用了原型;
console.log(Array.prototype.sort); // =>function sort() { [native
code] };
console.log(String.prototype.substring); // =>function substring() {
[native code] };

陆.原型对象的标题

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

// 原型模式创建对象缺点:省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的;

// 而原型最大的有点就是共享,属性共享;

// 但是,如果原型中的属性包含引用类型(对象),共享就会存在一定问题;

function Box(){};

Box.prototype = {

constructor:Box,

name:’Lee’,

age:100,

family:[‘father’,’mother’],

run:function(){

return this.name+this.age+this.family;

}

};

var box1 = new Box();

box1.family.push(‘sister’); // 为box1的family属性添加了sister;而这个属性被共享到原型了;

console.log(box1.run()); // =>Lee100father,mother,sister;

var box2 = new Box();

console.log(box2.run()); // =>Lee100father,mother,sister;

// 数据共享导致实例化出的数据不能保存自己的特性;

7.组成使用构造函数情势(对象不共享的多少)和原型情势(对象共享的多寡)

1

2

3

4

5

6

7

8

9

10

11

12

13

// 为了解决构造传参和共享问题,组合构造函数+原型模式:

function Box(name,age){ // 不共享的使用构造函数;

this.name = name;

this.age = age;

this.family = [‘father’,’moter’];

};

Box.prototype = { // 共享的使用原型模式;

constructor:Box,

run:function(){

return this.name+this.age+this.family;

}

};

// PS:这种混合模式很好的解决了传参和引用共享的大难题;是创建对象比较好的方法;

八.动态原型格局(将原型封装到构造函数里)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// 原型模式,不管是否调用了原型中的共享方法,它都会初始化原型中的方法;

// 并且在声明一个对象时,构造函数+原型让人感觉怪异;最好把构造函数和原型封装到一起;

function Box(name,age){ // 将所有信息封装到构造函数体内;

this.name = name;

this.age = age;

// 当第一次调用构造函数时,run()方法不存在,然后执行初始化原型;

// 当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会载初始化;

// 这样既得到了封装,又实现了原型方法共享,并且属性都保持独立;

if(typeof this.run != ‘function’){ // 仅在第一次调用时初始化;

Box.prototype.run = function (){

return this.name+this.age+’运行中…’;

};

}

};

var box = new Box(‘lee’,10);

console.log(box.run());

// PS:使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系;

玖.寄生构造函数

1

2

3

4

5

6

7

8

9

10

// 寄生构造函数,其实就是工厂模式+构造模式;这种模式比较通用,但不能确定对象关系;

function Box(name,age){

var obj = new Object();

obj.name = name;

obj.age = age;

obj.run = function (){

return this.name+this.age+’运行中…’;

};

return obj;

}

三 继承
1.原型链

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

// 继承是面向对象中一个比较核心的概念;

// 其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承;

// 而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成;

// 实质:利用原型让一个引用类型继承另一个引用类型的属性和方法;

// 原型继承链:Box ==>> Desk ==>> Table;

function Box(){ // Box构造;

this.name = ‘Lee’;

}

function Desk(){ // Desk构造;

this.age = 100;

}

Desk.prototype = new Box(); // 通过创建Box实例,并赋值给Desk.prototype实现的;通过原型,形成链条;

// 实质是:重写了Desk的原型对象,取而代之的是一个新类型Box的实例;

// 也就是说原来存在于Box实例中的属性和方法,现在也存在与Desk.prototype中了;

var desk = new Desk();

console.log(desk.age); // 100;

console.log(desk.name); // =>Lee;

 

function Table(){

this.level = ‘AAA’;

}

Table.prototype = new Desk(); // 继续原型链继承;Table继承了Desk;

var table = new Table();

console.log(table.name); // Lee;

2.原型与实例的关联;

1

2

3

4

5

6

7

8

9

10

11

// PS:以上原型链继承缺少一环,那就是Object,所有的构造函数都继承自Object;

// 而继承Object是自动完成的,并不需要手动继承;

console.log(table instanceof Object); // =>true;

console.log(desk instanceof Table); // =>false;Desk是Table的超类;

console.log(table instanceof Desk); // =>true;

console.log(table instanceof Box); // =>true;

// 在JS中,被继承的函数称为超类型(父类,基类);

// 继承的函数称为子类型(子类,派生类);

// 继承问题:

// 字面量重写原型会中断关系;

// 子类型无法给超类型传递参数;

叁.借出构造函数(对象冒充)
// 为了消除引用共享和给超类型无法传参难点;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// 在子类型构造函数的内部调用超类型构造函数;

function Box(age){

this.name = [‘Lee’,’Jack’,’Hello’];

this.age = age;

}

function Desk(age){

// 继承了Box;同时还传递了参数;

// 这样一来,就会在新Desk对象上执行Box()函数中定义的所有对象初始化代码;

Box.call(this,age); // 对象冒充,Desk继承Box,并可以给超类型传参;

// 为了确保Box构造函数不会重写子类型的属性,可以在超类型构造函数后,再添加应该在子类型中定义的属性;

this.height = 175;

 

}

var desk = new Desk(200);               // 向Desk()函数传参,再通过函数冒用向Box()函数传参;

console.log(desk.age); // =>200;

console.log(desk.name); // =>[‘Lee’,’Jack’,’Hello’];

desk.name.push(‘AAA’); // =>添加的新数据,只添加给desk;

console.log(desk.name); // =>[‘Lee’,’Jack’,’Hello’,’AAA’];

4.整合继承(原型链+借用构造函数)
//
借用构造函数尽管缓解了引用共享和给超类型无法传参难点,可是从未应用原型,复用则无从聊起;所以须要组合继承格局;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// 使用原型链实现对原型属性和方法的继承;

// 通过借用构造函数来实现对实例属性的继承;

// 这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有他自己的属性;

function Box(age){                   // 构造函数;

this.name = [‘Lee’,’Jack’,’Hello’];

this.age = age;

}

Box.prototype.run = function(){            // 原型;

return this.name+this.age;

}

function Desk(age){

Box.call(this,age); // 继承属性; 对象冒充; 将Box对象的作用域扩充到Desk中,Desk就会继承Box里的属性和方法;

}                             

Desk.prototype = new Box(); // 继承方法; 原型链继承;

var desk = new Desk(100);

console.log(desk.run()); // =>Lee,Jack,Hello100

// 最常用的继承模式;

5.原型式继承?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// 这种继承借助原型并基于已有的对象创建对象,同时还不必因此创建自定义类型;

function obj(o){ // 传递一个字面量函数;

function F(){}; // 创建一个构造函数;

F.prototype = o; // 把字面量函数赋值给构造函数的原型;

return new F(); // 返回实例化的构造函数;

}

var box = { // 字面量对象;

name:’Lee’,

arr:[‘brother’,’sisiter’]

};

var box1 = obj(box);

console.log(box1.name); // =>Lee;

box1.name = ‘Jack’;

console.log(box1.name); // =>Jack;

 

console.log(box1.arr); // =>brother,sister;

box1.arr.push(‘father’); //

console.log(box1.arr); // =>brother,sister,father;

 

var box2 = obj(box);

console.log(box2.name); // =>Lee;

console.log(box2.arr); // =>brother,sister,father;引用类型共享了;

6.寄生式继承?

1

2

3

4

5

6

7

8

9

// 把原型式+工厂模式结合而来,目的是为了封装创建对象的过程;

// 创建一个仅用于封装继承过程的函数,

function create(o){ // 封装创建过程;

var f = obj(o);

f.run = function(){

return this.arr; // 同样会共享引用;

};

return f;

}

七.寄生组合式继承?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

// 之前说过,组合式继承是JS最常用的继承模式;

// 但是,组合式继承也有问题:

// 超类型在使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部;

function Box(name){

this.name = name;

this.arr = [‘brother’,’sister’];

}

Box.prototype.run = function(){

return this.name;

}

function Desk(name,age){

Box.call(this,name); // 第二次调用Box;

this.age = age;

}

Desk.prototype = new Box(); // 第一次调用Box;

 

// 寄生组合式继承:

// 通过借用构造函数来继承属性,

// 通过原型链的混成形式来继承方法;

// 解决了两次调用的问题;

function obj(o){

function F(){};

F.prototype = o;

return new F();

}

function create(box,desk){

var f = obj(box.prototype);

f.constructor = desk;

desk.prototype = f;

}

function Box(name){

this.name = name;

this.arr = [‘brother’,’sister’];

}

Box.prototype.run = function(){

return this.name;

}

function Desk(name,age){

Box.call(this,name);

this.age = age;

}

inheritPrototype(Box,Desk); // 通过这里实现继承;

 

var desk = new Desk(‘Lee’,100);

desk.arr.push(‘father’);

console.log(desk.arr);

console.log(desk.run());

 

var desk2 = new Desk(‘Jack’,200);

console.log(desk2.arr); // 两次引用问题解决;

四 小结

一.创设对象

目的足以在代码执行进度中开创和拉长,因而具有动态性而非严苛定义的实业;
在尚未类的事态下,能够应用下列情势创制对象;
(一).工厂格局:使用简易的函数创制对象,为指标添加属性和格局,然后再次回到对象;
这一个情势后来被构造函数情势所代表;
(二).构造函数方式:可以自定义引用类型,能够像创设内置对象实例一眼使用new操作符;
症结:它的各个成员都爱莫能助取得复用,包括函数;由于函数能够不囿于于其余对象,因而并未有理由不在七个对象间共享函数;
(叁).原型方式:使用函数的prototype属性来钦命那多少个应该共享的性格和章程;
结缘使用构造函数形式和原型情势时,使用构造函数定义实例属性,使用原型定义共享的属性和办法;

 

2.原型链

原型链的营造是透过将3个类型的实例赋值给另二个构造函数的原型达成的;
子类型能够访问到超类型的具有属性和措施;
原型链的题材是目的实例共享全数继续的性质和办法,因而不适合单独选拔;
消除方案:借用构造函数,即在子类型构造函数的内部调用超类型构造函数;
如此就能够做到各种实例都富有友好的质量,同时还是能确认保证只利用构造函数来定义类型;
使用最多的接续形式是构成继承;它选择原型链继承共享的性质和艺术,而因此借用构造函数继承实例属性;

三.持续形式

(1).原型式继承:能够在无需预先定义构造函数的景况下达成再三再四;其本质是推行对给定对象的浅复制;
而复制获得的副本开能够收获更进一步改造;
(2).寄生式继承:基于有个别对象或有个别音信创设一个指标,然后增强对象,最终回来对象;
为了缓解组合继承形式由于频仍调用超类型构造函数而招致的低效能难点,能够将那个格局与整合继承一起行使;
(3).寄生组合式继承:集寄生式继承和组合式继承的多少于寥寥,是实现基于项目继承的最可行办法;

面向对象与原型
ECMAScript有三种开发情势:一.函数式(进程化);二.面向对象(OOP); 1 创造对象
壹.无独有偶的创立对象 1 二 三 肆 5 陆 7 8 9 // 开立3个…

ECMAScript有三种开发格局:一.函数式(过程化);二.面向对象(OOP);

创设对象的情势

连带文章

相关搜索:

明日看吗

搜索技术库

重临首页

  • 手机版手机QQ音乐加快0.五天怎么得到?
  • 隐性调用php程序的章程
  • ASP.NET中Application全局对象用法实例浅析
  • 浅谈JavaScript中的Math.atan()方法的采取
  • JavaScript中反正弦函数Math.asin()的行使简介
  • JavaScript中的acos()方法运用详解

连锁频道:
HTML/CSS  HTML5  Javascript  jQuery  AJax教程  前端代码  正则表明式  Flex教程  WEB前端教程  

1 创制对象
一.家常的创造对象

  • 工厂格局,使用简便的函数成立对象,为对象添加属性和方法,然后回来对象。这几个情势后来被构造函数方式所替代。
  • 构造函数格局,能够创设自定义引用类型,能够像创造内置对象实例一样选拔new
    操作符。然则,构造函数形式也有缺点,即它的各样成员都心有余而力不足获取复用,包含函数。由于函数能够不局限于任何对象(即与目的具备松弛耦合的性格),因而未有理由不在五个对象间共享函数。
  • 原型格局,使用构造函数的prototype
    属性来钦赐这些应该共享的性质和方法。
  • 重组使用构造函数情势和原型格局,使用构造函数定义实例属性,而采取原型定义共享的性子和办法。

帮客评论

// 创建一个对象,然后给这个对象新的属性和方法;
  var box = new Object();       // 创建一个Object对象;
  box.name = 'lee';          // 创建一个name属性并赋值;
  box.age = 100;
  box.run = function(){        // 创建一个run()方法并返回值;
    return this.name+this.age+'运行中...';
  } 
  console.log(box.run());       // 输入属性和方法的值;
// 缺点:想创建类似的对象,就会产生大量的代码;

继承

  1. 工厂格局创立对象
  • JavaScript
    主要通过原型链达成接二连三。原型链的营造是经过将1个品类的实例赋值给另1个构造函数的原型完毕的。那样,子类型就可见访问超类型的有所属性和章程。
  • 原型链的标题是指标实例共享全部继续的性质和办法。
  • 借用构造函数,即在子类型构造函数的里边调用超类型构造函数。那样就足以做到每一种实例都享有温馨的天性,同时还是能担保只使用构造函数形式来定义类型。
  • 整合继承,那种形式应用原型链继承共享的品质和措施,而由此借用构造函数继承实例属性,使用最多。
  • 原型式继承,能够在无需预先定义构造函数的情景下实现再三再四,其本质是实施对给定对象的浅复制。而复制获得的副本还是能取得更进一步改造。
  • 寄生式继承,与原型式继承极度相像,也是依照有些对象或有个别消息成立3个目的,然后增强对象,最后回到对象。为了消除组合继承方式由于频仍调用超类型构造函数而造成的低功效难题,能够将那一个方式与组合继承一起利用。
  • 寄生组合式继承,集寄生式继承和构成继承的独到之处与一身,是落实基于项目继承的最实用办法。
// 这种方法就是为了解决实例化对象产生大量代码重复的问题;
  function createObject(name,age){    // 集中创建函数体;
    var obj = new Object;         // 函数体内创建Object;
    obj.name = name; 
    obj.age = age;
    obj.run = function(){
      return this.name+this.age+"运行中...";
    };
    return obj;
  }
  var box1 = createObject("lee",100);   // 实例化;调用函数并传参;
  var box2 = createObject("jack",200);  // 实例二;
  console.log(box1.run()+box2.run());   // 实例保持相对独立;
// 缺点:对象与实例的识别问题;无法搞清楚它们到底是那个对象的实例;
  console.log(typeof box1);        // Object;

4858美高梅 3

叁.构造函数创设对象

好好学习

// ECMAScript采用构造函数(构造方法)可用来创建特定的对象;
  function Box(name,age){          // 构造函数模式;
    this.name = name;           // this代表对象Box;
    this.age = age;
    this.run = function(){
      return this.name+this.age+"运行中...";
    };
  }
  var box1 = new Box("lee",100);     // 要创建对象的实例必须用new操作符;
  var box2 = new Box("jack",200);    // box1和box2都是Box对象的实例;
  console.log(box1 instanceof Box);   // true;很清晰的识别box1从属于Box;
// 使用构造函数,即解决了重复实例化的问题,有解决了对象识别的问题;

 利用构造函数与工厂情势不一致之处: (一).构造函数形式未有显得的创立对象(new Object);
(二).直接将品质和措施赋值给this对象;
(三).未有return语句;一 // 构造函数规范:
(一).函数名(function Box)和实例化构造名(new Box)相同且大写;
(二).通过构造函数成立实例对象,必须利用new运算符;

// 构造函数和普通函数的区别:
  var box = new Box('lee',100);        // 构造模式调用;
  Box('lee',200);               // 普通模式调用,无效;

  var o = new Object();
  Box.call(o,'jack',200);           // 对象冒充调用;
  // 将Box对象作用域扩充到对象o;Box()方法的运行环境已经变成了对象o里;

 构造函数的题材: 使用构造函数创造各样实例的时候,构造函数里的情势都要在每种实例上海重机厂复创制2回;
因为ECMAScript中的函数是指标,由此每定义1个函数,也便是实例化了一个目的;
以那种办法开创函数,会促成分裂的作用域链和标识符解析;

二 原型 // 大家创设的各种函数都有一个prototype(原型)属性,那个性格是三个对象;

// 用途:包涵能够由特定类型的有所实例共享的习性和措施;

// 掌握:prototype是透过调用构造函数创造的分外指标的原型对象;

// 使用原型的便宜是足以让具有指标实例共享它所涵盖的属性和措施;

//
也正是说,不必在构造函数中定义对象音信(属性/方法),而是能够一向将那么些新闻添加到原型中;

一.原型方式(prototype添加属性和办法)

1.原型模式
  function Box(){}                // 声明构造函数;
  Box.prototype.name = 'Lee';           // 在原型里添加属性和方法;
  Box.prototype.age = 100;
  Box.prototype.run = function() {
    return this.name+this.age+'运行中...';
  };
  var box1 = new Box();
  var box2 = new Box();
  console.log(box1.run==box2.run);        // =>true;方法引用的地址保持一致;
// 在原型中多了两个属性,这两个原型属性都是创建对象时自动生成的;
// 1.__proto__:构造函数指向原型对象的一个指针;它的作用:指向构造函数的原型的属性constructor;
 14// IE浏览器在脚本访问__proto__会不能识别; 15 
// 判断一个实例对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试;
  console.log(Box.prototype.isPrototypeOf(box));  // =>true; 只要实例化对象,即都会指向;

// 原型模式的执行流程:
// 1.先查找构造函数对象的实例里的属性或方法,若有,立刻返回;
// 2.若构造函数对象的实例里没有,则去它的原型对象里找,若有,就返回;

// 虽然我们可以通过对象实例访问保存在原型中的值,但却不能访问通过对象实例重写原型中的值;
  var box1 = new Box();
  console.log(box1.name);              // Lee; 原型里的值;
  bo1.name = 'jack';
  console.log(box1.name);              // Jack;实例自己赋的值;
  var box2 = new Box();
  console.log(box2.name);              // Lee;原型里的值;没有被box1修改;
  // 如果想要box1继续访问原型里的值,可以把构造函数里的属性删除即可;
  delete box1.name;                 // 删除实例自己的属性;
  console.log(box1.name);              // Lee; 原型里原来的值;

4858美高梅 4

2.原型与in操作符

哪些判断属性是在构造函数的实例里,依然在原型里?
能够用hasOwnProperty()函数来注明;
console.log(box.hasOwnProperty(‘name’)); //
实例里若有再次回到true,不然再次来到false;
in操作符会在通过对象能够访问给定属性时回来true,无论该属性存在与实例中依旧原型中;
console.log(‘name’ in box); //
=>true,存在实例中或原型中;三.更简明的原型语法(原型+字面量情势)

三.更简短的原型语法(原型+字面量形式)

  function Box(){};
  Box.prototype = {                 // 以字面量形式创建包含属性和方法的新对象;
    name:'Lee',
    age:100,
    run:function(){
      return this.name+this.age+'运行中...';
    }
  };

// 使用构造函数创建原型对象和使用字面量创建原型对象在使用上基本相同;
// 但是,使用字面量创建的原型对象使用constructor属性不会指向实例,而是指向原型对象Object;构造函数的方式则相反;
  var box = new Box();
  console.log(box instanceof Box);
  console.log(box instanceof Object);  
  console.log(box.constructor == Box);      // 字面量方式,返回false;
  console.log(box.constructor == Object);     // 字面量方式,返回true;
  // 如果想让字面量方式的constructor指向实例对象:
  Box.prototype = {
    constructor:Box,              // 直接强制指向即可;
  }

  // PS:字面量方式为什么constructor会指向Object?
  // 因为Box.prototype={}这种字面量写法就是创建一个新对象;
  // 而每创建一个函数,就会同时创建它的prototype,这个对象也会自动获取constructor属性;
  // 所以,新对象的constructor重写了Box原来的constructor,因此指向了新对象,
  // 那个新对象没有指定构造函数,那么就默认为是Object;

四.原型的动态性(重写会覆盖此前的始末)

// 原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型;
  function Box(){};
  Box.prototype = {
    constructor:Box,
    name:'Lee',
    age:100,
    run:function(){
      return this.age+'运行中...';
    }
  };
  Box.prototype = {                // 原型重写了,覆盖了之前的原型;
    age:200,
    run:function(){
      return this.age+'运行中...';
    }
  }
  var box = new Box();
  console.log(box.run());              // =>200运行中...;
  // 重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;对象实例引用的仍然是最初的原型;

五.原生对象的原型

//
原型对象不仅能够在自定义对象的场所下行使,而是ECMAScript内置的引用类型都得以采纳那种办法,
// 并且放置的引用类型我也是用了原型;
console.log(Array.prototype.sort); // =>function sort() { [native
code] };
console.log(String.prototype.substring); // =>function substring() {
[native code] };

陆.原型对象的难题

// 原型模式创建对象缺点:省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的;
// 而原型最大的有点就是共享,属性共享;
// 但是,如果原型中的属性包含引用类型(对象),共享就会存在一定问题;
  function Box(){};
  Box.prototype = {
    constructor:Box,
    name:'Lee',
    age:100,
    family:['father','mother'],
    run:function(){
      return this.name+this.age+this.family;
    }
  };
  var box1 = new Box();
  box1.family.push('sister');           // 为box1的family属性添加了sister;而这个属性被共享到原型了;
  console.log(box1.run());            // =>Lee100father,mother,sister;
  var box2 = new Box();
  console.log(box2.run());            // =>Lee100father,mother,sister;
  // 数据共享导致实例化出的数据不能保存自己的特性;

七.结合使用构造函数形式(对象不共享的数目)和原型形式(对象共享的数码)

// 为了解决构造传参和共享问题,组合构造函数+原型模式:
  function Box(name,age){             // 不共享的使用构造函数;
    this.name = name;
    this.age = age;
    this.family = ['father','moter'];
  };
  Box.prototype = {                // 共享的使用原型模式;
    constructor:Box,
    run:function(){
      return this.name+this.age+this.family;
    }
  };
  // PS:这种混合模式很好的解决了传参和引用共享的大难题;是创建对象比较好的方法;

八.动态原型形式(将原型封装到构造函数里)

// 原型模式,不管是否调用了原型中的共享方法,它都会初始化原型中的方法;
// 并且在声明一个对象时,构造函数+原型让人感觉怪异;最好把构造函数和原型封装到一起;
  function Box(name,age){              // 将所有信息封装到构造函数体内;
    this.name = name;
    this.age = age; 
    // 当第一次调用构造函数时,run()方法不存在,然后执行初始化原型;
    // 当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会载初始化;
    // 这样既得到了封装,又实现了原型方法共享,并且属性都保持独立;
    if(typeof this.run != 'function'){      // 仅在第一次调用时初始化;
      Box.prototype.run = function (){
        return this.name+this.age+'运行中...';
      };
    }
  };
  var box = new Box('lee',10);
  console.log(box.run());
// PS:使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系;

玖.寄生构造函数

// 寄生构造函数,其实就是工厂模式+构造模式;这种模式比较通用,但不能确定对象关系;
  function Box(name,age){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.run = function (){
      return this.name+this.age+'运行中...';
    };
    return obj;
  }

三 继承 1.原型链

// 继承是面向对象中一个比较核心的概念;
// 其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承;
// 而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成;
// 实质:利用原型让一个引用类型继承另一个引用类型的属性和方法;
  // 原型继承链:Box ==>> Desk ==>> Table;
  function Box(){                 // Box构造;
    this.name = 'Lee';
  }
  function Desk(){                // Desk构造;
    this.age = 100;
  }
  Desk.prototype = new Box();           // 通过创建Box实例,并赋值给Desk.prototype实现的;通过原型,形成链条;  
                          // 实质是:重写了Desk的原型对象,取而代之的是一个新类型Box的实例;
                          // 也就是说原来存在于Box实例中的属性和方法,现在也存在与Desk.prototype中了;
  var desk = new Desk();
  console.log(desk.age);             // 100;
  console.log(desk.name);             // =>Lee;

  function Table(){
    this.level = 'AAA';
  }
  Table.prototype = new Desk();          // 继续原型链继承;Table继承了Desk;
  var table = new Table();
  console.log(table.name);            // Lee;

贰.原型与实例的涉及;

// PS:以上原型链继承缺少一环,那就是Object,所有的构造函数都继承自Object;
// 而继承Object是自动完成的,并不需要手动继承;
  console.log(table instanceof Object);       // =>true;
  console.log(desk instanceof Table);        // =>false;Desk是Table的超类;
  console.log(table instanceof Desk);        // =>true;
  console.log(table instanceof Box);         // =>true;
// 在JS中,被继承的函数称为超类型(父类,基类);
// 继承的函数称为子类型(子类,派生类);
// 继承问题:
// 字面量重写原型会中断关系;
// 子类型无法给超类型传递参数;

三.借出构造函数(对象冒充)
// 为了化解引用共享和给超类型不能够传参难点;

// 在子类型构造函数的内部调用超类型构造函数;
  function Box(age){
    this.name = ['Lee','Jack','Hello'];
    this.age = age;
  }
  function Desk(age){
    // 继承了Box;同时还传递了参数;
    // 这样一来,就会在新Desk对象上执行Box()函数中定义的所有对象初始化代码;
    Box.call(this,age);              // 对象冒充,Desk继承Box,并可以给超类型传参;
    // 为了确保Box构造函数不会重写子类型的属性,可以在超类型构造函数后,再添加应该在子类型中定义的属性;
    this.height = 175;

  }
  var desk = new Desk(200);               // 向Desk()函数传参,再通过函数冒用向Box()函数传参;
  console.log(desk.age);               // =>200;
  console.log(desk.name);              // =>['Lee','Jack','Hello'];
  desk.name.push('AAA');               // =>添加的新数据,只添加给desk;
  console.log(desk.name);              // =>['Lee','Jack','Hello','AAA'];

4.整合继承(原型链+借用构造函数)
//
借用构造函数尽管缓解了引用共享和给超类型不能够传参难题,不过尚未接纳原型,复用则无从聊起;所以必要整合继承方式;

// 使用原型链实现对原型属性和方法的继承;
// 通过借用构造函数来实现对实例属性的继承;
// 这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有他自己的属性;
  function Box(age){                    // 构造函数;
    this.name = ['Lee','Jack','Hello'];
    this.age = age;
  }
  Box.prototype.run = function(){            // 原型;
    return this.name+this.age;
  }
  function Desk(age){
    Box.call(this,age);              // 继承属性; 对象冒充; 将Box对象的作用域扩充到Desk中,Desk就会继承Box里的属性和方法;
  }                              
  Desk.prototype = new Box();            // 继承方法; 原型链继承;
  var desk = new Desk(100);
  console.log(desk.run());              // =>Lee,Jack,Hello100
// 最常用的继承模式;

伍.原型式继承?

// 这种继承借助原型并基于已有的对象创建对象,同时还不必因此创建自定义类型;
  function obj(o){                // 传递一个字面量函数;
    function F(){};               // 创建一个构造函数;
    F.prototype = o;              // 把字面量函数赋值给构造函数的原型;
    return new F();               // 返回实例化的构造函数;
  }
  var box = {                   // 字面量对象;
    name:'Lee',
    arr:['brother','sisiter']
  };
  var box1 = obj(box);
  console.log(box1.name);             // =>Lee;
  box1.name = 'Jack';
  console.log(box1.name);             // =>Jack;

  console.log(box1.arr);             // =>brother,sister;
  box1.arr.push('father');            // 
  console.log(box1.arr);             // =>brother,sister,father;

  var box2 = obj(box);
  console.log(box2.name);             // =>Lee;
  console.log(box2.arr);             // =>brother,sister,father;引用类型共享了;

6.寄生式继承?

// 把原型式+工厂模式结合而来,目的是为了封装创建对象的过程;
// 创建一个仅用于封装继承过程的函数,
  function create(o){               // 封装创建过程;
    var f = obj(o);
    f.run = function(){
      return this.arr;            // 同样会共享引用;
    };
    return f;
  }

七.寄生组合式继承?

// 之前说过,组合式继承是JS最常用的继承模式;
// 但是,组合式继承也有问题:
// 超类型在使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部;
  function Box(name){
    this.name = name;
    this.arr = ['brother','sister'];
  }
  Box.prototype.run = function(){
    return this.name;
  }
  function Desk(name,age){
    Box.call(this,name);            // 第二次调用Box;
    this.age = age;
  }
  Desk.prototype = new Box();           // 第一次调用Box;

// 寄生组合式继承:
// 通过借用构造函数来继承属性,
// 通过原型链的混成形式来继承方法;
// 解决了两次调用的问题;
  function obj(o){
    function F(){};
    F.prototype = o;
    return new F();
  }
  function create(box,desk){
    var f = obj(box.prototype);
    f.constructor = desk;
    desk.prototype = f;
  }
  function Box(name){
    this.name = name;
    this.arr = ['brother','sister'];
  }
  Box.prototype.run = function(){
    return this.name;
  }
  function Desk(name,age){
    Box.call(this,name);
    this.age = age;
  }
  inheritPrototype(Box,Desk);            // 通过这里实现继承;

  var desk = new Desk('Lee',100);
  desk.arr.push('father');
  console.log(desk.arr);
  console.log(desk.run());

  var desk2 = new Desk('Jack',200);
  console.log(desk2.arr);              // 两次引用问题解决;

四 小结


一.成立对象

对象足以在代码执行进程中成立和拉长,因而全体动态性而非严苛定义的实业;
在并没有类的情景下,能够行使下列格局创制对象;
(壹).工厂形式:使用简便的函数创立对象,为目的添加属性和方法,然后重返对象;
其1格局后来被构造函数格局所替代;
(2).构造函数方式:能够自定义引用类型,可以像成立内置对象实例一眼使用new操作符;
缺陷:它的种种成员都不也许赢得复用,包蕴函数;由于函数能够不局限于其它对象,因而并未有理由不在两个指标间共享函数;
(叁).原型方式:使用函数的prototype属性来钦赐那1个应该共享的本性和格局;
4858美高梅 ,重组使用构造函数格局和原型格局时,使用构造函数定义实例属性,使用原型定义共享的属性和格局;

2.原型链

原型链的塑造是因此将三个类型的实例赋值给另一个构造函数的原型完成的;
子类型能够访问到超类型的持有属性和章程;
原型链的难点是指标实例共享所有继续的习性和方法,因而不妥帖单独行使;
化解方案:借用构造函数,即在子类型构造函数的中间调用超类型构造函数;
如此就能够完结各样实例都拥有友好的习性,同时仍是能够确定保证只利用构造函数来定义类型;
利用最多的存在延续情势是整合继承;它应用原型链继承共享的天性和艺术,而经过借用构造函数继承实例属性;

3.继续格局

(一).原型式继承:能够在无需预先定义构造函数的场馆降低成连续;其本质是实行对给定对象的浅复制;
而复制得到的副本开能够取得更为改造;
(2).寄生式继承:基于有个别对象或某个新闻成立三个目的,然后增强对象,最终回到对象;
为了消除组合继承形式由于频仍调用超类型构造函数而致使的低功用难点,能够将以此方式与组合继承1起利用;
(三).寄生组合式继承:集寄生式继承和组合式继承的有点于1身,是贯彻基于项目继承的最可行情势;

您大概感兴趣的稿子:

  • JavaScript中的对象和原型(壹)
  • 举例表明JavaScript中的实例对象与原型对象
  • 深刻浅析JavaScript面向对象和原型函数
  • Javascript中赢得对象的原型对象的措施小结
  • 深刻掌握javascript构造函数和原型对象
  • JS面向对象基础讲解(工厂情势、构造函数格局、原型形式、混合形式、动态原型格局)
  • 构造函数+原型情势结构js自定义对象(最通用)
  • javascript当中的代码嗅探扩大原生对象和原型(prototype)
  • JS原型对象通俗”唱法”
  • Javascript中的对象和原型(二)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有