欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

JS继承性总结

最编程 2024-07-27 10:14:32
...
  1. 原型链继承

核心:创建父类实例对象作为子类原型

优点: 可以访问父类原型上的方法或者属性,实现了方法复用

缺点: 创建子类实例时,不能传父级类的参数,子类实例共用了父级构造的属性值

    function Person(name,age,sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    // 学生类型
    function Student(score) {      
      this.score = score;
    }
    // 老师类型
    function Teacher(salary) {
      this.salary = salary;
    }
    // 原型对象,可以将自己的属性和方法继承给将来的实例对象使用
    Student.prototype = new Person("zs",18,"男");
    Student.prototype.constructor = Student;
    // 生成一个实例
    var s1 = new Student(89);
    var s2 = new Student(100);
    console.dir(s1);
    console.dir(s2);
    console.log(s1.name);
    console.log(s1.constructor);

image.png

微信图片_20220613152727.jpg

  1. 构造函数继承 (借用call方法)

核心: 在子构造函数中调用父构造函数

优点: 解决了原型继承的缺点(使用构造函数来继承可以传父类的参数,可以解决子类共享父类构造函数中属性的问题)

缺点:子类无法访问父类原型中的方法和属性(原型链继承可以解决)

function  Parent() {     //构造函数
		this.name = 'parent';
		this.play = [1,2,3];
	}
	Parent.prototype.getName = function(){   //给构造函数增添原型属性
		return this.name;
	}
	function Child() {
		Parent.call(this);   //将this指向Parent
		this.type = 'child';
	}
	let child1 = new Child()   //创建实例
	let child2 = new Child()
	child1.play.push(4);
	console.log(child1);  //[1,2,3,4]
	console.log(child2);	//[1,2,3]   //不是共享的,互不影响
	
	console.log(Child);    //没有问题
	console.log(child.getName());   //会报错,因为他没有继承到Parent自定义添加的原型属性getName

image.png

cd3963a16526c88420d703e7931537f(2) (1).jpg

  1. 组合继承

核心: 使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承

优点:

a. 保留构造函数的优点:创建子类实例,可以向父级构造函数传参数

b. 保留原型链的优点:父类的方法定义在父类的原型对象上,可以实现方法的复用

c.补共享父类的引用属性。比如arr属性

缺点: 调用了两次父类构造函数,会存在多一份的父类实例属性

function Parent() {
		this.name = 'parent';
		this.play = [1,2,3]
	}
	Parent.prototype.getName = function() {
		return this.play
	}
	function Child() {
		//第二次借用Parent()
		Parent.call(this);
		this.type = 'child';
	}
	//第一次借用Parent()
	Child.prototype = new Parent();
	//手动挂上构造器,指向自己的构造函数
	Child.prototype.constructor = Child;
	
	var s1 = new Child();
	var s2 = new Child();
	s1.play.push(4);
	
	console.log(s1,s2);  //互不影响
	console.log(s1.getName());   //[1,2,3,4]
	console.log(s2.getName());   //[1,2,3]

image.png

微信图片_20220613201902(1) (1).jpg  

4.寄生式继承

核心:创建一个仅仅用于封装继承性过程的函数,然后在内部以某种方式增强对象。最后返回对象。

优点: 原型式继承的扩展(其实就是在原型式继承得到对象的基础上,在内部再以某种方式来增加对象,然后返回)

缺点: 依旧没有类的概念

 

跟原型式继承一样,实现的是浅拷贝,共享一个内存空间。

var parent = {
    name: "parent",         
    friends: [1,2,3],       
    getName:function() {            
        return this.name;       
    }   
};  
function cloneContext(original) {
    var clone = Object.create(original);
    clone.getFriends = function () {            
        return this.friends;        
    }       
        return clone;   
}       
    var child = cloneContext(parent);       
    console.log(child); 
    console.log(child.getName());  //parent 
    console.log(child.getFriends());  //[1,2,3]

image (1).png  

 

5.寄生组合式继承

优点: 寄生组合式继承就解决了上述所有问题,被认定是最理想的继承方式

function cloneContext(parent,child) {       
    //这里改用Object.create可以减少组合继承中多进行一次构造的过程      
    child.prototype = Object.create(parent.prototype);      
    child.prototype.constructor = child;    
}       
function Parent() {     
    this.name = 'parent';       
    this.play = [1,2,3];    
}   
    Parent.prototype.getName = function () {        
        return this.name;   
    }       
    function Child() {      
        Parent.call(this);      
        this.friends = 'child'; 
    }       
    cloneContext(Parent,Child);     
    Child.prototype.getFriends = function() {       
        return this.friends;    
    }       
        var person1 = new Child();  
        console.log(person1);   
        console.log(person1.getFriends());  //child 
        console.log(person1.getName());     //parent

image (2).png

微信图片_20220614002055(1).jpg

6.共享原型

缺点:原型之间改变相互影响

//父类
Father.prototype.name="Yezi"
funtion Father(){
  this.age=20
}
//子类
Son.prototype=new Father()
function Son(){}
var son = new Son()

image (3).png

可以继承到name属性,son.name也是"Li",我们输出son.age也可以得到18。

但是Son的构造函数中并没有age属性,该属性是Son.prototype中的。

所以引入下面的圣杯模式:

ab39f4a5141b46e0fa0932e694304d6.jpg  

7.圣杯模式

核心: 我们借用一个空的构造函数与Father共享原型,然后让Son的原型指向Temp的实例

分析:

1. Temp.prototype = Father.prototype,使Temp.prototype和Father.prototype指向同一个内存空间,并不会影响Son.prototype。

2. Son.prototype = new Temp()相当于Son.prototype__proto__ = Temp.prototype,也就是Son.prototype__proto__ = Father.prototype。从而实现了继承。

//父类
Father.prototype.name = "Li"
function Father() {
	this.age = 18
}
//子类
function Son() {
	Father.call(this)
}
//中间构造函数
function Temp() {}
 
Temp.prototype = Father.prototype
Son.prototype = new Temp()
var son = new Son()

f6d58a23be32bf846f3e1d896331743.jpg

优化

但是每次继承都会创建一个新的Temp中间函数,造成了资源浪费.所以我们可以将它封装为一个方法

var inherit = (function () {
	var Temp = function() {}
	return function (Target, Origin) { //target继承自origin
		Temp.prototype = Origin.prototype
		Target.prototype = new Temp()
		Target.prototype.constructor = Target //为了让Target原型的constructor指回Target
	}
})()

二、call、bind、apply

  • 我们可以通过使用bind()、call()、apply()来改变this的指向问题
  • 他们本质上的区别是传入的参数数据类型不同