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

理解js的对象,对象创建,继承

2015-11-28 01:23 661 查看

js对象概述

理解js对象

js对象的属性在创建时都带有一些特征值,js通过这些特征值来定义它们的行为。

属性类型

1.数据属性

数据属性包含一个数据值的位置,在这个位置可以读取和写入值

2.访问器属性

定义多个属性

读取属性的特性

创建对象

虽然Object构造函数,对象字面量都可以来创建对象,但这些方式有个缺点:在使用同一个接口创建很多对象的时候,会产生大量重复代码,所以在创建对象的时候,可以使用一些模式来进行创建:

工厂模式

/**
* 工厂模式——创建对象
* 解决了很多创建类似对象的问题,但却没有解决对象识别的问题
* @param name
* @param age
* @param job
* @returns {Object}
*/
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
return o;
}
var person1 = createPerson("sqliang",23,"IT");


构造函数模式

/**
* 构造函数模式——创建对象
* 创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
* 不同点:
* 1.没有显示地创建对象
* 2.直接将属性和方法赋给了this对象
* 3.没有return语句
*
* 构造函数的缺点:会导致不同的作用域链和标识符解析,但
* 创建Function新实例的机制仍然是相同的
* 每个方法都要在每个实例上重新创建一遍
*
* @param name
* @param age
* @param job
* @constructor
*/
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
};
}
var person2 = new Person("sqliang2",24,"ITT");

Person("cyy",23,"haha");//作为普通函数调用,添加到window对象
var person3 = new Object();
Person.call(person3,"sqliang",24,"TTI");//在另一个对象作用域中调用Person函数


原型模式

/**
* 原型模式——创建对象
*与构造函数不同,新对象的这些属性和方法是由所有实例共享的
* 原型最初只包含constructor属性,该属性也是共享的
* @constructor
*/
function PersonWithProto(){
PersonWithProto.prototype.name = "sqliang";
PersonWithProto.prototype.age = 23;
PersonWithProto.prototype.job = "IT";
PersonWithProto.prototype.sayName = function(){
console.log(this.name);
};
}
var per1 = new PersonWithProto(),
per2 = new PersonWithProto();
//可以通过isPrototypeOf()来确定对象之间是否存在原型关系
console.log(PersonWithProto.prototype.isPrototypeOf(per1));//true
//ES5新增方法getPrototypeOf()来获取Prototype的值
console.log(Object.getPrototypeOf(per2) === Person.prototype);//true
console.log(Object.getPrototypeOf(per2).name);//"sqliang"

//可以通过对象实例访问保存在原型中的值,但是不能通过对象实例修改原型对象中的值
per2.name = "hehe";
console.log(per2.name);//"hehe"
console.log(per1.name);//"sqliang"
delete per2.name;

console.log(per1.hasOwnProperty("name"));//false
console.log("name" in per1);//true

per1.name = "cyy";
console.log(per1.name);//"cyy"
console.log(per1.hasOwnProperty("name"));//true
console.log("name" in per1);//true

//同时使用in 和 hasOwnProperty可以检测该属性是存在于对象中,还是存在于原型中
/**
* 检测对象是否含有某原型属性
* @param obj
* @param name
* @returns {boolean}
*/
function hasPrototypeProperty(obj,name){
return !obj.hasOwnProperty(name) && (name in obj);
}
//原型模式更简单的实现
/**
* 更简单的实现原型模式——创建对象
* *****************注意:此时constructor属性不再指向Animal了,而是Object*****************
* ****原因:
* 每创建一个对象时,会同时创建它的prototype对象,这个对象也会自动获取constructor属性。而下面的
* 语法,本质上完全重写了默认的constructor属性,不再指向Animal了,指向了Object。此时,尽管instanceof
* 还可以返回正确的结果,但通过constructor已经无法确定对象的类型了
* @constructor
*/
function Animal(){}
Animal.prototype = {
name : 'sqliang',
age : 23,
job : 'softwares Engineer',
sayName : function(){
console.log(this.name);
}
};
var ani = new Animal();
console.log(ani instanceof Object);//true
console.log(ani instanceof Animal);//true
console.log(ani.constructor == Animal);//false
console.log(ani.constructor == Object);//true

//因此需要加上constructor属性
/**
* 这种重设constructor属性会导致他的[[Enumerable]]特性被设置为true。默认情况下,
* 原生的constructor是不可枚举的
* @constructor
*/
function Animals(){}
Animals.prototype = {
constructor : Animals,
name : 'sqliang',
age : 18,
job : 'Softwares Engineer',
sayName : function(){
console.log(this.name);
}
};
//所以,如果使用的是ES5的js引擎,可以重设构造函数,即:
function Animals2(){}
Animals2.prototype = {
name : 'sqliang',
age : 18,
job : 'Softwares Engineer',
sayName : function(){
console.log(this.name);
}
};
//重设构造函数,只适用于ES5兼容的浏览器
Object.defineProperty(Animals2.prototype,'constructor',{
enumerable : false,
value : Person
});


组合使用构造函数和原型模式

/**
* 组合使用构造函数模式和原型模式——创建对象
* 原型模式用于定义方法和共享的属性,构造函数模式用于定义实例属性。
* 使得:
* 每个实例都有自己的一份实例属性的副本,但同时又共享着对方法的引用
* 最大限度地节省了内存。这种汇合模式还支持向构造函数传递参数,集两种模式之长
* @param name
* @param age
* @param job
* @constructor
*/
function Man(name,age,job){
this.name = name;
this.age = age;
this.job = job;
}
//重写了原型属性,要指定constructor,否则指向了Object
Man.prototype = {
constructor : Man,
sayName : function(){
console.log(this.name);
}
};


动态原型模式(推荐使用的一种模式)

/**
* 动态原型模式————创建对象
* 把所有信息封装在构造函数中,通过在构造函数中初始化原型,又
* 保持了同时使用构造函数和原型的有点,还可以通过检查某个应该存在的方法
* 是否有效,来决定是否需要初始化原型
* @param name
* @param age
* @param job
* @constructor
*/
function Per(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["cyy","yyc"];

//检测
if(typeof this.sayName != "function"){
Per.prototype.sayName = function(){
console.log(this.name);
}
}
Per.prototype.getHand = function(){
console.log("two hands");
}
Per.prototype.hit = function(){
console.log("I will hit you.");
}
}


寄生构造函数模式

/**
* 寄生构造函数模式——创建对象
* 返回的对象与构造函数或者与构造函数的原型之间没有关系
*
* 除了使用时用new操作符,病叫做构造函数之外,与工厂模式没有差别
* 这个模式可以在特殊情况下用来为对象创建构造函数。如:
* 创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,
* 因此可以使用这个模式。
* @returns {Array}
* @constructor
*/
function SpecialArray(){
var arr = new Array();
arr.push.apply(arr,arguments);//添加值
arr.toPipedString = function(){
return this.join("&");
}
return arr;
}
var colors = new SpecialArray("red","blue","green");
console.log(colors.toPipedString());//"red&blue&green"


稳妥构造函数模式

/**
* 稳妥构造函数模式——创建对象,稳妥对象指没有公共属性,而且其方法也不引用this的对象。
* 该模式遵循与寄生构造函数类似的模式,但是创建对象的实例方法不引用this,不使用new
* 操作符调用构造函数
* 最适合在一些安全环境中(禁止使用this和new的环境),或者在纺织数据被其他应用程序
* 改动时使用。
* @param name
* @param age
* @constructor
*/
function Wemon(name,age){
var o = new Object();
var hello = "Hello";
o.sayName = function(){
console.log(name);
}
o.sayHello = function(){
console.log(hello);
return hello;
}
}
var nihao= Wemon("sqliang",23);
nihao.sayName();
nihao.sayHello();


继承

/**

* =====================继承

* 许多OO语言都支持两种继承方式:接口继承,实现继承

* 接口继承只继承方法签名,

* 实现继承继承实现的方法。

* *js只支持实现继承,由于函数没有签名,无法实现接口继承*

* js继承主要依靠原型链来实现的

*/

原型链

/**
* ====================原型链
* 基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法。
* 基本模式:让原型对象等于另一个类型的实例
* 问题:
* 1,所有子类型的实例会共享超类型的实例属性
* 2,在创建子类型的实例时,不能像超类型的构造函数传递参数。
* 即,没有办法在不影响所有对象实例的基础上给超类型传递参数。
* 因此,实践中很少会单独使用原型链
*
*/
function SuperType(){
this.property = true;
SuperType.prototype.getSuperValue = function(){
console.log("Super:" + this.property);
return this.property;
}
}
function SubType(){
this.subProperty = false;
SubType.prototype.getSubValue = function(){
console.log("Sub:" + this.subProperty);
return this.subProperty;

}
}
//继承了SuperType
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.getSuperValue());

//重写超类型里的方法
SubType.prototype.getSuperValue = function(){
return false;
}
console.log(instance.getSuperValue());


借用构造函数

/**
* 继承,借助构造函数
* 在子类型构造函数内部调用超类型构造函数
* 借助构造函数的问题:
* 方法都在构造函数中定义,函数无法复用
* 在超类型的原型中定义的方法,对子类型是不可见的,
* 导致所有模式都只能使用构造函数模式
* (借助构造函数的技术是很少单独使用的)
*/
function SuperColor(){
this.colors = ["red","green","blue"];
}

function SubColor(){
//执行了SuperColor(),继承了SuperColor的color属性
SuperColor.call(this);
}
var ins1 = new SubColor();
ins1.colors.push("black");
console.log(ins1.colors);//["red","green","blue","black"]
var ins2 = new SubColor();
console.log(ins2.colors);//["red","green","blue"]

//传递参数
function SuperMan(name){
this.name = name;
SuperMan.prototype.getName = function(){
console.log(this.name);
return this.name;
}
}

function SubMan(){
//为了确保SuperMan构造函数不会重写子类型的属性,可以在调用超类型构造函数后,
//再添加应该在子类型中定义的属性
SuperMan.call(this,"sqliang");
this.age = 23;
}
var man = new SubMan();
console.log(man.age);


组合式继承

/**
* 组合继承,(推荐使用)
* 将原型链和借用构造函数的技术组合到一块,
* 使用原型链实现对原型属性和方法的继承,借用构造函数来实现对实例属性的继承。
* @param name
* @constructor
*/
function SuperStar(name){
this.name = name;
this.colors = ["red","blue","green"];
SuperStar.prototype.getName = function(){
console.log(this.name);
}
}

function SubStar(name,age){
SuperStar.call(this,name);
this.age = age;
}
//继承方法,先重写,后加sayAge
SubStar.prototype = new SuperStar();
SubStar.prototype.constructor = SubStar;

SubStar.prototype.sayAge = function(){
console.log(this.age);
};
var star1 = new SubStar("sqliang",23);
star1.colors.push("black");
console.log(star1.colors);//["red","blue","green","black"]
star1.getName();//"sqliang"
star1.sayAge();//23

var star2 = new SubStar("cyy",32);
console.log(star2.colors);//["red", "blue", "green"]
star2.getName();//"cyy"
star2.sayAge();//32


原型式继承

/**
* 原型式继承(没有严格意义上的构造函数,借助原型基于已有的对象创建新对象
* 同时还不必因此创建自定义类型)
* 在这种范式下,person.friends不仅属于person所有,而且也被anotherPerson
* 和anotherTwo所共享,实际上也就是相当于有创建了person对象的两个副本。
*
*/
var person = {
name : "sqliang",
friends : ["hello","cyy","lp","gyf","JSF"]
};
var anotherPerson = object(person);
anotherPerson.name = "lsq";
anotherPerson.friends.push("lsq");

var anotherTwo = object(person);
console.log(anotherTwo.name);//"lsq"
console.log(anotherTwo.friends);//["hello","cyy","lp","gyf","JSF","lsq"]

function object(o){
function F(){}
F.prototype = o;
return new F();
}


寄生式继承

/**
* 寄生式继承(与原型式继承紧密相关的一种思路)
* 其思路与寄生构造函数和工厂模式类似,即创建一个用于封装继承过程
* 的函数,函数在内部以某种方式来增强对象,最后再像真的是它做了所有
* 工作一样返回对象.
* 在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种
* 有用的模式,还有object()不是必需的,任何能够返回新对象的函数都适用
* 于此模式
*/
function createAnother(original){
var clone = object(original);
clone.sayHi = function(){
console.log("Hi");
}
return clone;
}
function object(o){
function F(){}
F.prototype = o;
return new F();
}


寄生组合式继承(集寄生式和组合继承的优点于一身,是实现基于类型继承的最有效方式)

/**
* 寄生组合式继承(最有效的,最推荐使用的)
* 由于组合继承存在不足,即无论在什么情况下,都会调用两次超类型构造函数,
* 一次是在创建子类型原型时,一次是在子类型构造函数内部。虽然子类型最终会
* 包含超类型对象的全部实例属性,但是在调用子类型构造函数时重写了这些属性
* =================而寄生组合式继承,
* 通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,主要思路是:
* 不必为了指定子类型的原型而调用超类型构造函数,此时最需要的是超类型原型的一个副本而已。
* 本质上就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
* 这样,高效性就体现在之调用了一次超类型构造函数(在子类型构造函数中),并且避免了在
* 子类型上创建不必要的、多余的属性,原型链还能保持不变。
* 因此,还可以正常使用instanceof 和isPrototypeOf(),是目前最理想的继承范式
*/
function SuperBest(name){
this.name = name;
this.colors = ["red","green","blue"];
SuperBest.getName = function(){
return this.name;
}
}
function SubBest(name, age){
SuperBest.call(this,name);
this.age = age;
}
inheritPrototype(SubBest,SuperBest);
SubBest.prototype.sayAge = function(){
console.log(this.age);
};
function inheritPrototype(subType,superType){
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function object(o){
function F(){}
F.prototype = o;
return new F();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: