#### 构造函数:如果函数中的逻辑是生成一个对象的并将其返回,我们就将其称之为构造函数。
详解!! 也就是普通函数在调用的时候前面前面加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>