构造函数 es5 es6中的类与继承 call 和apply

####  构造函数:如果函数中的逻辑是生成一个对象的并将其返回,我们就将其称之为构造函数。
详解!! 也就是普通函数在调用的时候前面前面加new,函数名第一个字母要大写。 即 let name = new Person (参数1,参数2,参数三, )

new 关键字为我们做了什么?

一、 在构造函数中为我们自动生成了一个空对象(不用自己写,默认的)
二、 将本函数中的this指向改变成刚生成的对象
三、 在函数体最后将自动生成的的空对象return 返回出来

	 function Person(name,age,sex){
    
    
	 	this.name = name;
	 	this.age = age;
	 	this.sex = sex;
	 }
	 new Person("王大伟",18,"男")

原型对象prototype(工厂的后院,生成物品公共的部分!) person.prototype.name = “ 王大伟”(生成物品共有的名字叫王大伟)
原型的理解:每一个构造函数都有一个prototype属性,该属性指向了一个对象,这个对象我们叫原型对象。通过构造函数生成的实例化对象,如果在调用属性或方法时从自身找不到该属性或方法,此时不会直接报错或返回undefined,而是去找它的构造函数的原型对象,看它里面有没有该属性或方法,如果有则直接借用,如果没有则看看该原型对象还有没有prototype属性,如果有则重复上面的操作,如果没有则返回undefined或报错。
用途
##   为本地对象扩展原型方法
* 将构造函数中重复的属性或方法从私有属性与私有方法中提取出来,放到构造函数的原型对象中,从而减少内存的开销
JS的继承

	// es5继承 

	function Person(name,sex,age){
    
    
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	Person.prototype.type = "灵长类";


	function SuperMan(name,sex,age,skill){
    
    
		// 继承来自父类Person的所有私有属性
		Person.apply(this,[name,sex,age]);
		this.skill = skill;
	}

	// 继承来自父类Person的所有的原型属性和原型方法
	SuperMan.prototype = new Person();
	
	SuperMan.prototype.fadazhao = function(){
    
    
		alert(this.skill)
	}

	let spiderMan = new SuperMan("蜘蛛侠","男",18,"吐丝儿")

call和apply区别:call和apply都是函数的方法,该方法可以帮助函数调用一次自己,但是和普通调用不同,call和apply的调用可以调整本次执行函数中this的指向,这个指向会被第一个参数代替。如果这个函数在调用数需要传递实参,那么call方法会把实参依次排列在第一的参数之后,而apply则是将实参放到一个数组中,并把数组作为apply方法的第二个参数。call和apply常常被用在构造函数实现继承功能的场景下。
	

	function fn(num1,num2){
    
    
		console.log(this)	
		console.log(num1 + num2)
	}
	// 以下的两种写法是完全等价的。
	fn()
	fn.call()

	// 调用并改变本次函数的this指向
	fn.call(1)
	// 调用并改变本次函数的this指向并传入实参
	fn.call("hello",1,2)
	
	// 调用并改变本次函数的this指向
	fn.apply(1)
	// 调用并改变本次函数的this指向并传入实参
	fn.apply("hello",[1,2])

ES5的类和继承

原理:

一,子级继承父级的的私有属性(不包括父级.prototype所新建的属性)
代码: 父级.apply(this,[参数一,参数二,。。。。]) ( 写在子级构造函数中)
二,子级继承父级的原生属性
代码:子集.prototype = new fuji ()
如果子集也要给原生属性负值,子集.prototype = 新值或新函数


	class Person {
    
    
		constructor(name,age,sex){
    
    
			this.name = name;
			this.age = age;
			this.sex = sex;
		}
		sayName(){
    
    
			alert(this.name)
		}
	}
	let wdw = new Person("王大伟",18,'男')
	

	class SuperMan extends Person{
    
    
		constructor(name,age,sex,skill){
    
    
			// 继承父类的私有属性
			super(name,age,sex)
			this.skill = skill
		}
		dazhao(){
    
    
			alert(this.skill)
		}
	}

	let gangtiexia = new SuperMan("钢铁侠",18,"男","钞能力");

	class Person {
    
    
		constructor(name,age,sex){
    
    
			this.name = name;
			this.age = age;
			this.sex = sex;
		}
		sayName(){
    
    
			alert(this.name)
		}
	}
	let wdw = new Person("王大伟",18,'男')
	

	class SuperMan extends Person{
    
    
		constructor(name,age,sex,skill){
    
    
			// 继承父类的私有属性
			super(name,age,sex)
			this.skill = skill
		}
		dazhao(){
    
    
			alert(this.skill)
		}
	}

	let gangtiexia = new SuperMan("钢铁侠",18,"男","钞能力");

##  构造函数:如果函数中的逻辑是生成一个对象的并将其返回,我们就将其称之为构造函数。
function Person(name,age,sex){
    
    
		// 需要传递参数的值是可变的属性
		// 私有属性
		this.name = name;  
		this.age = age;
		this.sex = sex;
	}
	
	
	// 构造函数的原型对象
	// 原型属性和原型方法
	Person.prototype.type = "灵长类";
	Person.prototype.sayName = function(){
    
    
		alert(this.name) 
	}
	// 实例化
	let wdw = new Person("王大伟",18,"男");  
	let hhl = new Person("何恒磊",17,"男");
	let sy = new Person("石钰",16,"女");

		* 实例化对象.__proto__ 返回该实例化对象的构造函数的原型对象
	* 实例化对象.constructor 返回该实例化对象的构造函数的引用  (产品到工厂)
	* 构造函数.prototype 返回该构造函数的原型对象


## #### 原型prototype的使用场景:
* 用途	
	1. 把构造函数的公共的属性和方法从私有属性中提取到原型对象中,以达到节省内存的目的.
	2. 本地对象的扩展.

* 原型的理解:
每一个构造函数都有一个prototype属性,该属性指向了一个对象,这个对象我们叫原型对象。通过构造函数生成的实例化对象,如果在调用属性或方法时从自身找不到该属性或方法,此时不会直接报错或返回undefined,而是去找它的构造函数的原型对象,看它里面有没有该属性或方法,如果有则直接借用,如果没有则看看该原型对象还有没有prototype属性,如果有则重复上面的操作,如果没有则返回undefined或报错。

```javascript
//为字符串对象扩展了全局(原型)方法
	String.prototype.reverse = function(){
		var result = ""
		for(var i = 0; i < this.length;i++){
			result += this[this.length - 1 - i]
		}
		return result;
	}

###  js的继承	
// es5继承 

	function Person(name,sex,age){
    
    
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	Person.prototype.type = "灵长类";


	function SuperMan(name,sex,age,skill){
    
    
		// 继承来自父类Person的所有私有属性
		Person.apply(this,[name,sex,age]);
		this.skill = skill;
	}

	// 继承来自父类Person的所有的原型属性和原型方法
	SuperMan.prototype = new Person();
	
	SuperMan.prototype.fadazhao = function(){
    
    
		alert(this.skill)
	}

  let spiderMan = new SuperMan("蜘蛛侠","男",18,"吐丝儿")
  
  • call和apply区别:

call和apply都是函数的方法,该方法可以帮助函数调用一次自己,但是和普通调用不同,call和apply的调用可以调整本次执行函数中this的指向,这个指向会被第一个参数代替。
如果这个函数在调用数需要传递实参,那么call方法会把实参依次排列在第一的参数之后,而apply则是将实参放到一个数组中,并把数组作为apply方法的第二个参数。call和apply常常被用在构造函数实现继承功能的场景下。

	function fn(num1,num2){
    
    
		console.log(this)	
		console.log(num1 + num2)
	}
	// 以下的两种写法是完全等价的。
	fn()
	fn.call()

	// 调用并改变本次函数的this指向
	fn.call(1)
	// 调用并改变本次函数的this指向并传入实参
	fn.call("hello",1,2)
	
	// 调用并改变本次函数的this指向
	fn.apply(1)
	// 调用并改变本次函数的this指向并传入实参
  fn.apply("hello",[1,2])
  

##  es6的类与继承

```javascript
### ES6的类和继承

```js
	class Person {
		constructor(name,age,sex){
			this.name = name;
			this.age = age;
			this.sex = sex;
		}
		sayName(){
			alert(this.name)
		}
	}
	let wdw = new Person("王大伟",18,'男')
	

	class SuperMan extends Person{
		constructor(name,age,sex,skill){
			// 继承父类的私有属性
			super(name,age,sex)
			this.skill = skill
		}
		dazhao(){
			alert(this.skill)
		}
	}

	let gangtiexia = new SuperMan("钢铁侠",18,"男","钞能力");

	class Person {
		constructor(name,age,sex){
			this.name = name;
			this.age = age;
			this.sex = sex;
		}
		sayName(){
			alert(this.name)
		}
	}
	let wdw = new Person("王大伟",18,'男')
	

	class SuperMan extends Person{
		constructor(name,age,sex,skill){
			// 继承父类的私有属性
			super(name,age,sex)
			this.skill = skill
		}
		dazhao(){
			alert(this.skill)
		}
	}

  let gangtiexia = new SuperMan("钢铁侠",18,"男","钞能力");

总结!!!

1. 使用对象冒充:
在这里插入图片描述---
+ 理解:
在本构造函数中将其他构造函数视为本类的一个属性,并执行,从而实现了对超类的拷贝
+ 优点:
简单直接,思路清晰,可以实现多重继承,体现了类似Java等语言面向对象的原理
+ 缺点:
由于将其他构造函数的属性和方法嵌套到本类来,难免会发生属性和方法冲突,由于程序是自上到下运行的,先定义的同名的属性和方法会被覆盖掉。

2. 使用es6 class关键字实现
在这里插入图片描述---
+ 理解:
使用class / expends / super 关键字模拟Java等语言面向对象的原理,实现继承,本质上是对原型继承的封装与改写,形式更加纯正。
+ 优点:
形式体现了面向对象的风格,易于理解
+ 缺点:
只能在es6中使用,配合js其他语法(如对象扩展符,原型方法等)容易产生混淆和误解

3. 构造函数绑定
在这里插入图片描述---
+ 理解:
改变超类的执行环境,在基类实例的运行环境中运行,使得基类实例拥有超类构造函数的所有方法和属性。
+ 优点:
代码简洁,可以在call或apply方法中传参,获取超类非预定义属性(有传入变量决定属性)
+ 缺点:
基类无法继承超类原型上的方法,属性和方法继承会出现覆盖问题。

4. 原型继承
在这里插入图片描述---
+ 理解:
改变基类的原型,指向超类的实例,进而实现继承。
+ 优点:
各类js继承的核心和晕头,是js语言的灵魂之处;基类可以获得超类原型上的属性和方法;可以向超类原型中传参。
+ 缺点:
理解难度高;无法实现多重继承(基类prototype只能绑定为一个超类的实例);注意属性和方法的覆盖问题;原型继承将会因为原型链上溯(还有一个类继承了基类)而出现混淆问题。

## 代码如下!!!
<script>
		//  es5中构造函数的写法
		function Parent(name, age, food) {
    
    
			this.name = name
			this.age = age
			this.food = food
		}
		Parent.prototype.eat = function () {
    
    
			alert(this.name + '在吃' + this.food)
		}
		let ren = new Parent("suke", 18, '包子')
		console.log(ren);
		console.log(ren.eat());

		// es5中继承的写法
		function Song(name, age, food,sex,sing){
    
    
			// 通过apply改变this指向,将子集的参数传到数组中。
			Parent.apply(this,[name, age, food])
			// 给子集新增的参数负值
			this.sex = sex
			this.sing=sing 
		}
		// 将父级的原生函数即共有的,负值给子级
		Song.prototype = new Parent()
		Song.prototype.sing= function(){
    
    
			alert(this.name +'唱'+ this.Son)
		}
		// new子级函数
		let erzi = new Song("suke", 18,'包子','女','我爱你')
		console.log(erzi);








		// es6中构造函数的写法
		class Parent {
    
    
			constructor(name, age, food) {
    
    
				this.name = name
				this.age = age
				this.food = food
			}
			eat(){
    
    	
				alert(this.name + '在吃' + this.food)
			}
		}
		let ren = new Parent("suke", 18, '包子')

		console.log(ren);
		console.log(ren.eat());

		// es6 继承!!
		class Erzi extends Parent {
    
    
			constructor(name,age,food,sex,daima){
    
    
				super(name,age,food)
				this.sex = sex
				this.daima = daima
			}
			xiedaima(){
    
    
				alert(this.name+'吃'+ this.sex +'然后去'+this.daima)
			}
		}
		let erzi = new Erzi('suke',12,'nan','包子','写代码')
		console.log(erzi);
		console.log(erzi.eat());



	</script>

猜你喜欢

转载自blog.csdn.net/m0_54625720/article/details/112752073
今日推荐