原型的深入理解
- 每个函数都有是函数数据类型的实例,实例就会有
__proto__属性,指向所属类的原型 Object基类也是一个函数,所以Object.__proto__也指向Function.prototype- 函数的实例有
__proto__和prototype两个属性 Function基类自身也是个函数,所以他自身也有一个__proto__,指向所属类的原型,Function.prototype
原型拓展
一个方法重复使用时,可以直接将其定义为原型上的公有属性,例如
function Fn(name,age){
this.name=name;
this.age=age;
}
Fn.prototype.say=function(){
alert('我的名字是:'+this.name+'----我的年龄是:'+this.age);
}
var f1=new Fn("阿杰",18);
f1.say();
封装一个push
Array.prototype.myPush=function(){
for(var i=0;i<arguments.length;i++){
// this[this.length]=arguments[i];
// this.splice(this.length,0,arguments[i]);
this.splice(this.length,1,arguments[i]);
}
return this.length;
}
var ary=[1,2,3];
console.log(ary.myPush(4,5,6,7));//返回数组长度7
console.log(ary);//新数组[1,2,3,4,5,6,7]
链式调用
实现链式写法:保证每次函数执行完毕之后的返回结果是当前类的实例
//想要实现一个需求:
//var ary=[5,8,2,1,10] ;想要让这个数组先排序,然后再倒序,然后再往里面添加一个10,然后再删除第一项;
var ary=[5,8,2,1,10];
ary.sort(function(a,b){return a-b}).reverse().push(10);//因为push返回值是10所以不能继续
ary.shift();
console.log(ary);//[8,5,2,1,10]
原型重定向
手动更改原型的指向
,没有constructor,所以会沿着原型链查找,找到Object.prototype的constructor===>Object所以手动更改的原型需要手动加一个constructor属性
function Fn(){
this.x=100;
}
Fn.prototype.getX=function(){
return this.x;
}
var f1=new Fn();
Fn.prototype={
//consturctor:Fn,
getY:function(){
return this.x
}
};
var f2=new Fn();
console.log(f1.getX());//100
console.log(f2.getX()); // 报错
console.log(f1.constructor); //Fn
console.log(f2.constructor); // Object
封装检测是不是公有的
Object.prototype.myHasOwnProperty=function(item){return item in this&&!this.hasOwnProperty(item)?true:false;}
[1,2,3].myHasOwnProperty("length");//false
//hasOwnProperty检测的是是不是私有的
[1,2,3].hasOwnProperty("length");//true
new的时候先执行()之前的,如new fn(),先执行new fn然后在执行()
function Foo(){
getName=function(){
console.log(1);
};
return this;
}
Foo.getName=function(){
console.log(2);
}
Foo.prototype.getName=function(){
console.log(3);
}
var getName=function(){
console.log(4);
}
function getName(){
console.log(5);
}
Foo.getName();//2
getName(); //4
Foo().getName();//1
getName();//1
new Foo.getName();//{} 2
new Foo().getName();// {} 3先算new Foo再算.getName
new new (Foo().getName());//{} 3
继承 背
原型继承:
A 类和B 类,B类想要继承A类的私有和公有属性,我们可以用原型继承:让B的prototype(原型)等于 A的实例
function A(){
this.a="a";
}
A.prototype.getA=function(){
console.log(this.a);
}
function B(){
this.b="b";
}
B.prototype=new A();
var b=new B();
console.log(b);//"b"
中间类继承
- 继承公有属性
某些实例并不属于某类,但是想用其原型上的方法,我们可以手动的去更改实例的
__proto__,让它指向这个类的原型,就可以使用其原型上的方法
function fn(){
arguments.__proto__=Array.prototype;
arguments.sort(function(a,b){
return a-b;
})
return arguments;
}
fn(8,2,1,9);//Arguments(4) [1, 2, 8, 9, callee: ƒ, Symbol(Symbol.iterator): ƒ]
call继承
让B的实例包含A,B的私有属性
function A(){
this.a="a";
this.x=100;
}
function B(){
//此时的this是b1
A.call(this);
this.b="b";
this.y=200;
}
var b1=new B();
console.log(b1);//{a:"a",x:100,b:"b",y:200}
寄生组合继承
有A类、B类两个类,让B类继承A类的私有和公有
function A(){
this.a="a";
this.x=100;
}
A.prototype.getA=function(){
console.log("A");
}
function B(){
A.call(this);
this.b="b";
this.y=200;
}
var obj=Object.create(A.prototype);
//创建一个对象,让他的__proto__指向A.prototype
B.prototype=obj;
var b1=new B();
console.log(b1.getA);
改变this的三种方式
this不能手动更改,会报错
function fn(){
this=100;
console.log(this);
}
fn();//报错,因为this不能手动更改Invalid left-hand side in assignment
call,bind,apply这三个方法存在Function的原型上,只要是函数都可以使用,(Object基类也可以)
call
- call里面的第一个参数是this的指向
- 后面的其他当成参数传进去
- 严格模式下:
第一个参数为null,this就是null;不传或者undefined,this就是undefined- 非严格模式下:
第一个参数不传或者null、undefined,this都是window
function fn(){
console.log(this);//obj{}
console.log(arguments);//Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
var obj={};
fn.call(obj,1,2,3);
call执行过程
- 通过原型链找到Function.prototype上的call方法
- 找到之后,让fn这个方法执行
- 执行的时候,内部需要把fn函数里面的this进行改变,改成第一个参数
apply
- 与call大致一样,只是除了第一个值代表this指向以外,
后面的值是以数组或类数组进行传参的
function fn(){
console.log(this);//obj{}
console.log(arguments);//Arguments [Array(3), callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
var obj={};
fn.call(obj,[1,2,3]);
bind
- bind只是预处理this指向的,但是它是预处理this的指向,函数调用后,只是预处理了里面的this,要想让fn执行,我们需要再次调用它的返回值
let obj = { name: 1 };
function fn(x,y) {
console.log(this,x,y);
}
var res=fn.bind(obj,1,2);//只处理不执行函数
res();//需要再执行一下
封装call
基础版
Function.prototype.myCall=function(obj,...ary){
// var res=null;
// if(obj){
// obj.this=this;
// res=obj.this(...ary);
// delete obj.this;
// }else{
// res=this(...ary);
// delete obj.this;
// }
// return res;
obj=obj||window;
obj.this=this;
res=obj.this(...ary);
delete obj.this;
return res;//返回fn执行返回的结果
}
var obj={name:"li"};
function fn(x,y){
console.log(this);
console.log(arguments);
return x+y;
}
fn.myCall(obj,1,2);
进阶版
var A=function(){
console.log(this)
};
// A.call(100);
Function.prototype.mycall=function(index,...ary){
index=index||window;
index instanceof Object||index instanceof Function?null:index=Object(index);
var key=Symbol("key"),res;
index[key]=this;
res=index[key](...ary);
delete index[key];
return res;
}
A.mycall(100);
封装bind
var A=function(x,y){
console.log(this);
return x+y;
};
Function.prototype.mybind=function(...ary){
let that=this;
return function(){
that.call(...ary);
}
}
document.body.onclick=A.mybind(100,10,20);