<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
//写在前面:创建的每个函数都有一个属性:prototype,这个属性是一个指针,指向原型对象
//原型对象的用途:包含可以由特定类型(Object,Array,Function以及各种自定义)的所有实例共享的属性与方法
//或者说:prototype就是通过 调用构造函数构建的 那个对象实例 的原型对象
//这样就不必在构造函数中定义对象实例的信息,直接将这些信息添加到原型对象中即可
function Create(){};
Create.prototype.name = "Harold";//通过Create.prototype来为构造函数的原型对象添加方法
Create.prototype.method = function(){
// alert("upup就完事了!");
};
var obj1 = new Create();
var obj2 = new Create();
obj1.method();
console.log(obj1.method() == obj2.method() );//true
//两个对象访问的是同一组属性与方法
//如何理解原型对象?
//无论何时,只要创建了一个函数,就会根据一组特定的规则,为该函数创建一个prototype属性(指向函数的原型对象)
//默认情况下,所有原型对象都会获得一个constructor属性,这个属性是一个指向prototype属性所在函数的指针
// Person.prototype.construtor指向Person 而Person的prototype指向PersonPrototype 高程三P148
//在创建了自定义的构造函数Func后,其原型对象默认只会取得constructor属性,其他均有Object继承而来
//调用构造函数创建一个实例后,该实例的内部会包含一个指针,[[prototype]](属性_proto_)
//这个连接/关系存在于实例与构造函数的原型对象间,而非实例与构造函数间
//新实例的[[prototype]]/_proto_、构造函数的prototype属性均指向 Person.prototype
//注意在上面的实例中我们可以直接调用person.method() 这就是通过查找对象属性的过程来实现的
console.log(Create.prototype.isPrototypeOf(obj1));//true 用isPrototypeOf()方法判断 (这是原型对象的方法)
console.log(Object.getPrototypeOf(obj1));//Object method: ƒ ()name: "Harold"constructor: ƒ Create()__proto__: Object
///这个方法返回[[prototype]] / _proto_的值(即obj1的原型对象),== Create.prototype
//注意:每当代码读取对象的某个属性,都会执行一次搜索,先在实例中搜索,如果没有找到,则继续前往[[prototype]]指针指向的
//原型对象中搜索 这是多个对象实例共享原型所保存的属性和方法的基本原理
//可以通过对象实例访问保存在原型中的值 但不能这样重写原型中的值 只会屏蔽 而不会覆盖 联系行37
obj1.name = "budu";
console.log(obj1.name);
//使用delete操作符可以完全删除实例属性
delete obj1.name;
console.log(obj1.name);
//hasOwnProperty()方法可以检验一个实例是否自己拥有给定的属性/方法
console.log(obj1.hasOwnProperty("name"));//false
//原型与in操作符:in操作符的两种使用方式,单独使用与在for-in循环中使用
//单独使用时,in操作符会在能够通过对象访问给定的属性时返回true
console.log("name" in obj1);//true 不论通过原型还是实例,只要能访问到就true
//配合hasOwnProperty()方法 可以直接确定这个属性在实例上还是原型对象上
function hasPrototypeProperty(object,attrname){
return !object.hasOwnProperty(attrname) && (attrname in object);
//T && T = T 不在实例上且能访问到-在对象上 真·hasPrototypeProperty F && T = F
}
console.log(hasPrototypeProperty(obj1,"name"));//true
obj1.name = "new";
console.log(hasPrototypeProperty(obj1,"name"));//false
//for-in循环 返回所有通过对象能访问的,可枚举的(enumerated属性为true),包括实例和原型
//!屏蔽了原型中不可枚举属性的实例属性也会被返回,根据规定所有开发人员定义的属性都是可枚举的
for (var i in Create.prototype){//返回这个对象所有可枚举的属性~
console.log(i);
}
//Object.keys()方法取得
// var keys = Object.keys(Create.prototype);
var keys = Object.keys(obj1);//实例对象与原型对象返回的是不同的!
console.log(keys);//返回的是数组
// var attr = Object.getOwnPropertyNames(Create.prototype);
var attr = Object.getOwnPropertyNames(obj1);//也是不同的~ 可以得到所有的实例属性 无论是否可枚举
console.log(attr);
//更简单的原型语法
// 用一个包含所有属性和方法的对象字面量来重写整个原型对象
function NewCreate(){};
NewCreate.prototype = {
// constructor: "NewCreate",//可以特意这样设置 但这样会使得这个属性变得可枚举
name:"linbudu",
age:"21",
sayit:function(){
alert("If not me ,then who?")
}
}
//注意,在这里prototype对象的constructor属性不再指向NewCreate了
//在这里使用的语法本质上完全重写了该对象 该原型对象的construor属性指向Object构造函数
var obj3 = new NewCreate();
console.log(typeof obj3 );//object
console.log(obj3 instanceof Object);//true 这些对象既是Object的实例(所有对象都继承自Object),也是NewCreate的实例
console.log(obj3 instanceof NewCreate);
Object.defineProperty(NewCreate.prototype,"constructor",{
enumerable: false,
value:NewCreate,
})
//原型的动态性 在原型中查找值的过程是一次搜索,因此对原型对象做的任何修改都能立即从实例上反映出来
//即使先创建实例、后修改原型,其原因可以归结于实例与原型之间的松散连接关系,它们之间的连接只不过是一个指针而非一个副本
//调用一个构造函数创建一个实例时,会为实例添加一个只想最初原型的[[prototype]]指针
//如果把原型修改为另一个对象(重写),就相当于切断了构造函数与最初原型之间的联系
function cre(){};
var obj4 = new cre();
cre.prototype = {
constructor:"cc",
name:"qqq",
fruit:["apple","orange"]
}
// var obj4 = new cre();如果在重写原型之后再创建实例,那么引用的便是新的原型
console.log(obj4.name);//undefined,obj4在重写原型之前就已经存在,它引用的仍然是最初的原型,name属性未定义
console.log(cre.prototype.constructor);//cc 这说明重写原型对象切断了构造函数与最初原型之间的联系
//注意 最初的原型对象依然存在,在重写前就存在的实例的[[prototype]]指针仍然指向它
//牛皮的地方来了,通过原型对象不仅可以取得所有默认方法的引用,还可以定义新方法(甚至在原生的引用类型上!)
// for(var a in String.prototype){
// console.log(a);
// }
var str = Object.keys(String);
console.log(str);//返回一个空数组 估计是因为不可枚举
var str1 = "qwerdfa";
var str1_attr = Object.keys(str1);
console.log(str1_attr);//返回0~6数组...
String.prototype.newMethod = function(text){
alert(text);
}
// str1.newMethod();//undefined
// str1.newMethod(str1);//√
//原型对象的问题:1.省略了为构造函数传递初始化参数,所有实例默认情况下会取得相同的属性值
//2.对于包含基本值的属性,可以通过在实例上添加同名属性来屏蔽原型中的对应属性,达到修改值的目的
//但对于引用类型值:见下面的例子
var cre1 = new cre();
var cre2 = new cre();
cre1.fruit.push("pear");
console.log(cre1.fruit);
console.log(cre2.fruit);
//修改了一个实例引用的数组,由于该数组存在于cre.PROTOTYPE中,修改也会通过其他的实例反映出来...
//应当组合使用构造函数与原型模式
function P(name,age,job){
this.name = name;
this.age = age;
this.fruit = ["1","2","3"]
}//在构造函数中定义实例属性
// p.prototype = {
// constructor:P,
// shareMethod:function(){
// return true;
// }
// }//在原型中定义共享的方法与属性
//这样就不会发生上面的错误了,因为两个实例引用的是不同的数组
// 动态原型模式
// 在构造函数中加入
if(typeof this.method != "function"){
P.prototype.method = function(){
alert(this.attrname);
};
}
//这里只有当这个方法不存在的情况下才会为原型添加这个方法
//这段代码只有初次调用构造函数时才会执行,此后该函数的原型已经完成初始化,同时这里的修改能后立刻在所有实例中得到反映
//不能使用对象字面量重写原型,否则会切断已有的实例与新原型之间的联系(已有实例仍会指向旧原型)
//寄生构造函数模式
//创建一个函数,仅仅用来封装创建对象的代码,再返回新创建的对象
function parasitic(q,w,e){
var o = new Object();
this.q = q;
this.w = w;
return o;
}
var para_obj = new parasitic(); //与工厂模式的区别在于把函数叫做构造函数并使用new操作符
//该模式可用来为对象创建特殊的构造函数 如创建带有特殊额外方法的数组 但不能直接修改Array构造函数
function SpString(){
// var str = new String(arguments);
var arr = new Array();
arr.push.apply(arr,arguments);//在arr中以传入值为参数调用push函数
arr.newMethod = function(){
return this.join("|");
};
return arr;
// str.newMethod = function(){
// return this.substring(0,3);
// };
// return str;
}
var test = new SpString("qqqqq","111","eee");
console.log(test.newMethod());
//构造函数返回的对象与构造函数及其原型属性之间无关(即与在构造函数的外部创建对象没有什么不同)
</script>
</body>
</html>