SOURCE

/**
 *  在es6之前没有提供extend继承,所以我们要通过 构造函数 + 原型对象 模拟实现继承,被称为 组合继承
 *  
 *  1. call() 
 *      调用这个函数,可以修改函数运行的 this 指向
 *          fun.call(thisArg, arg1, arg2, ...);
 *              thisArg: 当前调用函数this的指向对象
 *              arg1, arg2: 传递的其他参数
 */

//  普通调用
    function fn1() {
        console.log(this); // 这里的this指向函数的调用者 也就是指向的是 window 对象
        console.log('hello');
    }
    fn1(); // 调用这个函数 

// call方法 call()调用函数
    function fn2() {
        console.log('hello ^_^');
    }
    fn2.call();
    // call() 调用函数 并且改变函数的this的指向
        function fn3(x, y) {
            console.log(this,'this的指向'); // {name: "c"}
            console.log(x+y)
        }
        var c = {
            name: 'c'
        }
        // 改变函数fn3 的this的指向 到c这个对象中;
        fn3.call(c, 1, 2); // 3


/**
 * 2.借用构造函数 继承父类型的属性
 *  核心原理: 通过call()把父类型的this指向到子类型的this,实现子类型继承父类型的属性
 */
    //  父构造函数
    function Father(name, age) {
        // this 指向 父构造函数的对象实例
        this.name = name;
        this.age = age;
        console.log(this);
        /**
         * Son {name: "DD", age: 19}
            age: 19
            name: "DD"
         */
    }
    // 子构造函数
    function Son(name, age) {
        // this 指向子构造函数的对象实例
        // 子构造函数 在 调用父构造函数的属性的时候 一定要记得把父构造函数里面的this 修改为 子构造函数里面的this
        // 调用了父构造函数,然后利用 call() 把父构造函数里面的this指向改为 子构造函数的this;然后就能实现子构造函数继承了父构造函数的属性
        Father.call(this, name, age); 
        this.sex = 'men';
        console.log(this);
        /**
         * 输出结果:
         *  Son {name: "DD", age: 19, sex: "men"}
                age: 19
                name: "DD"
                sex: "men"
         */
    }
    var s = new Son('DD', 19);

/**
 * 3.借用构造函数 继承父类型的方法
 *  利用原型指向 
 */
    // 父构造函数
    function F(name, age) {
        this.name = name;
        this.age = age;
    }
    F.prototype.sing = function() {
        console.log('唱歌')
    }
    // 子构造函数
    function S(name, age) {
        F.call(this, name, age);
    }
    /**
     * 继承父构造函数的方法 的错误写法
     *   直接将子构造函数的原型对象prototype 指向 父构造函数的原型对象
            S.prototype = F.prototype;
     *      
     *   错误:
     *      父构造函数 F =====> F构造函数原型对象 (F.prototype)
     *      子构造函数 S =====> S构造函数原型对象 (S.prototype)
     *      我们把父构造函数里面的原型对象,给了子构造函数里面的原型对象
     *      相当于把子构造函数原型对象 指向了 父构造函数的原型对象 (地址赋值了)
     *      然后子构造函数发生了变化 父构造函数也会发生变化
     */
    // 正确写法
    /**
     *  new F()
     *       创建了一个Fr的实例对象(与F的原型对象不是同一个对象)
     *  S.prototype = new F()
     *       让 S 的原型对象 指向了 F 的实例对象(因为F的实例对象有个__proto__原型根据原型链 去访问F 的原型对象)
     *       F实例对象 和 F的原型对象 地址是不一样的 
     */
    S.prototype = new F(); 
    S.prototype.dance = function() {
        console.log('跳舞')
    }
    var ss = new S('柒柒', 3);
    console.log(ss)

console 命令行工具 X clear

                    
>
console