JS面向对象基础
读阮一峰教程的简单记录
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html
一、封装
原始模式的弊端:如果生成多个实例,写起来非常麻烦;实例与原型之间看不出有什么联系。
var Cat = { name : ''; color : '' } var cat1 = {}; cat1.name = '大毛'; cat1.color = '黄色'; var cat2 = {}; cat2.name = '二毛'; cat2.color = '黑色';
所谓构造函数,就是一个内部使用 this 变量的普通函数,对构造函数使用new 运算符,就能生成实例,并且 this 变量会绑定在实例对象上
function Cat(name,color){ this.name = name; this.color = color; } var cat1 = new Cat('大毛','黄色'); var cat2 = new Cat('二毛','黑色');
但是我们为Cat对象添加一个不变的属性,每生成一个实例都为重复内容,浪费一些内存
function Cat(name,color){ this.name = name; this.color = color; this.type = "猫科动物"; this.eat = function(){alert("吃老鼠");}; }
每一个构造函数都有一个prototype 属性,指向另一个对象。这个对象的所有属性和方法都会被构造函数的实例继承。那些不变的属性和方法,可以直接定义在 prototype 对象上。
function Cat(name,color){ this.name = name; this.color = color; } Cat.prototype.type = "猫科动物"; Cat.prototype.eat = function(){alert("吃老鼠")};
isPrototypeOf()
判断某个prototype 对象和某个实例之间的关系
hasOwnProperty()
判断某一个属性是本地的,还是继承自prototype对象的
in
判断某个实例是否含有某个属性,不管是不是本地属性
for in 可以用来遍历某个对象的所有属性
二、构造函数的继承
1、构造函数绑定,使用 call apply 方法,将父对象构造函数绑在子对象上
function Animal(){ this.species = "动物"; } function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; } var cat1 = new Cat("大毛","黄色"); alert(cat1.species); //动物
2、prototype模式
猫的prototype对象 ——指向——> Animal 的实例
猫的 实例 ——继承——>Animal
Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
注意:如果替换了prototype对象,那么接下来一定要为新的prototype对象加上constructor属性,并指回原来的构造函数
o.prototype = {}; o.prototype.constructor = o;
3、直接继承prototype
Animal对象中,不变的属性都可以直接写入 Animal.prototype
function Animal(){ } Animal.prototype.species = "动物";
Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
缺点:修改Cat.prototype 会反映到 Animal.prototype
4、利用空对象作为中介
var F = function(){}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat;
修改Cat的prototype对象,就不会影响到Animal的prototype对象
封装和使用
function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; //在子对象打开一条通道,直接调用父对象方法,属于备用性值 }
extend(Cat,Animal); var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
5、拷贝继承
将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。
function Animal(){} Animal.prototype.species = "动物";
function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; //备用 }
extend2(Cat, Animal); var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物
三、非构造函数继承
var Chinese = { nation:'中国' };
var Doctor ={ career:'医生' }
object()方法
function object(o) { function F() {} F.prototype = o; return new F(); }
浅拷贝
不是真正的拷贝,存在父对象被篡改的可能
function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } c.uber = p; return c; }
var Doctor = extendCopy(Chinese); Doctor.career = '医生'; alert(Doctor.nation); // 中国
深拷贝(递归调用浅拷贝)
function deepCopy(p, c) { var c = c || {}; for (var i in p) { if (typeof p[i] === 'object') { c[i] = (p[i].constructor === Array) ? [] : {}; deepCopy(p[i], c[i]); } else { c[i] = p[i]; } } return c; }