05 JS面向对象

来源:互联网 发布:mysql是大型数据库吗 编辑:程序博客网 时间:2024/04/30 13:44

声明对象

对象分两种,函数对象和普通对象,使用new Function()声明的都是函数对象,Object ,Function 是 JS 自带的函数对象,其它的都是普通对象。JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做proto 的内置属性,用于指向创建它的构造函数的原型对象。每个函数对象都有一个prototype属性,指向它的原型对象,普通对象只有一个proto属性。

使用字面量

下面代码使用字面量声明两个对象:Byron和Casper

var obj1 = {    nick: 'Byron和',    age: 20,    printName: function(){        console.log(obj1.nick);    }}var obj2 = {    nick: 'Casper',    age: 25,    printName: function(){        console.log(obj2.nick);    }}

这样构造有两个明显的问题:
1.太麻烦了,每次构建一个对象都是复制一遍代码
2.如果想个性化,只能通过手工赋值,使用者必需了解对象详细

这两个问题其实也是我们不能抛开类的重要原因,也是类的作用。
我们可以通过创建一个函数来实现自动创建对象的过程,至于个性化通过参数实现,开发者不必关注细节,只需要传入指定参数即可。

使用构造函数

function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name) } }var p1 = new Person('Tom', 28, 'Software Engineer');var p2 = new Person('Jack', 23, 'Doctor');p1 instanceof Person; // truep1 instanceof Object;// trueconsole.log(p1.constructor == Person); //trueconsole.log(p2.constructor == Person); //true

在内存中的对应关系如图所示:
这里写图片描述
p1 和 p2 都是 构造函数 Person 的实例

1、使用构造函数创建出来的p1和p2,可以使用instanceof 来判断它们是否是Person的实例
2、实例的构造函数属性(constructor)指向构造函数。

构造函数方法很好用,但是存在一个浪费内存的问题,如图所示,实例都从构造复制了一份属性和方法在内存中,能不能有一个所有实例都可以访问到的一个公共容器,重复的东西移动到公共容器里放一份就可以了?答案是可以的,原型对象(prototype)就是解决这个问题的。原型对象,最重要的作用就是把常量和方法独立到自身里,供给其它 “自己的对象” 使用,下面详细讲解原型对象。

在实例化对象的时候使用了new关键字,new的过程拆分成以下三步:
(1) var p1={}; 也就是说,初始化一个对象p
(2) p1.[[proto]] = Person.prototype;
(3) Person.call(p1); 也就是说构造p,也可以称之为初始化p

原型对象

示例代码:

function Person(nick, age){    this.nick = nick;    this.age = age;}Person.prototype.sayName = function(){    console.log(this.nick);}var p1 = new Person('Byron',20);var p2 = new Person('Casper',25);p1.sayName();//'Byron'p2.sayName();//'Casper'

这时候我们对应的关系是这样的:
这里写图片描述
如图所示:
p1和p2都具有了原型对象的sayName方法,它们都有一个[[proto]]属性,都指向Person的prototype(原型对象),无论Person有多少个实例,这样就实现了面向对象中的继承。
原型对象还有一个constructor属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person),关系如下代码:

p1.constructor == PersonPerson.prototype.constructor == Person

原型链

上文说过每个对象都有一个内置的[[proto]]属性,指向创建它的函数对象的原型对象prototype。
示例代码:

function Task(id){      this.id = id;  }  Task.prototype.status = "STOPPED";  Task.prototype.execute = function(args){      return "execute task_"+this.id+"["+this.status+"]:"+args;  }  var task1 = new Task(1);  var task2 = new Task(2);  task1.status = "ACTIVE";  task2.status = "STARTING";  print(task1.execute("task1"));  print(task2.execute("task2"));  

执行结果:
execute task_1[ACTIVE]:task1
execute task_2[STARTING]:task2
构造器会自动为task1,task2两个对象设置原型对象Task.prototype,这个对象被Task(在此最为构造器)的prototype属性引用,参看下图中的箭头指向。
这里写图片描述
由于Task本身仍旧是函数,因此其”[[proto]]”属性为Function.prototype, 而内建的函数原型对象的”[[proto]]”属性则为Object.prototype对象。最后Obejct.prototype的”[[proto]]”值为null。
结论:

1.原型和原型链是JS实现继承的一种模型。
2.原型链的形成是真正是靠[[proto]] 而非prototype

如下代码:

var animal = function(){};var dog = function(){};animal.price = 2000;//dog.prototype = animal;var tidy = new dog();console.log(dog.price) //undefinedconsole.log(tidy.price) // 2000

内存如图所示:
这里写图片描述

最后用一张图来总结JS原型与原型链:
这里写图片描述

0 0