javascript高级编程-原型模式

什么是原型

Javascript 只有一种结构,那就是:对象。在 javaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)原型链Wiki

我们创建每个函数都有一个原型属性,这个属性指是一个指针,指向一个对象,而这个对象的用途包含由特定类型的所有实例共享的属性和方法

function Person(){}  
 Person.prototype.name = 'freax';
 Person.prototype.age = 18;
 Person.prototype.job = 'javascript';
 Person.prototype.sayName = function () {
 console.info(this.name);
 };
 var person1 = new Person();
 var person2 = new Person();
 person1.sayName(); //freax
 person2.sayName(); //freax
 console.info(person1.sayName == person2.sayName); //true

理解原型

prototype

使用Person.isPrototypeOf() 检查对象间的是否是原型关系

console.info(Person.isPrototypeOf(person1));//true  
console.info(Person.isPrototypeOf(person2));//true  

在es5 中可以通过Object.getPrototypeOf() 获取到指定对象的原型对象

console.info(Object.getPrototypeOf(person1)); //返回原型对象  
console.info(Object.getPrototypeOf(person1).name); //freax  原型对象的name属性  

虽然我们可以通过实例可以访问到原型属性的值,但我们不能通过实例重写原型对象属性的值,如下

person1.name = 'freax1';  
console.info(person1.name);//freax1  
console.info(person2.name);//freax  

把person1的name属性设置成freax1 而person2的name属性值依然没有任何变化,而person1是个实例,因此是不能通过实例修改原型属性的

为什么person1没有找到原型上的name属性freax?

当执行person1.name的时候javascript就会搜索实例上是否有同名属性,有则返回,没有则往原型链搜索, person1实例name属性屏蔽了原型上name属性,因此返回freax1

更简单的原型写法

Person.prototype = {  
    name:"freax",
    age:18,
    job:'javascript'
};

这样就可以很方便重写原型对象,但是这么做也要付出代价的,分别有以下几点

  1. 原型的构造属性不再指向Person,因为每创建一个函数,就会同时创建他的原型对象,因此字面量创建的对象也自动获得constructor属性,这个构造属性指向Object的构造函数,
  2. 对象识别问题,尽管instancenof操作符还能正确返回结果,但是通过constructor无法识别对象类型
var friend = new Person();  
console.info(friend instanceof  Person);//true  
console.info(friend instanceof Object); //true  
console.info(friend.constructor == Person.constructor); //false  
console.info(friend.constructor == Object);//true  

此时instanceof 能正确返回,但是constructor不等与Person如果constructor真的很重要,可以像下面这样设回适当的值

function Person1(){}  
Person1.prototype = {  
    constructor:Person1,
    name:'freax'
    //........
};

注意:这样导致constructor属性变成可枚举的enumerable的,原生默认是不可枚举的,因此你可以使用兼容ECMAscript5 的javascript引擎

设置成不可枚举, Object.defineProperty()设置属性的特性

function Person2(){}  
Person2.prototype = {  
    constructor:Person2,
    name:'freax'
    //........
};

重新设置构造属性

Object.defineProperty(Person2.prototype,'constructor',{  
    enumerable:false,
    value:Person2
});

原型的动态性

由于在原型查找值的过程是一次搜索,因此我们在原型对象所做的任何修改都能够在实例上立即反应出来—即先创建对象后修改原型也是如此

var friend = new  Person();  
Person.prototype.sayHi = function () {  
    console.info('hi');
};
friend.sayHi();  

尽管随时可以为原型添加属性和方法,但是重写整个原型对象的情况就不一样了

function Person3(){}  
var friend3 = new Person3();  
Person3.prototype = {  
    constructor:Person3,
    name:"freax",
    age:12,
    sayName: function () {
        console.info(this.name);
    }
};
friend3.sayName();  ///Uncaught TypeError: friend3.sayName is not a function  

原生对象的原型

原型模式的重要性不仅体现在创建自定义类型方面,就连所有引用类型都是采用这种模式创建,原生引用类型(Array,Object,String),通过原生对象的原型不仅取得所有默认引用方法,而且可以定义新方法,可以修改原型对象

var str = 'str';  
String.prototype.getLength = function () {  //增强原生原型对象  
    return this.length;
};
console.info(str.getLength());  

当然你也可以重写整个原型对象,但是不推荐在产品化的程序上修改原生对象的原型,如果因为某个实现缺少某个方法, 就在原生对象的原型中添加这个方法,那么当一个支持该方法的实现中运行代码时,就会导致命名冲突,这样做也会意外的重写整个原型对象

原型对象的问题

原型模式也并不是没有问题,它省略构造函数的环节,结果是在所有实例中取得相同的值,这还不是原型模式最大的问题,最大问题在于原型共享本性所导致

function Person4(){}  
Person4.prototype = {  
    constructor:Person4,
    name:"freax",
    age:"29",
    job:'javascript',
    friend:[1,2,3],
    sayNaeme: function () {
        console.info('freax');
    }
};
var person4 = new Person4();  
var person5 = new Person4();  
person4.friend.push(4);  
console.info(person4.friend);//[1,2,3,4]  
console.info(person5.friend);//[1,2,3,4]  

假如我们的初衷就是共享这个数组,这没有什么问题,但是没有的人的朋友不可能都是一样的吧!这不是我们期望的,每一个人的朋友总是有些差别的,所以我们不想共享friend属性,这就是原型模式的问题, 说白我们就是想在原型上定义私有属性像java的private一样

你的欣赏是我最大的动力

Yanxiong Huang

My name is Yanxiong Huang. graduated from Nanyang middle school.Love Linux,familiar with Node.js,Docker,Serverless... and more Web technology.Contact Me:QQ 31356617;Email:huangaynxiong2013@gmail.com

guangzhou,china http://www.myfreax.com

乐在分享