【设计模式+原型理解】第二章:基于构造函数扩展出来的原型模式

       在第一章的时候,说过了单例模式、工厂模式、构造函数模式,你还记得构造模式是怎么样的吗?

function CreateJsPerson(name, age) {
	this.name = name;
	this.age = age;
	this.writeJs = function() {
		console.log("my name is " + this.name + ", i can write js.");
	}
}
var p1 = new CreateJsPerson("p1", 18);
var p2 = new CreateJsPerson("p2", 17);
p1.writeJs();
p2.writeJs();
console.log(p1.writeJs() === p2.writeJs()); //-> false
// 构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的
// -> 叫做实例识别

       上面的就是构造函数模式,你知道,为什么p1、p2的writeJs()方法为什么不相等吗?这是因为正如上面所说的,两个实例是相互独立的,也就是说,两个实例的属性都是各自私有属性。

   -> 问题来了,两个实例里面的属性都是私有的以外,是不是还得有公有的部分?

 

一、【基于构造函数的原型模式

        使用基于构造函数模式的原型模式,能够实现把writeJs()方法变成公有的,代码如下:

function CreateJsPerson(name, age) {
	this.name = name;
	this.age = age;
	// this.writeJs = function() {
	// 	 console.log("my name is " + this.name + ", i can write js.");
	// }
}
CreateJsPerson.prototype.writeJsG = function() {
	console.log("my name is " + this.name + ", i can write js.");
};

var p1 = new CreateJsPerson("p1", 18);
var p2 = new CreateJsPerson("p2", 17);
p1.writeJs();
p2.writeJs();
console.log(p1.writeJsG() === p2.writeJsG()); //->true

 

        为什么这样写,就能把属性变为公有的呢?

        基于构造函数模式扩展出来的原型模式:

                ->它解决了方法或者属性公有的问题

                ->即把实例之间公有的属性和方法提出成公有的属性和方法

                ->想让谁公有,就把它放在prototype上即可

 

二、【原型基础3句话】

         想要学习原型,要记住下面的三句话(不要问为什么~~):

         1)每一个函数数据类型(普通函数、类)都有一个天生自带的属性,prototype(原型),并且这个属性是一个对象数据类型的值。

          2)并且在prototype上浏览器会天生给它加上一个属性contructor(构造函数),属性值是当前函数(类)本身。

          3)每一个对象数据类型(普通对象,实例,prototype...)也天生自带一个属性:__proto__,属性值是当前实例所属类的原型(prototype)。

下面这段代码,可以说明一下上面的3句话:

function Fn() {
	this.x = 100;
};
Fn.prototype.getX = function() {
	console.log(this.x);
};
var f1 = new Fn;
var f2 = new Fn;
console.log(Fn.prototype.constructor === Fn); //->true
// 堆内存:存储 对象、函数里面的代码字符串

 

上面代码的原型链如下图所示,正方形代表栈内存(即函数作用域),椭圆正方形代表堆内存(即对象)

 

    从画图可以很清晰看到,整个基于构造函数扩展出来的原型链函数,类与实例的原型链,实例与JS基类Object的原型链关系,一览无遗。

    上图+代码,可以总结出:

     1、Object是JS中所有对象数据类型的基类(最顶层的类)

           1) f1 instanceof Object -> true,这是因为f1通过__proto__可以向上级查找,不管有多少级,最后总能找到Object

            2)在Object.prototype上没有__proto__这个属性(因为自己指向自己没意义)

      2、原型链模

            举个简单的例子,f1.hasOwnProperty("x");  //->hasWwnProperty是f1的一个属性,但是我们发现f1的私有属性上并没有这个方法,那如何处理的呢?

            通过 对象名.属性名 的方式获取属性值的时候,首先在对象的私有的属性上进行查找,如果私有中存在这个属性,则获取的是私有的属性值;

         ->如果私有的没有,则通过__proto__找到所属类的原型(类的原型上定义的属性和方法都是当前实例的公有的属性和方法),原型上存在的话,获取的是公有的属性值;

        -> 如果原型上也没有,则继续通过原型上的__proto__继续向上查找,一直找到Object.prototype为止...

         ->这种查找的机制就是我们的原型链模式

 

三、【原型知识玩起来】

cconsole.log(f1.getX === f2.getX); //->true
console.log(f1.__proto__.getX === f2.getX); //->true 
console.log(f1.getX === Fn.prototype.getX); //->true
// f1.getX 跟 f1.__proto__.getX的区别
// 前者是浏览器先找私有作用域,找不到再找公有作用域
// 后者是浏览器直接查找公有作用域
// 
console.log(f1.hasOwnProperty === f1.__proto__.__proto__.hasOwnProperty);

// 在IE浏览器中,我们原型模式也是同样的原理,但是IE浏览器怕你通过__proto__把公有的修改,
// 禁止我们使用__proto__,下面的例子就可以很明显的说明为啥IE禁止了

f1.sum == function() {
	//修改自己私有的sum
};
f1.__proto__.sum = function() {
	//修改所属类原型上的sum
};
// 所以修改公有的,IE只能通过prototype
Fn.prototype.sum = function() {
	// 修改公有的sum
};

 

四、【总结】

        在这一章中,主要说的是原型模式,但是原型模式是通过构造函数扩展出来的,同时也通过代码+图的方式,把原型模式的实现原理给画了出来。

        既然原型这个知识点出来了,我会在后面介绍一下,使用原型来实现类的封装、继承、多态(里面的重写),并且会介绍使用原型来实现7中继承方法,将会非常有趣。

 

posted @ 2019-02-22 00:02  彭胜光  阅读(426)  评论(0编辑  收藏  举报