原型链模式
实例识别:
构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的
function CreateJsPerson(name, age){
this.name = name;
this.age = age;
}
CreateJsPerson.prototype.writeJs = function(){
console.log("my name is " + this.name + ", i can write js ~~")
}
基于构造函数模式的原型模式解决了,方法或者属性共有的问题,想让谁共有就把他放在CreateJsPerson.prototype上即可
js中规定的
prototype
- 每一个函数数据类型(普通函数,类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值
constructor
- 并且在prototype上浏览器天生给它加了一个属性constructor(构造函数),属性值是当前函数(类)本身
_ proto _
- 每一个对象数据类型(普通的对象,实例,prototype..)也天生自带一个属性: _ _proto _ _,属性值是当前实例所属类的原型(prototype)
function Fn(){
this.x = 100;
}
Fn.prototype.getX = function(){
console.log(this.x);
}
var f1 = new Fn;
var f2 = new Fn;
console.log(Fn.prototype.constructor === Fn); // true
console.log(f1 instanceof Object); // true
- f1 instanceof Object -> true 因为f1通过_ _ protp _ _ 可以向上级查找,不管查找多少级都可以查找到Object
- 在Object.prototype上没有_ _protp _ _ 这个属性
原型链查找机制
- 通过 对象名.属性名 的方式获取属性值的时候, 首先在对象的私有的属性上进行查找, 如果私有中存在这个属性,则获取的是私有的属性值;
- 如果私有的没有,则通过__proto__找到所属类的原型, 类的原型上定义的属性和方法都是当前实例公有的属性和方法, 原型上存在的话, 获取的是共有的属性值;
- 如果原型上也没有,则继续通过原型上的__proto__继续向上查找, 一直找到Obejct.prototype为止
console.log(f1.getX === f2.getX); // true
console.log(f1.__proto__.getX === f2.getX);// true
console.log(f1.getX === Fn.prototype.getX); // true
在IE浏览器中,原型模式也是同样的原理,但是IE浏览器怕你通过__proto__把公有的修改,禁止我们修改__proto__
批量设置共有属性
- 起别名
function Fn(){
this.x = 100;
}
var pro = Fn.prototype;
pro.getX = function(){}
pro.getY = function(){}
- 重构原型对象的方式
function Fn(){
this.x = 100;
}
Fn.prototype = {
getX: function(){},
getY: function(){}
}
var f = new Fn;
console.log(f.constructor)' // Object
只有浏览器天生给Fn.prototype开辟的堆内存里面才有constructor,而我们自己开辟的这个堆内存没有这个属性,
这样constructor指向就不再是Fn而是Object
为了和原来的保持一致,我们需要手动的增加constructor的指向
Fn.prototype = {
constructor: Fn
}
会把之前已经存在于原型上的属性和方法给替换掉, 用这种方式修改内置类的话, 浏览器会给屏蔽掉
Array.prototype = {
constructor: Array,
unique: function(){}
}
console.dir(Array.prototype);
但是可以使用prototype属性,一个个修改内置的方法,如果方法名和原来内置的重复了, 会把内置的修改掉, 在内置类的原型上增加方法, 名命都需要加特殊的前缀
Array.prototype.sort = function(){
console.log("lemon");
}
var ary = [1, 2, 2, 1, 2, 3, 4];
ary.sort();
console.log(ary);
常用的六种继承模式
for in 循坏在遍历的时候, 可以把自己私有的和在它所属原型上扩展的属性和方法都遍历到.
Object.prototype.aaa = function(){};
var obj = {name: 'lemon', age: 22};
for(let key in obj){
console.log(key)
}
可以使用以下方法,进行判断处理
for(let key in obj){
if(obj.propertyIsEnumerable(key)){
console.log(key);
}
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
console.log(key);
}
}
Object.create(proObj)
创建一个新的对象, 但是要把proObj作为这个对象的原型, 在IE6-IE8不兼容(ECMAScript5)
var obj = {
getX: function(){}
}
var obj2 = Object.create(obj)
console.dir(obj2)
obj.getY = function(){}
console.dir(obj2)
自己实现一个create
var obj = {
getX: function(){}
}
function object(o){
function Fn(){}
Fn.prototype = o;
return new Fn();
}
var newObj = object(obj);
原型链继承
原型继承是JS中最常用的一种继承方式, 子类B想要继承父类A中的所有的属性和方法(私有+公有), 只需要让B.prototype = new A;
特点: 它是把父类中的私有+公有的都继承到了子类原型上(共有的)
核心: 原型继承并不是把父类中的属性和方法克隆一份一模一样的给B,而是让B和A之间增加了原型链的连接, 以后B的实例n想要A中的getX方法,需要一级一级的向上查找来使用
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(){
this.y = 200;
}
B.prototype = new A
缺点: 不安全, 可以通过子类或子类的实例,更改父类原型链上的属性和方法, 对A的实例和子类造成影响
var b = new B
var a = new A
b.__proto__.__proto__.getX = 3000;
console.log(a.getX)
B.prototype.__proto__.getX = 2000;
console.log(a.getX)
call继承
借用构造函数, 伪造对象继承和经典继承
call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
A.call(this)
}
var b = new B;
console.log(b.x)
缺点:
父类在原型链中定义的函数不能被子类访问,也就是说所有的函数都必须写在构造函数内部,无法复用
冒充对象
把父类私有和公有的属性和方法,克隆一份一模一样的给子类私有的
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
var temp = new A;
for(var key in temp){
this[key] = temp[key];
}
}
var b = new B;
console.log(b.x)
混合模式继承
子私有 = 父私有, 子公有 = 父私有 + 父公有
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
A.call(this)
}
B.prototype = new A;
B.prototype.constructor = B;
var b = new B;
console.log(b.x)
寄生组合式继承
子私有 = 父私有, 子公有 = 父公有
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
A.call(this)
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
var b = new B;
console.log(b.x)
中间类继承法,不兼容
function avgFn(){
arguments.__proto__ = Array.prototype;
arguments.sort(function (a, b) {
return a - b;
})
arguments.pop()
arguments.shift()
return eval(arguments.join("+")) / arguments.length;
}
avgFn(10, 20 ,30 ,10 ,30, 40, 40); // 26