您的位置:首页 > Web前端 > JavaScript

重识Javascript系列---ECMAScript继承

2016-01-13 17:50 591 查看

1 、工厂模式

即用函数来封装以特定接口创建对象的细节

function createPerson (name,age,sex) {
var person = new Object();
person.name = name;
person.age = age;
person.sex = sex;
person.sayName = function() {
console.log(this.name);
};
return person;
}
var person1 = createPerson("Liz","21","female");
var person2 = createPerson("Cyt","21","male");
person1.sayName();
person2.sayName();


2 、构造函数模式

自定义构造函数,自定义属性和方法

function Person (name,age,sex) {//构造函数应大写第一个字母,其他函数应小写第一个字母
this.name = name;
this.age = age;
this.sex = sex;
this.sayName = function() {
console.log(this.name);
};
}
var person1 = new Person("Liz","21","female");
var person2 = new Person("Cyt","21","male");
person1.sayName();
person2.sayName();


2.1 构造函数的调用

作为构造函数使用

var person = new Person("Liz","21","female");
person.sayName();


作为普通函数使用

Person("Cyt","21","male");
window.sayName();


在另一个对象中调用

var o = new Object();
Person.cell(o,"Cyt","21","male");//使用cell或apply在某个特殊对象的作用域中调用Person()构造函数,调用完成后,此特殊对象就有了Person的所有属性和方法。
o.sayName();


2.2 构造函数的缺点

ECMAScriptS中函数是对象,因此每定义一个函数,也就是实例化了一个对象。

所以每个Person实例都包含了不同的方法显示name属性,然而创建多个方法完成同样任务function实例根本没必要。故可将函数定义转移到构造函数外部:

function Person (name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayName = sayName;
}
function sayName() {
console.log(this.name);
};
var person = new Person("Liz","21","female"); person.sayName();


3 、原型模式

使用原型的好处就是可以让所有对象共享它所包含的属性和方法,不必在构造函数中定义对象信息,而是可以将这些信息直接添加到原型对象中。

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.sayName();//Liz
person2.sayName();//Liz


3.1 理解原型

无论什么时候,只要创建你一个函数,就会默认为该函数创建一个prototype属性(Person.prototype)。默认情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性和_proto _(神秘内部属性━━━(゚∀゚)━━━━!!姐自创的,反正就一个神奇的内部属性呗)。

constructor(构造函数)属性包含一个指向prototype属性所在函数的指针。

_proto _(神秘内部属性)属性则在使用构造函数创建一个实例后,在新实例中的该_proto _(神秘内部属性)属性包含一个指向构造函数的原型属性的指针,没有创建新实例则默认Object。

function Person () {
}




默认情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性和_proto _(神秘内部属性)。

添加其他prototype后也是如此。

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};




function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.sayName();//Liz
person2.sayName();//Liz


创建新实例后person1.constructor指向person1的prototype属性所在的函数,person1._proto_ 指向的内容就是其构造函数的原型即Person.prototype



黑色箭头表示包含关系,红色箭头表示指向关系



Person构造函数、Person的原型属性及现有的两个实例之间的关系如下





在所有实现中可通过isPrototypeOf()来确定对象之间是否存在这种关系。

如:

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//true


3.2 原型与实例工作关系

由图可知:



实例person1和person2都只是包含了一个指向Person.prototype的内部属性_proto_,但依然可以调用person1.sayName()。

那是因为,每当代码读取某个对象的某个属性时,都会执行一次搜索。搜索首先从对象实例本身开始,若没找到,再继续搜索指针指向的原型对象。故person1.sayName()会读取保存在原型对象中的函数。

虽然可以通过对象实例访问原型中的值,但不能通过对象实例重写原型中的值。如果在实例中添加了一个属性,其名称与实例原型中的一个属性同名,那该属性就会屏蔽原型中的那个属性

例:

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Cyt";
person1.sayName();//Cyt
person2.sayName();//Liz


创建同名属性,原型中的属性会被屏蔽,但如果是修改·····那就修改了原型属性,所有实例都将可访问该被修改的属性内容!!!

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.friend = ["Jace","Lucy"]
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.friend.push("Cyt");




通过 delete 操作符可完全删除实例的该属性,使我们可以再次通过此实例访问原型对象中的该属性

如:

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Cyt";
person1.sayName();//Cyt
person2.sayName();//Liz
delete person1.name;
person1.sayName();//Liz
person2.sayName();//Liz


可通过hasOwnProperty()方法来检测一个属性是否存在于实例还是原型,返回true才是给定属性存在于对象实例

如:

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Cyt";
console.log(person1.hasOwnProperty("name"));//true——来自实例自己
console.log(person2.hasOwnProperty("name"));//false——来自原型


通过hasOwnProperty方法和for in 的结合可以确定该属性存在于原型还是实例

因为,hasOwnProperty()只在属性存在于实例才返回true,而in只要通过对象能访问到该属性即返回true.所以hasOwnPrototypeProperty返回true,则该属性属于原型,反之属于实例或者不存在该属性,所以使用时应当确保该属性存在

function Person () {
}
Person.prototype.name = "Liz";
Person.prototype.age = 21;
Person.prototype.sex = "female";
Person.prototype.sayName=function() {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(hasOwnPrototypeProperty (person1,"name"));//true——存在于
person1.name = "Cyt";
console.log(hasOwnPrototypeProperty (person1,"name"));//false
console.log(hasOwnPrototypeProperty (person2,"name"));//true
console.log(hasOwnPrototypeProperty (person2,"id"));//false

function hasOwnPrototypeProperty (object,name) {
return !object.hasOwnProperty(name)&&(name in object);
}




3.3 新的原型语法

对象字面量写法

function Person () {
}
Person.prototype = {
name : "Liz",
age : 21,
sex : "female",
sayName:function() {
console.log(this.name);
}
}


这样的创建方法确实更加简洁,但是Person.prototype也相当于设置为一个以字面量形式创建的新对象,虽然最终结果相同,仅constructor属性不再指向Person了,而是一个Object,如果constructor属性非常重要,可在创建原型时特意重新设置它的值。

如:

function Person () {
}
Person.prototype = {
constructor : "Person",
name : "Liz",
age : 21,
sex : "female",
sayName:function() {
console.log(this.name);
}
}


3.4 对_proto_ 的重新理解

_proto_是当前实例指向其构造原型的指针,当创建实例后,又将原型修改为其他对象,则切断了构造函数与最初原型之间的联系,但它任引用最初的原型实例中的[b]_proto_指针仅指向原型(Person.prototype)而不是构造函数(function Person)[/b]



function Person () {
}
Person.prototype = {
constructor : "Person",
name : "Liz",
age : 21,
sex : "female",
sayName:function() {
console.log(this.name);
}
}
var person1 = new Person();
person1.sayName();//Liz
Person.prototype = {
constructor : "Person",
name : "Cyt",
age : 21,
sex : "female",
sayName:function() {
console.log(this.name);
}
}
var person2 = new Person();
person1.sayName();//Liz
person2.sayName();//Cyt


3.5 原型对象的总结

原生对象的原型在构造函数上都是采用这样的模式,所有我们可以通过Array.prototype找到sort方法,甚至通过重新定义同名方法修改原生对象的原型,但是并不推荐这样的做法。

原型模式忽略了为构造函数传参,也因为其共享的本性造成很大的问题。

4 、构造模式与原型模式组合 !!!

实例属性在构造函数中定义,而所有实例共享属性则在原型中定义。

function Person (name,age,sex) {
this.name =name;
this.age = age;
this.sex = sex;
this.friend = [];
}
Person.prototype = {
constructor : "Person",
sayName:function() {
console.log(this.name);
}
};
var person1 = new Person("person1","21","female");
var person2 = new Person("person2","22","male");
person1.friend.push("Cyt,Liz,Jace");
person2.friend.push("Cyt,Liz,Lucy");
console.log(person1.friend);//["Cyt,Liz,Jace"]
console.log(person2.friend);//["Cyt,Liz,Lucy"]


5 、继承

原型、构造函数、实例之间的关系:



让原型对象等于另一个类型的实例。则此时的原型对象将多包含一个指向另一个原型的指针,相应的,另一个原型对象中也会包含一个指向另一个构造函数的指针

继承通过创建Person实例,并将Person实例付给Monitor.prototype实现的。其本质是重写原型对象,从而使Person实例的属性和方法也存在Monitor.prototype中。

function Person (name,age,sex) {
this.name =name;
this.age = age;
this.sex = sex;
this.friend = [];
}
Person.prototype = {
constructor : "Person",
sayName:function() {
console.log(this.name);
},
sayFriend:function() {
console.log(this.friend);
}
}
function Student(className , id){
this.class = className;
this.id = id;
}
Student.prototype = new Person();
Student.prototype.sayId = function() {
console.log(this.id);
}
Student.prototype.sayClass = function() {
console.log(this.class);
}
var person = new Person();
var student = new Student();




!!!自有方法一定要在继承语句后添加,否则会被重写···即白写了,而且自有方法也不能采用字面量形式创建!!!否则···连你的继承也白写,嘿嘿嘿,字面量形式固然简洁,一定要注意···!!!

function Person (name,age,sex) {
this.name =name;
this.age = age;
this.sex = sex;
this.friend = [];
}
Person.prototype = {
constructor : "Person",
sayName:function() {
console.log(this.name);
},
sayFriend:function() {
console.log(this.friend);
}
}
function Student(className , id){
this.class = className;
this.id = id;
}
Student.prototype = new Person();
Student.prototype.sayId = function() {
console.log(this.id);
}
Student.prototype.sayClass = function() {
console.log(this.class);
}

function Monitor(grade){
this.grade = grade;
}
Monitor.prototype = new Student();
Monitor.prototype.sayGrade = function() {
console.log(this.grade);
}
var person = new Person();
var student = new Student();
var monitor = new Monitor();




额额,这个只能说确实是让他们继承了,但是并不实用······························

于是乎,吼吼,总结一句话就是使用空func作为继承纽带,连接父类和子类。

创建空func

使func的原型指向父类的原型

使子类的原型指向新创建的func()

确保父类构造函数和子类构造函数都是自己

function Person (name,age,sex) {
this.name =name;
this.age = age;
this.sex = sex;
this.friend = [];
}
Person.prototype = {
sayName:function() {
console.log(this.name);
},
sayFriend:function() {
console.log(this.friend);
}
}
function Student(className,id,name,age,sex){
Person.apply(this,[name,age,sex]);
this.class = className;
this.id = id;
}
function Monitor(grade,className,id,name,age,sex){
Student.apply(this,[className,id,name,age,sex]);
this.grade = grade;
}
function extend (subClass,superClass) {
var func = function() {};//这其实是个类
func.prototype = superClass.prototype;//因为我们采用的是构造原型组合模式,所有,prototype只含是方法
subClass.prototype = new func();//子类的原型指向新创建的func(),其实就是将func中superClass.Prototype加到自己的方法中。
subClass.prototype.constructor=subClass;//确保父类构造函数和子类构造函数都是自己
superClass.prototype.constructor=superClass;
}
extend(Student,Person);
extend(Monitor,Student);
Student.prototype.sayId = function() {
console.log(this.id);
}
Student.prototype.sayClass = function() {
console.log(this.class);
}
Monitor.prototype.sayGrade = function() {//自定义的方法要写在继承后,以免被重写
console.log(this.grade);
}
var Liz = new Monitor("心理委员","计科2013-02","2013443394","Liz","21","female");




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: