构造函数模式用于定义实例私有属性,而原型模式可以定义共享的属性和方法,可以节省内存,同时可以有自己的私有属性和方法,这种方法模式使用的最广;比如如下代码:
function Dog(name,age){ this.name = name; this.age = age; this.values = ["aa",'bb']; }; Dog.prototype = { constructor: Dog, say: function(){ alert(this.name); } } var dog1 = new Dog("dog1",'12'); dog1.values.push("cc"); console.log(dog1.values); // ["aa","bb","cc"] var dog2 = new Dog("dog2",'14'); console.log(dog2.values); // ["aa","bb"] console.log(dog1.values === dog2.values);//false console.log(dog1.say === dog2.say); //true
还有许多其他的模式,我这边不一一介绍,需要了解的话,可以看看Javascript设计模式那本书;
理解Javascript继承
一:原型链
ECMAScript中有原型链的概念,并将原型链作为继承的主要的方法,其思想是让一个引用类型去继承另一个引用类型的属性和方法,从上面我们了解到,原型和实例的关系,每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针,实例与构造函数本身没有什么关系,比如我们现在让一个原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个类型的指针,相应的,另一个原型中也包含着指向另一个构造函数的指针,那么层层递进,就成了原型链;
如下代码:
function Animal() { this.name = "aa"; } Animal.prototype.fly = function(){ return this.name; }; function Dog() { this.value = "bb"; } Dog.prototype = new Animal(); Dog.prototype.fly = function(){ return this.name; }; var dog1 = new Dog(); console.log(dog1.fly()); // aa
如上代码:我们先定义了一个Animal这个构造函数,它有一个属性name=”aa”; 且原型定义了一个方法fly; 接着我定义了Dog这么一个构造函数,且让其原型等于Animal的实例,也就是使用这种方式使Dog这个构造函数继承了Animal的属性和方法,因此Dog有Animal这个构造函数所有的属性和方法,接着再定义Dog的自己的fly方法,它会覆盖原型Animal的方法,且指针还是指向与Animal的,因此this.name =”aa”; 所以当我们实例化Dog的时候,访问dog1.fly()方法的时候,打印出aa;
如上代码我们知道如果想要A继承与B的话,那么继承可以这样写:
A.prototype = new B();
还有Dog的fly方法实际上是对原型Animal的fly方法进行重写;我们继续看看dog1实例与Dog与Animal的关系;如下代码:
console.log(dog1 instanceof Dog); // true console.log(dog1 instanceof Animal); // true console.log(dog1 instanceof dog1.constructor); // true console.log(dog1.constructor === Dog); // false console.log(dog1.constructor === Animal); // true
如上可以看到,dog1是Dog与Animal的实例,dog1还是指向与dog1.constructor,但是dog1的实例的constructor不再指向与Dog了,而是指向与Animal,这是因为dog1.constructor被重写了的缘故!
通过原型的继承,我们看到dog1.fly()方法,会经历如下几个搜索步骤,第一先搜索该实例有没有fly这个方法,接着搜索Dog的原型有没有这个方法,最后悔搜索Animal这个prototype这个;最后会继续看Object中有没有这个方法,我们都知道所有的对象都是Object的实例,我们可以看下:
console.log(dog1 instanceof Object); //true
所有函数默认的原型都继承与Object的实例,因此默认原型都有一个内部指针指向与Object.prototype; 那也就是说所有的自定义类型都会继承与toString()方法和valueOf()方法的根本原因,我们知道测试原型与实例的关系除了可以使用instanceof之外,我们还可以使用isPrototypeOf()方法, 如下代码:
console.log(Object.prototype.isPrototypeOf(dog1)); // true console.log(Dog.prototype.isPrototypeOf(dog1)); // true console.log(Animal.prototype.isPrototypeOf(dog1)); // true
注意:1. 子类型有时候需要重写超类型的某个方法,或者需要添加超类型中不存在的某个方法,给原型添加的方法一定要放在替换原型方法之后;如下代码:
function Animal() { this.name = "aa"; } Animal.prototype.fly = function(){ return this.name; }; function Dog() { this.value = "bb"; } // 继承Animal Dog.prototype = new Animal(); // 重写原型的方法 Dog.prototype.fly = function(){ return this.name; }; // 给自身添加新方法 Dog.prototype.cry = function(){ return false; }; var dog1 = new Dog(); console.log(dog1.fly()); // aa console.log(dog1.cry()); // false
2 . 通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链;如下代码: