深入学习jquery源码之继承方案的选择

prototype实现继承

原型链继承的主要思想是:让子类型的引用指向父类型的实例。

每次在函数(类)中定义了成员方法,都会导致实例有副本,因此可以借助prototype原型,进行改进

先访问自己本身的方法,没有再访问自己原型的方法,本身原型没有访问父类,父类没有,访问父类的原型方法...继续往上

  Object.prototype.say=function  () { //最上层定义say的方法
       alert("我是顶层的方法");
   }

	function person () {
	    this.say=function  () {
		   alert("我是父类的方法");//先访问本身方法,再访问原型方法
	    }
	}
 
	person.prototype.say=function  () {
	     alert("我是父类原型的方法");
	}
 
	function study () {
	this.say=function  () {
	   alert("本身的方法");
	     }
    }

    //继承person
	study.prototype=new person();


	 study.prototype.say=function  () {
	   alert("本身原型的方法");
	 } 

    var zhangsan=new study ();
	alert(zhangsan.say)//我是父类原型的方法    父类有方法,不再向顶层找方法

使用这种方式,生成的实例既是子类实例,又是父类的实例

子类访问父类型原型链属性和方法

通过原型链继承的子类,可以直接访问到父类原型链上新增的方法和属性。

原型链继承缺点:

无法实现多继承

从原型链继承的代码来看,子类的prototype属性只能指向一个父类的实例,因此只能实现单继承。

父类引用属性是共享的

父类的值类型属性在生成的每个子实例中是独有的,而对应的引用类型属性则是每个实例共享的,这个问题对于原型链继承的影响是致命的。

引用类型属性共享问题

通过call方法调用父类的构造方法,并通过this改变构造方法的指向,this就指向了cat了。

  var obj1={
      name:"java"
  }
  
  window.name="javascript";
  var func=function(){
      console.log(this.name);
  }
  
 func();  //javasript
 func.call(obj1); //java

name属性的修改,两者子类型实例name属性是不同的;而对于features属性的修改,两者features属性的值是相同的

 

create()实现继承

create的第二参数是一个对象,其中的属性如果和person重名,则会覆盖person的属性。

var person={
        name:"Tom",
        age:23,
        job:"厨师"
    };
    var anotherPerson=Object.create(person,{
        name:{
            value:"Mike"
        }
    });
    alert(anotherPerson.name);

 

apply()实现属性和方法继承全部继承

apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

apply(thisArg [,argArray] );                 // 参数数组,argArray

只接收两个参数,其中第二个参数必须是一个数组或者类数组

作用都是将函数绑定到另外一个对象上去运行,内部的this指针,都会被赋值为thisArg,这可实现将函数作为另外一个对象的方法运行的目的

function add(a,b){
  return a+b;  
}
function sub(a,b){
  return a-b;  
}
var a1 = add.apply(sub,[4,2]);  //sub调用add的方法
var a2 = sub.apply(add,[4,2]);
alert(a1);  //6     
alert(a2);  //2

/*call的用法*/
var a1 = add.call(sub,4,2);

apply : obj1.fun.apply(obj2,[参数1,参数2....]) 让对象1的方法冒充成对象2的方法。

function person (name) {
   this.name=name;//this代表window this那个对象引用,就是那个对象的函数function person()
   this.say=function  () {
     alert(this.name)
   }
}
 
function student () {
 window.person.cell(this)//this代表zhangsan
}
var zhangsan=new student ();
zhangsan.name;//张三
 
function student () {
 window.person.apply(this,["zhangsan"])//参数以数组形式传递
}
 
var zhangsan=new student ();
  zhangsan.name;
  zhangsan.say();

js实现多态

<script type="text/javascript">
	function Animal(name,age){
		this.name=name;
		this.age=age;
		this.shout=function(){
			alert("我是:"+this.name+",今年:"+this.age);
		};
		this.action=function(){
			alert("会吃");
		};
	}
	
	function Dog(name,age){
		Animal.apply(this, [name,age]);
	}
	
	var jack=new Dog("jack",1);
	alert(jack.name);
	alert(jack.age);
	jack.shout();
	jack.action();
</script>

 

 

构造函数call()

call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。

function.call(obj[,arg1[, arg2[, [,.argN]]]]])
  • 调用call的对象必须是个函数function
  • call的第一个参数将会是function改变上下文后指向的对象,如果不传,将会默认是全局对象window
  • 第二个参数开始可以接收任意个参数,这些参数将会作为function的参数传入function
  • 调用call的方法会立即执行

call : obj1.fun.call(obj2,参数1......).让对象1的方法冒充成对象2的方法。

function person () {
   this.name="张三";
   this.say=function  () {
     alert(this.name)
   }
}
 
function student () {
  this.name="李四";
}
var ren=new person ();
var zhangsan=new student ();//也有了say方法
 
ren.say.call(zhangsan)//李四

call的一个作用是可以改变函数运行的作用域

把Person的方法放到Student上执行,原来Student是没有say方法,现在是把Person的say方法放到student上来执行,所以this就指向了student对象

 

构造函数也可以通过子对象向父对象传递参数

function SuperType(name){
        this.name=name
    }
    function SubType(){
        SuperType.call(this,"Tom")//或者apply()
    }
    var instance1=new SubType();
    alert(instance1.name);

 

由于可以改变this的指向,所以也就可以实现对象的继承

function superClass () {
    this.a = 1;
    this.print = function () {
        console.log(this.a);
    }
}

function subClass () {
    superClass.call(this);
    this.print();
}

subClass();
// 1

subClass通过call方法,继承了superClassprint方法和a变量,同时subClass还可以扩展自己的其他方法

的意思就是使用superClass对象代替this对象,那么subClass中不就有superClass的所有属性和方法了吗,subClass对象就能够直接调用superClass的方法以及属性了

 


异同

相同点

都能够改变方法的执行上下文(执行环境),将一个对象的方法交给另一个对象来执行,并且是立即执行.

一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。

   var Pet = {
        words : '...',
        speak : function (say) {
            console.log(say + ''+ this.words)
        }
    }
    Pet.speak('Speak'); // 结果:Speak...

    var Dog = {
        words:'Wang'
    }

    //将this的指向改变成了Dog
    Pet.speak.call(Dog, 'Speak'); //结果: SpeakWang
--------------------- 
    function Pet(words){
        this.words = words;
        this.speak = function () {
            console.log( this.words)
        }
    }
    function Dog(words){
        //Pet.call(this, words); //结果: Wang
       Pet.apply(this, arguments); //结果: Wang
    }
    var dog = new Dog('Wang');
    dog.speak();
--------------------- 

不同点

call方法从第二个参数开始可以接收任意个参数,每个参数会映射到相应位置的func的参数上,可以通过参数名调用,但是如果将所有的参数作为数组传入,它们会作为一个整体映射到func对应的第一个参数上,之后参数都为空

function func (a,b,c) {}

func.call(obj, 1,2,3)
// function接收到的参数实际上是 1,2,3

func.call(obj, [1,2,3])
// function接收到的参数实际上是 [1,2,3],undefined,undefined

apply方法最多只有两个参数,第二个参数接收数组或者类数组,但是都会被转换成类数组传入func中,并且会被映射到func对应的参数上

func.apply(obj, [1,2,3])
// function接收到的参数实际上是 1,2,3

func.apply(obj, {
    0: 1,
    1: 2,
    2: 3,
    length: 3
})
// function接收到的参数实际上是 1,2,3

两个方法该如何选择?

跟简单,根据你要传入的参数来做选择,不需要传参或者只有1个参数的时候,用call,当要传入多个对象时,用apply

或者,如果需要传入的参数已经是一个数组或者类数组了,就用apply,如果还是单独的需要逐个传入的,可以考虑使用call(如果你不嫌麻烦的话 )

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wespten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值