理解JavaScript中的面向对象
1、工厂模式:用函数来封装,以特定接口创建对象的细节
- 解决了创建多个相似对象的问题,
- 多用于创建多个含有相同属性,和方法的对象,避免代码的重复编写;
- 没有解决对象识别的问题(即怎么知道一个对象的类型)(它的 instanceof 只能是Object)
function creatPerson(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 p1 = creatPerson(\"hh\",23,\"worker\");var p2 = creatPerson(\"xx\",26,\"worker1\");p1.__proto__===p2.__proto__===Object.prototype;
2、构造函数模式:
缺点:每个方法都在每个实例上重新创建了一例子中p1.sayName!=p2.sayName。
function Person(name,age,job){this.age = age;this.name = name;this.job = job;this.sayName = function(){alert(this.name)}//this.sayName = sayName; //解决方式}var p1 = new Person(\"hh\",23,\"worker\");var p2 = new Person(\"xx\",26,\"worker1\");//function sayName(){alert(this.name)} //解决方式new的执行:{var obj ={};obj.__proto__ = Person.prototype;Person.call(obj);return obj}p1 instanceof Person //true;p2 instanceof Person //true;p1 instanceof Object //true;p2 instanceof Object //true;这样是得p1和p2实例有了特定的类型, Person;
3、 原型模式
- 优点:他省略了构造函数初始化参数这个环节,原型中所有属性都被很多实例共享,共享对函数非常合适,基本属性也还行
通过在实例上添加同名属性,可隐藏原型中的对应属性值; - 缺点: 他的共享属性,对于包含引用类型值的属性 如果实例重新赋值没什么影响,和基本类型一样,如果是操作修改 就有些问题了,会使得所有实例获取到的该属性都被修改, 所以也不单独使用
function Person(){}Person.prototype={constructor:Person,name:\"ss\",friends:[\"s1\",\"s2\"],sayName:function(){alert(this.name)}}var p1= new Person();var p2= new Person();p1.name = \"p1\"console.log(p1.name) //p1console.log(p2.name) //ssp1.friends.push(\"s3\");console.log(p1.friends) //[\"s1\",\"s2\",\"s3\"]console.log(p2.friends) //[\"s1\",\"s2\",\"s3\"]
继承
1、组合继承:(常用继承模式)
- 这样可以使得实例分别拥有各自的属性(包括引用类型的,实例间互补影响),又可以使用相同的方法;
- 缺陷: 无论什么情况下,都会调用两次父类的构造函数
function Father(){this.name =\"ss\";this.friends=[1,2,3,4]}function Son(){Father.call(this);}Son.prototype = new Father(); // Son 原型获得Father上的属性,name和friendsvar son1 = new Son(); // 此时调用Son构造函数为son1 实例上添加了属性(name和friends), 这些属性就屏蔽了原型中的同名属性;// 调用两次Father构造函数的结果就是有两组属性,一组在实例上,一组在原型上;
2、寄生组合式:(理想的继承实现方式)
- 解决组合继承中的缺陷,生成两组属性,只在实例上生成原型;
- 通过构造函数来继承属性,通过原型链的混成形式来继承方法(就是给子类原型,指定一个父类的副本即可)
function inherit(son,father){var prototype = object(father.prototype);// 上句话,创建父类原型的副本,prototype.__proto__ = Father.prototype;// prototype 继承constructor prototype.constructor 取的是原型链上,原型的Father.prototype.constructor, 为 Father();即:prototype.constructor == prototype.__proto__.constructor // true// prototype.hasOwnPrototyoe(\"constructor\") //falseprototype.constructor = son; // 弥补重写原型造成的默认属性的修改;//此时是给prototype 添加了constructor 属性,赋值为son, 屏蔽了原型上的constructor属性// prototype.hasOwnPrototype(\"constructor\") // true// prototype.constructor = son;// prototype.__proto__.constructor = fatherson.prototype = prototype;// 给子类原型赋值为父类原型的副本;}//使用:function Father(){this.name=\"11\";this.age=12;}Father.prototype.sayName=function(){alert(this.name);}function Son(){Father.call(this);this.age=23;}inherit(Son,Father);Son.prototype.sayAge=function(){alert(this.age)}
参考阅读