理解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(); }
相关文章推荐
- kettle中使用JavaScript的一个例子
- 《JavaScript权威指南》学习笔记 第四天 数组
- jsp内置对象有哪些?作用是什么
- jsp有哪些内置对象?作用分别是什么?
- Javascript编程中几种继承方式比较分析
- Javascript编程之继承实例汇总
- Javascript页面跳转常见实现方式汇总
- JavaScript实现页面跳转的几种常用方式
- javascript 闭包
- 南大软院大神养成计划--js
- 【JavaScript】页面刷新location.reload和location.replace的区别
- js的闭包原理理解
- javascript判断数据类型
- json 和 pickle
- javascript函数中的匿名函数
- jsp学习---mvc模式介绍和el表达式,jstl标签库的使用入门
- JavaScript设计模式 - 策略模式
- javascript的几种继承方式
- javascript变量的作用域
- javascript中的screen对象