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

理解原型

使用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一样

全部为采集文章,文中的 联系方式 均不是 本人 的!

发表评论