Skip to main content
 首页 » 编程设计

js高级程序设计之面向对象的理解(包括原型和继承)

2022年07月16日73三少


//创建自定义对象的最简单的方式
//缺点:会产生大量重复的代码
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function () {
alert(this.name);
};


//工厂模式
//缺点:工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题
//(即怎样知道一个对象的类型)
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("Nichoslas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
person1.sayName();
person2.sayName();


//构造函数模式
//缺点:使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍
//构造函数都应该以一个大写字母开头,区别普通函数
//要创建构造函数的新实例,必须使用new操作符,任何函数,只要通过new操作符来调用,就可以作为构造函数
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
alert(this.name);
};
}

var person1 = new Person("Nocholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName();
person2.sayName();

alert(person1.sayName == person2.sayName); //false

//对象的constructor属性用来标识对象类型的
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true

//检测对象类型,instanceof操作符更可靠一些
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true

//当作构造函数使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName();

//作为普通函数调用
Person("Greg", 27, "Doctor"); //添加到window
window.sayName(); //Greg

//在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse"); //or Person.apply(o,["Kristen",25,"Nurse"])
o.sayName(); //Kristen


/*****************原型模式***********************/
//prototype就是通过调用构造函数而创建的那个对象的原型对象
//使用原型的好处是可以让所有对象实例共享它所包含的属性和方法
//不必在构造函数中定义对象信息,而是可以将这些信息直接添加到原型对象中
function Person() {
}

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

var person1 = new Person();
person1.sayName(); //Nicholas
var person2 = new Person();
person2.sayName(); //Nicholas
alert(person1.sayName == person2.sayName); //true
//检测实例对象是否指向某个构造函数的原型
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true

person1.name = "Greg"; //对象实例属性与原型中的一个属性相同,该属性会屏蔽原型中的那个属性
alert(person1.name); //Greg
alert(person2.name); //Nicholas

delete person1.name; //使用delete操作符可以完全删除实例属性,从而让我们能够重新访问原型中的属性
alert(person1.name); //Nicholas

//使用hasOwinProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中
//存在实例中返回true,否则返回false
alert(person1.hasOwnProperty("name")); //false
person1.name = "Greg";
alert(person1.name); //Greg
alert(person1.hasOwnProperty("name")); //true
alert(person2.name); //Nicholas
alert(person2.hasOwnProperty("name")); //false
delete person1.name;
alert(person1.name); //nicholas
alert(person1.hasOwnProperty("name")); //false

//原型与in操作符
//in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中
alert("name" in person1); //true
//同时使用in操作符和hasOwnProperty()方法,就可以确定该属性是存在于对象中还是原型中
function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty(name) && (name in object);
}
alert(hasPrototypeProperty(person1, "name")); //true

//IE的JScript存在一个bug,即屏蔽不可枚举属性不会出现在for-in循环中
var o = {
toString:function () {
return "My Object";
}
};
for (var prop in o) {
if (prop == "toString") {
alert("Found toString"); //IE中不会显示
}
}

//更简单的原型语法,对象字面量
function Person() {
}
Person.prototype = {
constructor:Person, //不加这个该原型对象的constructor不再指向Person,有必要才加
name:"Nicholas",
age:29,
job:"Software Engineer",
sayName:function () {
alert(this.name);
}
};
var person = new Person();
alert(person instanceof Object); //true
alert(person instanceof Person); //true
alert(person.constructor == Person); //true
alert(person.constructor == Object); //false

//调用构造函数时会为实例添加一个指向最初原型的_proto_指针
//而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系
function Person() {
}
var person = new Person();
Person.prototype = {
constructor:person,
name:"Nicholas",
age:29,
job:"Software Engineer",
sayName:function () {
alert(this.name);
}
};
person.sayName(); //error

//定义原生对象新方法,不推荐在产品化的程序中修改原生对象的原型
String.prototype.startWith = function (text) {
return this.indexOf(text) == 0;
};
var msg = "Hello world";
alert(msg.startWith("Hello")); //true

/**
*组合使用构造函数和原型函数
*创建自定义类型的最常见方式。每个实例都有自己的一份实例属性的副本,而原型模式用于定义方法和共享的属性
*最大限度的节省了内存
*/
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelly", "Court"];
}
Person.prototype = {
constructor:Person,
sayName:function () {
alert(this.name);
}
};
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 29, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //Shelly,Court,Van
alert(person2.friends); //Shelly,Court
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

/**
*动态原型模式
*把所有信息都封装在构造函数中,可以使用instanceof确定它的类型
*不能使用对象字面量重写原型
*已经创见了实例的情况下重写原型,会切断现有实例与新原型之间的联系
*/
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
if (typeof this.sayName != "function") {
Person.prototype.sayName = function () {
alert(this.name);
};
}
}
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName();

//寄生构造函数模式
//其他模式不适用的情况下,可以使用寄生。不能依赖instanceof操作符确定对象类型
//该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象,与工厂函数类似
function Person(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 person = new Person("Nicholas", 29, "Software");
person.sayName(); //Nicholas

//稳妥构造函数模式
//在禁止是用this和new的情况下使用,instanceof不能确定该对象类型
function Person(name, age, job) {
//创建要返回的对象
var o = new Object();

//可以在这里定义私有变量和函数

//添加方法
o.sayName = function () {
alert(name);
};

//返回对象
return o;
}
var person = Person("Nicholas", 29, "Software Engineer");
person.sayName(); //Nicholas


/****************继承*****************/

//原型链
//利用原型让一个引用类型继承另一个引用类型的属性和方法
//实现的本质是重写原型对象,代之以一个新类型的实例
//原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了
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
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

//谨慎地定义方法
//给原型添加方法的代码一定要放在替换原型的语句之后
//不能使用对象字面量创建原型方法,因为会重写
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;
};
//重写超类型中的方法
Subtype.prototype.getSuperValue = function () {
return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false

//原型链的问题
//共享问题和不能向超类型的构造函数传递参数,因此实践中很少会单独使用原型链
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
}
//继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black"); //red,blue,green,black
alert(instance1.colors);
var instance2 = new SubType();
alert(instance2.colors); //red,blue,green,black

//借用构造函数
//在子类型构造函数的内部调用超类型构造函数,通过使用apply()和call()方法,可以传递参数
//虽然解决原型中包含引用类型至所带来的问题,但无法解决函数复用问题,因此很少单独使用
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
function SubType() {
//继承了SuperType,同时还传递了参数
SuperType.call(this, "Nicholas");
//实例属性
this.age = 29;
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //red,blue,green,black
var instance2 = new SubType();
alert(instance2.colors); //red,blue,green
alert(instance1.name); //"Nicholas"
alert(instance1.age); //29
/**
*组合继承
*使用原型链实现对原型的属性和方法继承,而通过借用构造函数来实现对实例属性的继承
*通过函数复用,每个实例都有自己的属性
*最常用的继承模式,而且instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象
*/
//组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
alert(this.name);
};
function SubType(name, age) {
//继承属性
SuperType.call(this, name); //第二次调用SuperType()
this.age = age;
}
//继承方法
SubType.prototype = new SuperType(); //第一次调用SuperType()
SubType.prototype.sayAge = function () {
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //red,blue,green,black
instance1.sayName(); //Nicholas
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //red,blue,green
instance2.sayName(); //Greg
instance2.sayAge(); //27

//原型式继承
//借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型
//本质上,object()对传入其中的对象执行了一次浅复制
function object(o) {
function F() {
}
F.prototype = o;
return new F();
}

var person = {
name:"Nicholas",
friends:["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barble");
console.log(person.friends); //Shelby,Court,Van,Rob,Barble
console.log(anotherPerson.name); //Greg
console.log(yetAnotherPerson.name); //Linda
console.log(anotherPerson.friends); //Shelby,Court,Van,Rob,Barble
console.log(yetAnotherPerson.friends); //Shelby,Court,Van,Rob,Barble

//寄生式继承
//创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再向真的是它做了所有工作一样返回对象
//使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似
function createAnother(original) {
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function () { //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
var person = {
name:"Nicholas",
friends:["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //hi

/**
*寄生组合式继承
*最理想的继承范式
*/
function object(o) {
function F() {
}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
alert(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29

var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27


本文参考链接:https://www.cnblogs.com/webFrontDev/archive/2012/11/04/2753487.html