原型(如何链式写法&&批量设置公有属性&&继承方式)(1-2)

原型

在内置类的原型上扩展我们的方法(链式写法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//数组去重
Array.prototype.myUique=function(){
var arr={};
for(var i=0; i<this.length;i++){
var cur=this[i];
if(arr[cur]==cur) {
this[i]=this[this.length-1];
this.length--;
i--
continue;
}
arr[cur]=cur;
}
arr=null;//清除堆内存
return this;//目的是实现链式写法
};
var a=[11,1,23,3,3,3,3,3];
a.myUique().sort(function(a,b){return a-b});
console.log(a);
```
> 拓展:链式写法
为什么可以使用链式写法
ary.sort(function(a,b){return a-b;}).reverse().pop();
`sort(返回的是排序后的**数组**)是Array.prototype上的公有的方法,ary是Array这个类的实例,所以ary可使用sort();`
&emsp; &emsp;sort返回一个数组,所以可执行reverse();
&emsp; &emsp;reverse执行完返回的值是一个数,可以继续执行pop();
&emsp; &emsp;pop返回的是被删除的元素,
**1、练习链式写法**(未解决)
- [ ] 1、mySclice
```js
//考虑情况(不能用内置方法push等)
//slice(n,m);
//slice(n);
//slice();
//n<0,m<0;
//n<m;
//n,m超过数组的长度了
//n,m不是有效数字
Array.prototype.mySlice=function(m,n){
var str="";
if(typeof m=="undefined" && typeof n=="undefined"){
return
}
if(typeof m=="Number" && typeof n=="undefined"){
for(var i=0;i<this.length;i++){
if(i<=m){
str[0]=arr[m];
}else{
str[i-m]=arr[i];
}
}
return this;
}
if(typeof m=="Number" && typeof n=="Number"){
if(i<=m){
str[0]=arr[m];
}else if(i>=n){
str[n-m+1]=arr[n];
}else if(i>m&&i<n){
str[i-m]=arr[i];
}
return this;
}
}
var str="hello";
str.mySlice(2);
console.log(str);
  • [ ] 2、myNume
1
2
3
(5).push(10).reduce(2) 5+10-2
Number.prototype.plus=function(num){}
Number.prototype.plus=function(num){}

批量设置公有属性

1
2
3
4
5
function fn(){
}
fn.prototype.getX=function(){};
fn.prototype.getY=function(){};
fn.prototype.getZ=function(){};
  • [x] 1、起别用
    (jQuery用)

    1
    2
    3
    4
    5
    6
    function fn(){
    }
    var pro=fn.prototype;
    pro.getX=function(){};
    pro.getY=function(){};
    pro.getZ=function(){};
  • [x] 2、重构原型对象的方式

概念

1
2
3
function fn(){
}
//自己新开辟一个堆内存,存储我们公有的属性和方法,把浏览器原来给

  • [ ] 自定义类增加公有属性

图形26

1
2
3
4
5
6
7
8
9
fn.prototype={
constructor:fn;//为了和原来的保持一致,需手动增加constructor的指向
a:function(){},
b:function(){},
c:function(){};
}
var f=new fn;
console.log(f.constructor)//-->object(只有浏览器天生的fn.prototype开辟的堆内存才有constructor,自己开辟的堆没这个属性;)
  • [ ] 内置类增加公有属性
        浏览器自动屏蔽这种方法修改内置类的属性和方法
    1
    2
    3
    4
    5
    6
    7
    Array.prototype={
    constructor:Array;
    unique:function(){}
    }
    console.dir(Array.prototype);
    //本身内置类的属性方法未被修改

    但是一个个修改内置的方法,如果方法名和原来内置重复,则会修改(再内置类的原型上增加方法,命名需要特殊的前缀)

1
2
3
4
5
6
7
Array.prototype.sort=function(){
console.log("ok");
}
var arr=[1,3];
arr.sort();
console.log(ary);//"ok"
//本身内置类的属性方法被修改了

继承前情提要(小技巧)

  • [x] 技巧一用for in遍历私有属性

  • [ ] for in默认遍历私有的和自己扩展的属性和方法

1
2
3
4
5
6
Object.prototype.aaa=function(){};
var obj={name:"li",age:7};
for(var key in obj){
consolelog(key);//name,age,aaa
}
}
  • [ ] 如果只需要遍历私有的,可以加一下两种判断
1
2
3
4
5
6
7
8
9
10
11
12
Object.prototype.aaa=function(){};
var obj={name:"li",age:7};
for(var key in obj){
//if(obj.prototypeIsEnumerable(key)){
// consolelog(key); //name.age
//}
if(obj.hasOwnProperty(key)){
consolelog(key);//name.age
}
}
  • [x] 技巧二Object.create(obj)方法创建一个新的对象,把obj作为对象的原型。Object.create(原型[一组属性和值])
    作用:多建立一层原型,修改各自的层,不互相受影响
    IE6~8不兼容

原型继承(指向)(父类的私有+公有–>子类公有)

  • [x] 1、Fn.prototype=obj;
  • [ ] 怎么实现原型继承(怎么让Fn的实例f继承obj)
    –>将为Fn.prototype重新指向新堆内存obj(Fn.prototype=obj;)
1
2
3
4
5
6
7
8
9
10
11
var obj={ //开创一个堆内存(作为父类)
num:10
};//-->obj=xx00
function Fn(){};
Fn.prototype=obj;//-->Fn.prototype=xx00
var f=new Fn;
f.num;//-->10继承到obj的属性和方法(私有的num没有,找到其原型也没有,最后找到obj的num)
//新问题:私有的constructor没有,找到其原型也没有(不过之前的原型堆内存被释放销毁),最后找到obj的内置类的原型的constructor
f.constructor;//-->Object(){};

易错点: Fn.prototype=obj;var f=new Fn;的顺序不能颠倒

  • [ ] 怎么解决因为Fn.prototype重新指向新堆内存obj后使得原有的Fn的原型被销毁的问题,Fn的实例找不到Fn原有的原型的constructor了,只能往上找到obj的原型的constructor,结果指向了Object(){}
    —>在obj中强行添加constructor:Fn;·
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj={ //开创一个堆内存
constructor:Fn,//Fn的实例重新指向Fn
add:function(){}
};//-->obj=xx00
function Fn(){};
Fn.prototype=obj;//-->Fn.prototype=xx00
var f=new Fn;
f.constructor;//-->Fn
//新问题:obj中被添加了sum,地址一样,结果obj也被添加了sum
Fn.prototype.sum=function(){return 1};
alert(f.sum());//1
alert(obj.sum());//1
  • [ ] Fn.prototype和obj互影响的问题
    –>只克隆,不引用地址
    方法一:
1
2
3
4
5
6
7
8
9
10
function Fn(){};
var obj={
add:function(){}
};
var obj2={};//普通对象:遍历到的私有属性复制obj2的私有属性中
for(var key in obj){
if(obj.hasOwnProperty(key)){
obj2[key]=obj[key]
}
}//obj!=obj2
  • [x] 2、Object.create(obj)
    方法二:使用技巧二的Object.create(obj)方法;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Fn(){};
    var obj={
    add:function(){}
    };
    //obj2是把obj的私有属性放到obj2的原型上
    var obj2=Object.create(obj);//obj2的原型指向obj
    > obj2
    > __proto__:
    > add:function(){}
    > __proto__:Object.prototype
  • 分析obj、obj2、Fn、f的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    function Fn() {};
    var obj = {
    getx: function() {}
    };
    var obj2 = Object.create(obj);
    Fn.prototype = obj2;
    var f = new Fn;
    //obj添加objadd
    obj.objadd = function() {};
    obj.objadd();
    obj2.objadd();
    Fn.prototype.objadd();
    // Fn.objadd();访问不到???
    f.objadd();
    //obj2添加obj2add(同 Fn.prototype添加Fnproadd)
    obj2.obj2add = function() {};
    // obj.obj2add();访问不到
    obj2.obj2add();
    Fn.prototype.obj2add();
    // Fn.obj2add();访问不到
    f.obj2add();
    //Fn添加Fnadd
    Fn.Fnadd = function() {};
    // obj.Fnadd();访问不到
    // obj2.Fnadd();访问不到
    // Fn.prototype.Fnadd();访问不到
    Fn.Fnadd();
    // f.Fnadd();访问不到
    //-->Fn添加的函数不是公有属性也没有this(无法指向了这个新对象f,即无法将构造函数的作用域赋给新对象f)

分析图27

  • 模拟Object.create(obj)实现原型继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var obj={
    add:function(){}
    };
    function object(o){
    function Fn(){
    }
    Fn.prototype=obj;
    return new Fn;
    }
    var newobj=object(obj);
  • [x] 3、B.prototype=new A(链接)
    原型继承是最常用的一种继承方式
    子类B要继承父类A的所有(公私有),只需B.prototype=new A;
    特点:父类私有和公有的都继承到子类原型上(子类公有的);
    核心:不是克隆父类,而是让A和B增加了原型链的链接,B的实例用A的方法,需要一级级查找

图形28;

1
2
3
4
5
6
7
8
9
10
function A(){
this.x=100;
}
A.prototype.geX=function(){
};
function B(){
this.x=200;
}
B.prototype=new A;//实现原型继承

call继承(克隆)(父类的私有–>子类私有)

父类私有的–>子类私有

1
2
3
4
5
6
7
8
9
10
11
12
function A(){
this.x=100;
}
A.prototype.geX=function(){
};
function B(){
//this->n
A.call(this);//-->A.call(n),把A中的this变为了n,(相当于把A中属性复制给B一份)实现继承
}
var n=new B;
console.log(n.x);


冒充对象继承(克隆)(父类的私有+公有–>子类私有)

父类私有的公有的–>子类私有的

1
2
3
4
5
6
7
8
9
10
11
12
13
function A(){
this.x=100;
}
A.prototype.geX=function(){
};
function B(){
//this-->n
var temp=new A;//把A的实例当成普通对象遍历
for(var key in temp){
this[key]=temp[key];
}
}
var n=new B;

混合模式继承(原型继承+call继承)(常用)

( 父类的私有–>子类私有的)
(父类的私有–>子类公有的)
(父类的公有–>子类公有的)

1
2
3
4
5
6
7
8
9
10
11
12
function A(){
this.x=100;
}
A.prototype.geX=function(){
};
function B(){
//this->n
A.call(this);//-->A.call(n),n.x=100( 父类的私有-->子类私有)
}
B.prototype=new A;//B.prototype:x=100( 父类私有的-->子类公有的(小问题:多复制了一次))
//B.prototype:geX (父类公有-->子类公有)
B.prototype.constructor=B;

寄生组合继承

为解决混合模式继承多复制的那一次(父类的私有–>子类公有的)

1
2
3
4
5
6
7
8
9
10
11
12
function A(){
this.x=100;
}
A.prototype.geX=function(){
};
function B(){
//this->n
A.call(this);//-->A.call(n),n.x=100( 父类的私有-->子类私有)
}
B.prototype=Object.creat(A.prototype);
B.prototype.constructor=B;

中间类继承法

…………