JS--Day24(继承(bind/apply/call)

一.回顾柯里化

①柯里化的意思:把多个参数的函数变成  单个,部分参数  的函数。

②柯里化函数(工具)的实现思路:

     1)、专门定义一个数组,保存原函数的参数

     2)、当原函数的参数(数量)不够时,返回一个函数(收集原函数参数的)

     3)、当原函数的参数(数量)够了,那么,调用原原函数的调用。

③代码:

 // 1、原函数:
    function sum(a, b, c, d) {
        return a + b + c + d;
    }
 // 2、柯里化工具函数
    function curry(cb) {
        let arr = [];
        function fn(...args) {
            arr = [...arr, ...args];//收集参数
            if (arr.length < cb.length) {// 函数.length 表示的是函数形参的个数
                return fn;
            } else {
                return cb(...arr);
            }
        }
        return fn;
    }
    // 3、(调用柯里化工具函数)把原函数柯里化
    let cSum = curry(sum);
    // 4、调用柯里化后的函数。
    console.log(cSum(2, 3)(5)(6))
    // console.log(cSum(2)(4)(5)(6))
    // console.log(cSum(2, 4, 5)(6))

④柯里化的作用:可以复用函数的参数值。
   

 let f01 = cSum(2, 3)(5);// f01是持有 2,3,5的函数
     f01(6);//求 2+3+5 + 6
     f01(7);//求 2+3+5 +7

二.补充:定义类

①ES5中

// 1)、用构造函数:这种写法,会导致内存中定义了多个相同的函数。浪费了内存
 function Doctor(name,dept){
     this.name = name;
     this.dept = dept;
     // 这种写法,会导致内存中定义了多个相同的函数。浪费了内存。
     this.seatZhen = function(){
         console.log(this.name+"在坐诊………………");
     }
 }
 let d1= new Doctor("宋和叶","神经科");
 let d2 = new Doctor("纪朝锋","精神科");
// 2)、用构造函数结合原型(prototype)定义类
 function Doctor(name,dept){
     this.name = name;
     this.dept = dept;
 }
// 一个构造函数的原型属性上的属性和方法,可以被它的实例共享
 Doctor.prototype.seatZhen =  function(){
     console.log(this.name+"在坐诊………………");
 }
 let d1= new Doctor("宋和叶","神经科");
 let d2 = new Doctor("纪朝锋","精神科");
 d1.seatZhen();
 d2.seatZhen();

②ES6中

//ES6中用class:class定义类,是个语法糖。
class Doctor{
    constructor(name,dept){
        this.name = name;
        this.dept = dept;
    }
    seatZhen(){
        console.log(this.name+"在坐诊………………");
    }
}
let d1= new Doctor("宋和叶","神经科");
let d2 = new Doctor("纪朝锋","精神科");
d1.seatZhen();
d2.seatZhen();

三.原型(链)继承

// 原型(链)继承,必须用ES5的写法。
// 1、父类
    function Person(name, sex) {
        this.name = name;
        this.sex = sex;
    }
    Person.prototype.eat = function () {
    }
// 2、子类1
    function Doctor(dept) {
        this.dept = dept;
    }
    // 建立继承关系(原型的方式):子类(构造函数)的原型属性 指向 父类的对象(实例)
    Doctor.prototype = new Person("宋和叶", "女");
    let d1 = new Doctor("神经科");
    console.log(d1.name);
    console.log(d1.eat);
    let d1 = new Doctor("神经科");
// 3、子类2
    function Programmer(lanuage){
     this.lanuage = lanuage;
    }
    Programmer.prototype = new Person("纪朝锋","男");
    let p1 = new Programmer("javascript");
    p1.name;
    p1.eat();

四.原型(链)继承的注意点

①先完成继承关系,再增加子类特有的属性和方法。

②在写子类特有属性和方法时,不能重新prototype(不能再直接给prototype赋值为json对象)

// 1、父类
function Person(name,sex,books){
    this.name = name;
    this.sex = sex;
    this.books = books;
}
Person.prototype.eat = function(str){
    console.log(this.name+"在吃"+str+",天在看…………");
}
// 2、子类1
function Doctor(dept){
    this.dept = dept;
}
// 建立继承关系(原型的方式):子类(构造函数)的原型属性 指向 父类的对象(实例)
Doctor.prototype = new Person("宋和叶","女",[]);
Doctor.prototype.seatZhen = function(){
    console.log(this.name+"在坐诊……");
}
// 不能重新prototype属性
// Doctor.prototype = {
//     seatZhen:function(){
//         console.log(this.name+"在坐诊……");
//     }
// }
let d1 = new Doctor("神经科");
d1.name = "纪朝锋";
let d2 = new Doctor("神经科");
d2.name="党艺华";
d1.eat("油条");
d1.seatZhen();
d2.eat("包子");

五.call和apply函数

①call和apply:

1、call和apply都是函数的一个方法(函数)

2、call和apply都是在调用原函数。

3、call和apply是可以改变原函数里的this指向的

4、call和apply的第一个参数就是原函数的this指向

5、call可以把函数和对象解耦(耦合度:依赖程度/关联程度。高内聚,低耦合)

6、call和apply的区别(仅仅只是格式上的区别):

      1)、call函数从第二个参数朝后的参数是原函数的参数

      2)、apply函数只有两个参数,第二个参数是个数组,数组里是原函数的参数。这样的话,                   apply第二个参数就可以使用arguments

②示例代码

    let person = {
        name: "张三疯",
        eat: function () {
            console.log(this.name + "在吃");
        }
    }
    person.eat();
    let dog = {
        name: "小黑"
    }
    person.eat.call(dog);
    function eat(str) {
        console.log(this.name + "在吃" + str);
    }
    eat("食物");
    let dog = {
        name: "小黑"
    }
    eat.call(dog, "油泼面");//等价于:dog.eat("油泼面");
    let doctor = {
        name: "张三疯"
    }
    eat.call(doctor, "方便面");//等价于:doctor.eat("方便面");
    eat.apply(doctor, ["油条"])
    3、call和apply的区别
    eat.call(doctor, "方便面");//等价于:doctor.eat("方便面");
    eat.apply(doctor, ["油条"])//等价于:doctor.eat("油条");
    function eat(str, song) {
        console.log(this.name + "吃着" + str + ",唱着" + song);
    }
    let dog = {
        name: "小黑"
    }
    function fn01() {
        console.log("arguments", arguments);
        eat.apply(dog, arguments); 
//  arguments也是函数的内置对象,该对象是个伪数组(不是数组,
//  但是可以使用数组的length,下标),里面存储着函数的实参
    }
    fn01("火锅", "最炫民族风");

六.call、apply、bind函数

①bind和call、apply的区别:

相同点: bind,call,apply都可以改变this的指向。

不同点:1、bind不会调用原函数,而会产生一个新的函数(bind的返回值是新函数),新函数里的                 this是bind时的对象。bind有强烈绑定的意思。只要调用bind,那么对象和函数就永远                     绑定起来了。 2、call和apply会调用原函数。  

②示例:

  function eat(str){
      console.log(this.name+"在吃"+str);
  } 
  let dog = {
    name:"小黑"
  }
  let pig = {
      name:"小白"
  }
// 1)、bind
  let fn01 = eat.bind(dog);//fn01 这个函数,就是dog和eat绑定在一起的函数。只要调用fn01,this必 
  然是dog
  fn01("油泼面");
  fn01("狗粮");
  let fn02 = eat.bind(pig);//fn02 这个函数,就是pig和eat绑定在一起的函数。只要调用fn02,this必 
 然是pig
  fn02("玉米");
  fn02("米饭");
// 2)、call
  eat.call(dog,"油泼面");// 如果希望this是dog,那就call dog
  eat.call(dog,"狗粮");
  eat.call(pig,"玉米");// 如果希望this是pig,那就call pig
  eat.call(pig,"米饭");

七.call和apply的继承

①在子类构造函数里,用call和apply改变父类构造函数里的this(子类对象)。

②缺点:没法继承父类原型属性上内容(属性和方法)

function Person(name,sex){
    this.name = name;
    this.sex = sex;
}
// Person.prototype.eat = function(str){
//     console.log(this.name+"在吃"+str);
// }
function Doctor(name,sex,dept){
    // apply的继承。
    let obj = this;
    Person.apply(obj,arguments);//没有把所谓的构造函数(Person)当构造函数使用(就当一个普通函数);等价于: this.Person(arguments);
    this.dept = dept;
}
let d1 =new Doctor("宋和叶","女","儿科");
let d2 = new Doctor("纪朝锋","男","骨科");
console.log(d1.name);
console.log(d2.name);
d1.eat("油泼面");

八.组合继承

把原型(链)继承和call,apply继承结合起来。各自发挥自己的优势。

①原型继承:把父类原型属性上内容(属性和方法)继承下来。

②call和apply继承:把父类构造函数里的属性和方法继承下来。

function Person(name,sex){
    if(arguments.length>0){
        this.name = name;
        this.sex = sex;
    }
}
Person.prototype.eat = function(str){
    console.log(this.name+"在吃"+str);
}
function Doctor(name,sex,dept){
    // call和apply继承:把父类构造函数里的属性和方法继承下来。
    Person.apply(this,arguments);
    this.dept = dept;
}
// 原型继承:把父类原型属性上内容(属性和方法)继承下来。
Doctor.prototype = new Person();
let d1 =new Doctor("宋和叶","女","儿科");
let d2 = new Doctor("纪朝锋","男","骨科");
console.log(d1.name);
console.log(d2.name);
d1.eat("油泼面");

九.ES6的继承

 ①继承关系的关键字:extends;

 ②子类构造函数里必须调用super(),而且必须写在子类构造函数的第一句话

 ③ES6的这种写法就是个语法糖(换了写法,本质一样)

class Person{
    constructor(name,sex){
        this.name = name;
        this.sex = sex;
    }
    eat(str){
        console.log(this.name+"在吃"+str);
    }
}
// extends关键字完成继承关系
class Doctor extends Person{
    constructor(name,sex,dept){
        // super就是父类
        super(name,sex);//这就相当于调用了父类的构造函数。这句话必须放在子类构造函数里的第一句话
        this.dept = dept;
    }
}
let d1 =new Doctor("宋和叶","女","儿科");
let d2 = new Doctor("纪朝锋","男","骨科");
console.log(d1.name);
console.log(d2.name);
d1.eat("油泼面");

十.(扩展)验证ES6的语法糖

 function Person(name, sex) {
        if (arguments.length > 0) {
            this.name = name;
            this.sex = sex;
            // this.eat = function(str){
            //     console.log(this.name+"在吃"+str);
            // }
        }
    }
    Person.prototype.eat = function (str) {
        console.log(this.name + "在吃" + str);
    }
    function Doctor(name, sex, dept) {
        Person.apply(this, arguments);
        this.dept = dept;
    }
    // Doctor.prototype = new Person();
    let d1 = new Doctor("宋和叶", "女", "儿科");
    let d2 = new Doctor("纪朝锋", "男", "骨科");
    console.log(d1.eat === d2.eat);//如果把eat方法写在构造函数里,那么是false(因为,每次调用构造函数,都会重新定义函数) )
    console.log(d1.eat === d2.eat);//如果把eat方法写在原型属性上,那么是true

十一.(扩展)看看官方类的继承关系

①js语言本身的类 的继承关系

  console.dir(Array);
//继承关系: Array ---> Function --> Object  
  let arr = new Array(10);      
  console.dir(arr);
  console.log(arr.__proto__ == Array.prototype);

②dom对象所属的类及其继承关系

  let box =  document.getElementById("box"); //box对象是 类 HTMLDivElement 的实例
  console.dir(box);
  div是 HTMLDivElement的对象,即就是: div属于 HTMLDivElement 类型。
  继承关系: HTMLDivElement--> HTMLElement-->Element-->Node-->EventTarget-->Object
  console.log(box.constructor===HTMLDivElement);
  let t = document.createElement("div");
  let t2 = new HTMLDivElement();//不行, 因为 HTMLDivElement,不是真正的构造函数,实际是个接口

01.原型继承的示意图

02.call和apply继承的示意图

 

猜你喜欢

转载自blog.csdn.net/weixin_72756818/article/details/129861822