{ 原型链模式
构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的 --> 实例识别
function CreatejsPerson(name,age){
this.name = name;
this.age = age;
}
CreatejsPerson.prototype.writeJS = function(){
console.log('my name is '+ this.name);
}
var p1 =new CreatejsPerson('rose',23);
var p2 =new CreatejsPerson('jack',22);
console.log(p1.writeJS === p2.writeJS ); // true
基于构造函数模式的原型链模式
解决了: 方法或者属性公有的问题 -> 把实例之间相同的属性和方法提取成公有的属性和方法 -> 将其放在 CreatejsPerson.prototype 上即可
记住的三句话:
1 每一个函数数据类型( 普通函数、类 )都有一个天生自带的属性 prototype
(原型),并且这个属性是一个对象数据类型
的值
2 并且在 prototype 上浏览器天生给它加了一个属性 constructor
(构造函数),属性值是当前函数本身
3 每一个对象数据类型 ( 普通的对象、实例、prototype…) 也天生自带一个属性__proto__
, 属性值是当前实例所属类的原型(prototype)
function Fn(){
this.x = 100;
this.sum = function(){ ... }
}
Fn.prototype.getX = function(){
console.log(this.x);
}
Fn.prototype.sum = function(){ ... }
var f1 = new Fn;
var f2 = new Fn;
console.log('result:', Fn.prototype.constructor === Fn); // true
1Object 是js中所有对象数据类型的基类
在 Object.prototype 上没有 proto 这个属性
f1 instanceof Object
-> true 是因为通过 proto 可以向上级查找,不管有多少级,最后总能找到 Object
2 原型链模式
f1.hasOwnProperty('x');
// -> hasOwnProperty是 f1 的一个属性
但是,我们发现在f1的私有属性上并没有这个方法,那是如何处理的呢?
1) 通过 对象名.属性名的方法获取属性值的时候,首先在对象的私有属性上进行查找,如果私有中存在这个属性,则获取的是私有属性的值;
2) 如果私有的没有,则通过 proto 找到所属类的原型(类的原型上定义的属性和方法都是当前实例公有的属性和方法),原型上存在的话,获取的是公有属性
3) 如果原型上也没有,则继续通过原型上的__proto__继续向上查找,一直找到 Object.prototype为止
f1.getX === f2.getX
-> true 都是公有的
f1.__proto__.getX === f2.getX
-> true 前者是省略查找私有的
f1.getX === Fn.prototype.getX
-> true
f1.sum === f1.__proto__.sum
-> false 前者是私有的,后面是公有的
f1.sum === Fn.prototype.sum
-> false
注意:
f1.hasOwnProperty
-> f1.__proto__.__proto__.hasOwnProperty
在 IE 浏览器中,原型链模式也是同样的原理,但是 IE 浏览器怕你通过__proto__
把公有的修改,禁止使用__proto__
f1.sum = function(){ 修改私有的sum...}
f1.__proto__.sum = function(){ 修改公有的sum...}
Fn.prototype.sum = function(){ 修改公有的sum...}
{ 批量设置公有属性
1 起别名方式
var pro = Fn.prototype; // 设置了一个别名 pro, 两者指向同一地址
pro.getY = function(){...}
pro.getZ = function(){...}
2 重构原型对象方式
自己新开辟一个堆内存,存储公有的属性和方法,把浏览器原来给 Fn.prototype 开辟的替换掉
只有浏览器天生给 Fn.prototype 开辟的堆内存里面才有 constructor , 而我们自己开辟的这个堆内存没有这个属性,所以 f.constructor 指向的就不是 Fn,而是 Object
为了和原来保存一致,需要手动增加 constructor 指向
Fn.prototype = {
constructor: Fn,
getX: function(){...},
getY: function(){...}
}
{ 给内置 类增加公有的属性
// 给 内置类 Array 增加数组去重的方法
Array.prototype.unique = function(){...}
Array.prototype = {
constructor: Array,
unique:function(){ }
}
console.dir(Array.prototype);
用以上这种方式会把之前已经存在于原型上的属性和方法给替换掉,所有如果用这种方式修改内置类的话,浏览器是给屏蔽掉的
所以是用第一种方式修改内置类的属性和方法,一个一个的添加,(在原型上增加方法,如果方法名和原来内置类的重复了,就会把内置类的方法修改掉)
在内置类的原型上增加方法时,命名需要加特殊的前缀
{ 原型链 this
在原型链模式中,this 常用的有两种情况
1 在类中 this.xxx = xxx; this 指当前类的实例
2 在某一个方法中(公有和私有)的 this , 看执行的时候 . (点)前面是谁 this 就是谁
a) 需要先确定 this 的指向(this 是谁)
b) 把 this 替换成对应的代码
c) 按照原型链查找的机制,一步步的查找
function Fn(){
this.x = 100;
this.y = 200;
this.getY = function(){
console.log(this.y);
}
}
Fn.prototype = {
constructor: Fn,
y: 300,
getX: function(){
console.log(this.x);
}
getY: function(){
console.log(this.y);
}
}
var f = new Fn;
f.getX(); // 100
f.__proto__.getX(); // undefined
查找步骤: f.getX()
; -> console.log(f.x);
->100
f.__proto__.getX();
-> this 是 f.__proto__
-> console.log(f.__proto__.x)
-> undefined
数组去重( this 的使用 )案例:
// 在内置类上扩展自己的方法
Array.prototype.myUnique = function(){
// this 指向当前类的实例 ary, 这个方法不用传参
var temp = [];
for(var i=0; i<this.length; i++){
var cur = this[i];
if(temp[cur] == cur){
this[i] = this[this.length -1]; //最后一项
this.length--;
i--;
continue;
}
temp[cur] = cur; // 以值为下标
}
}
链式写法:
ary.sort(function(a,b){
return a - b;
}).reverse().pop();
sort 是 Array.prototype 上的公有方法,ary 是 Array 类的一个实例,ary 可以使用 sort 方法,其执行完成的返回值是一个排序后的“数组”,可以继续执行数组类的公有方法
要让自己写的数组去重的代码也能实现链式编程,在代码最后添加:return this;
思考:slice的实现,参数的大小,个数,负值,链式写法
遍历
for… in 遍历 对象,遍历出私有的和手动添加的公有方法
手动添加: Object.prototype.xxx = function(){…}
原型上方法都是不可枚举的
控制台查看:Object.prototype 回车 xxx 方法颜色不一样
console.log(f.propertyIsEnumerable('age')
); // true
考虑: 只遍历私有的方法和属性
加判断:用 propertyIsEnumerable 或 hasOwnProperty
Object.create(proto, [propertiesObject]) ie6-8 不兼容
proto 新创建对象的原型对象 ,上述方法返回值是一个新对象,带着指定的原型对象和属性
var obj = {
getX: function() {}
}
function Fn(){ }
var f = new Fn;
默认 f.constructor 为 Fn( ) {…}
添加代码 Fn.prototype = obj; 后,f.constructor 为 Object( ){ …}
若要其指向原来的,在 obj 中添加 constructor: Fn
此时 obj 和 Fn.prototype 指向同一地址
要求是能使用 obj中的方法,不改变 obj, 怎么克隆?
用一个新对象 for…in 遍历赋值,只要私有的
var obj2 = {};
for(var key in obj){
if(obj.hasOwnProperty(key)){
obj2[key] = obj[key];
}
}
另一种实现: var obj3 = Object.create(obj);
控制台对比: dir obj2
vs dir obj3
模拟 Object.create()
var obj = {};
function object(o){
function Fn(){ }
Fn.prototype = o;
return new Fn;
}
var newObj = object(obj);
{ 常用的六种继承方式
1 原型继承
以 按钮标签 button为例, 一级一级往上找
btn.__proto__
->HTMLButtonElement -> HTMLElement -> Element -> Node -> EventTarget ->Object
模拟实现:
EventTarget.prototype = new MyObject();
EventTarget.prototype.addEventListener = function(){ }
原型继承是js中 最常用的一种继承方式
子类B想要继承父类A中所有的属性和方法(公有+私有),只要 B.prototype = new A;
即可,记得加上 B.prototype.constructor = B;
原型继承的特点:它是把父类中私有的 + 公有的都继承到子类原型上(子类公有)
核心:原型继承并不是把父类中的属性和方法克隆一份,而是让B和A之间增加了原型链的连接,以后 B的实例 想要 A 中的方法,需要一级一级向上查找来使用
2 call 继承
把父类私有
的属性和方法克隆一份,作为子类私有的属性
function A(){
this.x = 100;
this.showY = function(){
return this.y;
}
}
A.prototype.getX = function(){
console.log('a:',this.x);
}
function B(){
// this -> n
A.call(this); // A.call(n)-> 让 A 执行,把 A 中的this 指向 n
this.y = 23;
}
var n = new B;
console.log('n.x',n.x); // n.x 100
console.log('n.showY',n.showY()); // n.showX 23
3 冒充对象继承
把父类私有的 +公有的克隆一份 给子类私有
在 B 中:
function B(){
// this -> n
var temp = new A;
for(var key in A ){
this[key] = temp[key];
}
temp = null;
}
4 混合模式继承
原型继承 + call 继承
function B(){
A.call(this); // A 中私有的在B中有两份
}
B.prototype = new A;
B.prototype.constructor = B;
5 寄生组合式继承
用 Object.create ,有兼容问题,自己模拟
function B(){
A.call(B); // 私有
}
B.prototype = objectCreate(A.prototype); // 公有
B.prototype.constructor = B;
function objectCreate(o){ // 模拟
function fn(){}
fn.prototype = o;
return new fn;
}
6
function avgFn(){ //求平均,先排序,去最低最高,取平均
Array.prototype.sort.call(arguments,function(a,b){
return a - b;
});
Array.prototype.pop.call(arguments);
Array.prototype.shift.call(arguments);
return (eval(Array.prototype.join.call(arguments,'+'))/arguments.length).toFixed(2);
}
console.log(avgFn(10,2,53,23)); // 保留了小数点2位
这种继承不兼容 (ie )
function avgFn(){
console.log( typeof arguments); // object
arguments.__proto__ = Array.prototype; // 只公有
arguments.sort(function(a,b){ return a-b; });
arguments.pop();
arguments.shift();
return (eval(arguments.join('+')) / arguments.length).toFixed(2);
}
{ 原型链综合练习
代码中有一个 id 为 ‘box’ 的 div (a , document, window)
控制台: dir( box ) 查看其所有私有属性,一级一级往上找方法和属性
ps:__proto__
指向 类 Fn 的原型 不是 new 出来的, 又所有对象都是 object 的一个实例, 所以其指向 Object.prototype
增加一个名,在控制台查看的时候和其它原有的保持一致:
结果: true 20 NaN