本文通过例子将原生JavaScript中的原型进行介绍,在学习时可以将代码运行下,查看下控制台相关的输出;在写代码的同时添加了很多注释,方便理解和代码分块。实例代码如下:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<script type="text/javascript">
//原型模式
function Person(){};
Person.prototype.name = 'jum';
Person.prototype.age = 26;
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = 'jums';
console.log(person1.name);
console.log(person2.name);
console.log("this is person1's result:");
console.log(person1.name);
console.log(Person.prototype.isPrototypeOf(person1));//确定对象之间是否存在对象关系
console.log(Object.getPrototypeOf(person1) == Person.prototype);//Object对象方法返回原型对象
console.log(Object.getPrototypeOf(person1).name);//Object对象返回原型对象的属性值
console.log(person1.hasOwnProperty('name'));//该属性是实例属性时才返回true
console.log("name" in person1);//无论实例属性还是原型属性,只要有就会返回true
console.log(hasPrototypeProperty(person1, 'name'));
person1.name = 'po';//添加实例属性,此时覆盖原型属性,只能通过delete才可以删除
console.log(person1.name);
console.log(hasPrototypeProperty(person1, 'name')); //自定义函数,判断是不是原型属性
delete person1.name;//删除实例属性
console.log(person1.name);
console.log("this is person2's result:");
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
};
var keys = Object.keys(Person.prototype);//获取原型中可以枚举的属性 返回数组
console.log(keys);
var person3 = new Person();
person3.name = 'zhou';
person3.age = 18;
var keysByP3 = Object.keys(person3);//获取实例中可以枚举的属性 返回数组
console.log(keysByP3);
// getOwnPropertyNames()方法获取所有属性,包括不可枚举的constructor属性 返回数组
var keysAll = Object.getOwnPropertyNames(Person.prototype);
console.log(keysAll);
//另一种更简单形式的原型
function People(){};
People.prototype = {
name: 'jumbo',
age: 26,
sayName: function(){
console.log(this.name);
},
};
// PS: 此方法缺点是constructor属性不再指向People,因为此方法相当于把prototype对象重写了。
// 此时的constructor属性指向了Object构造函数,无法通过constructor属性确定对象类型了。
// 可以通过在上述方法中重置constraintor的指向,添加 constructor;People,即可
// 手动添加的缺点是constructor属性的特性[[Enumerable]](枚举)设为true,默认是不可枚举的
// 因此再做一下改动
Object.defineProperty(People.prototype, "constructor", {
enumerable: false,
value:People
});
var people1 = new People();
var peoplekeys = Object.keys(People.prototype);
var people1keys = Object.keys(people1);
var peoplekeysAll = Object.getOwnPropertyNames(Person.prototype);
console.log(peoplekeys);
console.log(people1keys);
console.log(peoplekeysAll);
// 特别注意:当用简化形式重写原型对象时,相当于重写了原型,此时切断了构造函数和原来原型的联系,此时如果新建的实例是紧跟在构造函数之后而重写prototype之前是,该实例调用的还是指向原来的原型,此时调用新的原型中的属性时是找不到的。
// 此时的原型指向关系图可以简述为
//---------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
// 因此需要注意,新的实例必须在重写完Prototype后创建。
// 原型模式存在的问题:
// 对于一般属性,当第一个实例修改时会自动在实例中添加自己的同名属性屏蔽原型属性,
// 然而当原型中的属性是引用类型时,例如数组,此时第一个对象对数组值的改变将在第二
// 个对象中反映出来,此时的改变会影响所有对象的引用值。
// 原型模式 和 构造函数模式 组合最优化
// 构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性
// 动态原型模式
// 解决构造和原型独立存在的实现方案;
function P(name,age){
this.name = name;
this.age = age;
if(typeof this.sayName != 'function'){
P.prototype.sayName = function(){
alert(this.name);
};
};
};
var p1 = new P('jumP',18);
p1.sayName();
// 寄生构造函数模式
// 在之前几种模式都不适用的情况下考虑
// 类似于工厂模式,多了个new的创建的过程
// 用法之一,可以用该方法重写或给原生对象(String,Array...)添加新方法
function SpecialArray(){
var values = new Array();
//添加值
values.push.apply(values, arguments);
//添加方法
values.toPipedString = function(){
return this.join("|");
};
return values;
};
var colors = new SpecialArray("RED", "BLUE");
console.log(colors.toPipedString());
// 稳妥构造函数模式
// 类似于寄生构造模式,但是其中禁用this new 的使用,适合用于一些安全的环境中,
// 或者防止数据被其他应用程序改动时。改造如下:
function W (nmae, age){
var o = new Object();
// 可以定义一些私有变量或者函数
// 添加方法
o.sayName = function(){
alert(name);
};
};
</script>
</body>
</html>
THIS END