珠峰js笔记1.2(原型链扩展)

{ 原型链模式

构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的 --> 实例识别

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

猜你喜欢

转载自blog.csdn.net/asjean/article/details/84233672
今日推荐