js高阶基础知识

闭包:

一个函数的返回值是另一个子函数 用完之后本该销毁掉 由于外部调用了返回的子函数 而且子函数中又用到了函数的局部变量 导致函数的局部变量无法被销毁 所以形成了一个新的封闭空间 这个封闭空间我们叫闭包

IIFE立即执行函数:

六种方式:

<script>
    // 我们的函数 你声明之后  不会立即调用 你声明完就是声明完 并没有执行 需要的话手动调用
    function f1() {
        console.log("f1");
    }
    f1();
    // 第一种IIFE
    (function f2() {
        console.log("f2");
    })();
    //第二种
    (function f3() {
        console.log("f3");
    }());
    //第三种
    + function f4() {
        console.log("f4");
    }();
    //第四种
    - function f5() {
        console.log("f5");
    }();
    //第五种
    ! function f6() {
        console.log("f6");
    }()
    //第六种
    ;(function f7() {
        console.log("f7");
    })()

    // 对象是如何声明的?
    let obj={
        name:"wc",
        age:15
    }
    console.log(obj.name);
    console.log(obj.age);
    console.log(obj);
</script>

this

this介绍

this
在不同的场合下 this代表的意义不同
1.this出现在普通函数中的时候 this代表的是window
2.this如果出现在事件中 那么this指的就是事件源
3.对象中方法里面的this 指的是调用这个方法的对象

this 练习

<!-- <script>
    // this出现在普通函数中
    function fn(){
        console.log(this);
    }
    // 这里的this 代表的是window
    fn();
    // 我说这里的fn 是不是window上的fn
    window.fn();
    // this指的是window
    // 谁调用了fn  this指的就是谁
</script> -->
<!-- <script>
    let btn=document.getElementById("btn");
    btn.onclick=function(){
        console.log(this);
    }
    // 再事件中 this指的是 事件源
    // this指的是不是调用事件函数点前面的?实际上是浏览器帮我们调用了 但是点前面是事件源 也是我们this指向的
</script> -->
<!-- <script>
    let obj={
        name:"wangcai",
        age:"15",
        // 在对象中定义方法
        run:function(){
            console.log(this);
            console.log("run...");
            //在这里 this 表示的是当前对象
            // 但是有一点  就是说 你调用方法之前 你永远不知道这个this指的是谁
            // 在这里 this代表谁 就看谁调用了这个方法
        }
    }
    obj.run();
    //在这里 obj调用了 所以 这里的this指的就是obj  打点调用
</script> -->
<!-- <script>
    ;(function(){
        console.log(this);
    }())
</script> -->
<!-- <script>
    let btn=document.getElementById("btn")
    console.log(btn);
    function f(){
        console.log(this);
    }
    btn.onclick=f();
</script> -->
<!-- <script>
     let btn=document.getElementById("btn");
     function f(){
         return function(){
             console.log(this);
         }
     }
     btn.onclick=f();
    // btn.onclick=function(){
    //          console.log(this);
    //      }
</script> -->
<!-- <script>
    function qq(){
        console.log(this);
    }
    window.qq();
    qq();
</script> -->
<!-- <script>
    let wc={
        name:"Wangcai",
        age:"15",
        eat:function(){
            console.log(this);
        }
    }
    wc.eat();
</script> -->
<!-- <script>
    let wc={
        name:"Wangcai",
        age:"15",
        eat:function(){
            console.log(this);
        }
    }
    let mm=wc.eat;
    mm();
</script> -->
<script>
    var num=10;//GO里面 也就是window上的  10-60-65
    var obj={
        num:20
    };
    obj.fn=(function(num){//函数的立即调用  传入obj.num 也就是20
        this.num=num*3; //num*3=60  把60赋给了谁?就是一个很普通的立即执行函数  那么这里的this
        // 指的是 window 所以这里是将 60赋值给了 window上面的num
        num++;//这个num是指的我们传进来的21
        console.log(num);//21
        return function(n){
            this.num +=n;//this还是指的是window  60+5  65
            num++;//num形成了一个闭包 22
            console.log(num);//22
        }
    })(obj.num);
    var fn=obj.fn;
    fn(5)
    console.log(window.num);//65
</script>

对象

对对象的理解

对象实际上就是我们说的该类的具体的事物,也就是具体的对象,是真实存在的 万物皆对象
对象是属性的无序集合
new 出来的一定是对象

创建对象的方式

 // 创建一个对象的两种方式
    // 第一种方式,我们通过字面量创造对象  
    let obj1={
        name:"wangcai",
        age:"100"
    }
    // 第二种方式,通过new的形式创建一个对象
    // obj2是对象  Object是构造器
    let obj2=new Object();
    obj2.name="xiaoqiang";
    obj2.age="99"

原型对象和原型链

<!-- <script>
    let arr1=new Array("a","b","c");
    console.log(arr1);
    console.log(arr1.__proto__);//这是数组构造器对应的原型对象 也是我们arr1的原型对象
    console.log(arr1.__proto__.constructor);
    // 你如何证明 构造器的prototype和你的__proto__指向的都是原型对象
    console.log(Array.prototype);
    // 你也可以证明 我们的arr1.__proto__.constructor 就是我们数组构造器
    console.log(arr1.__proto__.constructor ==Array);
</script> -->
<!-- 以上是系统自带的构造器 -->
<!-- 那么我们自定义的行不行? -->
<script>
    function Person(name,age){
        this.name=name;
        this.age=age;
        this.say=function(){
            console.log("123");
        }
        // 这里写的方法 是不会挂载到你的原型对象上的 
        // 但是你对象上能用这个方法吗?
        // 可以 相当于是继承过去的
    }
    let p1=new Person("zhangsan","18");
    console.log(p1);//如果你在对象上找一个属性或者方法 找不到的时候 会沿着__proto__往上找
    console.log(p1.__proto__);//这里应该是我实例化对象p1中__proto__指向的原型对象
    console.log(Person.prototype);//这里指的和上面的应该一致 不过这里是从构造器找过去的
    console.log(p1.__proto__.constructor);//这里会找到构造器

    // 我给你演示一下 方法的寻找过程
    // p1上有一个say方法
    // p1.jump();
    // 第一种
    // p1.__proto__.jump=function(){
    //     console.log("我是原型对象上的jump方法");
    // }
    // p1.jump()
    // 第二种
    // Person.prototype.jump=function(){
    //     console.log("我是原型对象上的jump方法");
    // }
    // p1.jump();

    // 在原型对象上添加方法 当你的对象在自己本身找不到的时候 就会沿着__proto__网上找
    // 这个链式的寻找过程 就叫原型链
</script>

构造器

对构造器的理解

构造器是什么?构造器实际上就是我们类的另一种说法,我们创造对象是通过new 后面加上构造器创造的
原型对象 那么什么是原型对象,按我的理解,原型对象就好像一个模具,或者说我们游戏里面的母体
对象的生成都是按照原型对象的样子去创造的 他们都有原型对象的影子
一个构造器对应一个原型对象 说白了 构造器和原型对象 就更像一个对象的父母一样
单独的有父不行 单独的有母也不行

JS中内置的类 说白了 就是内置的构造器
1)先有类 然后通过new这个类(构造器)就可以创建一个对象 就好比你得先有第一代 然后才能产生第二代
2)Number叫类 数字类 也叫构造器 在JS中我们叫构造器 在Java中我们叫类
3)new出来的一定是对象 存储在堆空间中
4)我们这里的a的值是一个地址 这个地址指向那个堆
5)我们这里不严谨的说 a是一个对象 但是如果你特别严谨的话 a不能称之为对象 只能说他指向了对象
6)对象是属性的无序集合

构造器的种类

 console.log("内置构造器一:Number");
    let a=new Number(10);
    console.dir(a);
    console.log(typeof a);
    console.log("内置构造器二:String");
    let b=new String("hello")
    console.dir(b)
    console.log(typeof b)
    console.log("内置构造器三:Boolean");
    let c=new Boolean(true);
    console.dir(c)
    console.log(typeof c)
    console.log("内置构造器四:Array");
    let d=new Array(4);
    console.dir(d)
    console.log(d instanceof Object) //true表示他是对象
    console.log("内置构造器五:Object");
    let e=new Object();
    e.name="wangcai";
    console.dir(e);

判断数据类型的方式

判断数据类型的方式

通过对象上的方法来判断

<!-- <script>
    // 我们说 有一个方法 叫toString  
    // 这个方法的作用是将调用者转成字符串
    // 这个方法是在哪?
    let a=10;
    console.log(a.toString());;
    // a本身上面明显没有toString这个方法
    // 那么他势必会沿着__proto__往上找
    // 他找到的 是Number构造器所对应的原型对象

    console.log(Number.prototype);//这上面有toString
    // 其他构造器上是否也都有toString
    console.log(Array.prototype);
    console.log(Function.toString);
    console.log(String.prototype);
    console.log(Boolean.prototype);
</script> -->

<!-- <script>
    // 我要通过toString去判断
    let obj={name:"wc"};
    console.log(obj.toString());//[object Object]

    let arr=[1,2,3];
    console.log(arr.toString());//1,2,3
    console.log(typeof arr.toString());//1,2,3
    // 数组打点调用toString 和 对象打点调用toString结果是不一样的
    // 数组的结果还是和数组元素一模一样的字符串  对象的结果是[object Object]

    function fn(){}
    console.log(fn.toString());//function fn(){}
    console.log(typeof fn.toString());
    //函数打点toString 结果是和函数一模一样的字符串

    let a=10;
    console.log(a.toString());//字符串10
    // 数字打点调用toString 结果就相当于给其加了一个引号 让他变成字符串

    let str="hello";
    console.log(str.toString());
</script> -->

<script>
    // 还有一种 也是通过toString  不过这里我们通过的是Object原型对象上的toString
    let a=[1,2];
    let b;
    function fn(){}
    let date=new Date()
    // 我们既然想借用Object原型对象上的toString 那么肯定需要去使用call
    console.log(Object.prototype.toString.call(a));//[object Array]
    console.log(Object.prototype.toString.call("hello"));//[object String]
    console.log(Object.prototype.toString.call(123));//[object Number]
    console.log(Object.prototype.toString.call(true));//[object Boolean]
    console.log(Object.prototype.toString.call(fn));//[object Function]
    console.log(Object.prototype.toString.call(date));//[object Date]
    console.log(Object.prototype.toString.call(b));//[object Undefined]
    console.log(Object.prototype.toString.call(null));//[object Null]


    // 上面看的还不过瘾 我可以自己封装一个函数
    function getType(data){
        let str=Object.prototype.toString.call(data);
        // 上面得到的是[object Null]这种类型的  我只想要后面那几个
        // 现在我想把前面的8个字符切掉
        // 用slice方法
        // 我之前是不是说过 字符串也可以看成数组
        return str.slice(8,length-1);
    }
    // 我这个函数的作用就是判断数据类型 ,返回值即数据类型
    console.log(getType(1));
    console.log(getType(null));
    console.log(getType([1,2,3]));
    console.log(getType(fn));
</script>

typeof

<!-- typeof 判断数据类型的 -->
<!-- 
    我们使用typeof判断数据类型(除了null)的时候
        优点:
            简单
            对基本数据类型和函数测试的非常准确
        缺点:
            对引用数据类型 测试都是Object
 -->
 <script>
     console.log(typeof 110);//number
     console.log(typeof "hello");//string
     console.log(typeof true);//boolean
     console.log(typeof a);//undefined
     function fn(){}
     console.log(typeof fn);//function
     console.log("----------------");
     console.log(typeof [1,2,3]);//object
     console.log(typeof {name:"Wc"});//object
     let d =new Date();
     console.log(typeof d);//object
     console.log(typeof null);//object

     
 </script>

typeof instanceof Array

   // 如何判定一个对象是否属于某个类
    console.log(a instanceof Number);//true
    console.log(a instanceof String);//flase 

    // instanceof还有一个什么用呢? 用于判定我一个变量是数组还是对象
    // instanceof可以用于判定一个变量 是不是对象
    // 但是没法判定一个变量是不是数组
    let test=[1,2,3]
    let testO={

    }
    console.log(typeof test);
    console.log(typeof testO);
    console.log(test instanceof Array);
    console.log(test instanceof Object);//数组 也是对象
    console.log(testO instanceof Object);//对象 不是数组
    console.log(testO instanceof Array);
    //我如何判定一个变量是不是数组?
    console.log(Array.isArray(test));//如果结果为true test就是数组
    console.log(Array.isArray(testO));//这里结果为false 所以说他不是数组

属性

属性了解

// 对象 是属性的无序集合
// 属性 属性是用来描述对象的 比如 姓名 年龄 体重 身高
// 其实你也可以把方法理解为属性 不过 方法是动词 能做的事情3
// 为什么我们也可以把方法理解为属性
// 属性名:属性值 这一整条 叫属性
// 属性和属性之间用逗号隔开
// 方法 无非就是一种特殊的属性 因为其属性值为 函数

// 对象中的键 如何命名?
// 什么是键? 键就是属性名
// 实际上 键可以用其他类型来定义

访问属性的方式

   let xx="sex";
    let obj={
        name:"旺财",
        age:"18",
        xx:"man",
        dogCall: function(){
            console.log("wangwangwang");
        }   
    }
    // 我们去访问对象上的属性的时候 直接打点就可以访问
    console.log(obj.name);
    console.log(obj.age);
    // 还有一种方法访问 通过[]的方式访问
    console.log(obj["name"]);
    console.log(obj["age"]);

属性的增删改查

    // 空对象
    let obj={

    }
    // 给空对象上添加属性 如何加
    // 添加属性
    obj.name="wangcai"  //因为调用一个对象上没有的属性的时候 相当于给他加上这个属性 如果没有赋值 则值为undefined
    // 访问属性
    console.log(obj.name);
    // 添加属性
    obj["age"]=18;
    // 访问属性
    console.log(obj["age"]);
    // 设置属性 说白了就是访问 然后重新赋值
    obj.name="zhangsan"
    console.log(obj.name);
    // 删除对象上的属性
    delete obj.name;//delete是一个运算符 instanceof typeof
    console.dir(obj);

共有属性和私有属性

<!-- <script>
    // 我们说 对于对象而言  对象是属性的无序集合
    // 既然是属性  属性是对对象的描述
    // 每个对象肯定不同 就好比 你和其他学生不一样
    // 你就是你 是不一样的烟火
    // 但是 你们又有公有的部分 比如 学生 都有学号 都有姓名 都会写作业
    // 这是公有属性  对于你来说 肯定有别人没有的 比如说你有腿毛
    // 这是啥 这叫私有属性
    // 我们JS中的对象 也有公有属性 和 私有属性之分
    let obj={
        name:"wangcai",//这叫 私有属性
        age:"18"//私有属性
    }
    console.dir(obj);
    // 那么 我们的公有属性是什么?去哪了?
</script> -->

<!-- <script>
    // 公有属性是我们这个对象在产生的时候 从原型对象上继承过来的 从母体上继承过来的
    // 现在听不懂 没关系 我们下周会讲原型对象和原型链
    // 但是我现在要让你把公有属性找出来
    // let arr=[1,2,3,4];
    let arr=new Array("1","2","3")
    console.log(arr);
    // 数组上都有push方法对不对
    // 我说 对象的某一个属性 也是对象 可以不可以?
    // 当然可以

    // 在每一个对象身上 有一个属性 叫__proto__
    // 这个属性 他对应的属性值 是一个新的对象
    // 这个对象上 有我们的公有属性
    console.dir(arr.__proto__);
    // 这是你 数组类 里面所有对象都具有的属性
</script> -->

<!-- <script>
    // 当我们调用属性的时候 他是先找公有的呢 还是先找私有的呢
    let obj={
        name:"wangcai",
        age:"100"
    }
    // 首先 hasOwnProperty是对象的公有方法
    // 他的作用是判断私有属性是否存在
    // 判断obj上是否有name这个私有属性
    console.log(obj.hasOwnProperty("name"));//true 表示有这个私有属性
    console.log(obj.hasOwnProperty("toString"));//false 表示这个对象上没有这个私有属性
    // 可以判断自己吗?
    console.log(obj.hasOwnProperty("hasOwnProperty"));  //false
    //判断自己的私有属性有没有hasOwnProperty

    // 我们说 公有属性都是在__proto__上面  
    console.log(obj.__proto__.hasOwnProperty("hasOwnProperty"));


    // 我们说他去找属性的一个过程  就像你找变量的一样  先找自己的 再找更大的
    // 我们去找对象里面的属性也是 先找自己的私有变量 然后再去找共有变量
</script> -->

<!-- <script>
    let arr=["a","b","c"]
    arr.push("d");
    // 这里调用的是是公有属性上的push
    // 现在arr私有属性找 找不到 然后去公有属性找 找到了 然后就用
    arr.valueOf();
    // 先去私有属性找valueOf 
    // 找不到 然后沿着__proto__去他的 隐士原型对象 上面找
    // 还没有找到 那么继续沿着__proto__找
    // 然后找到了
    console.dir(arr.__proto__.__proto__);
    console.dir(arr.__proto__.__proto__.__proto__);
    // 因为我们的arr是一个数组 所以最开始__proto__找到的是 数组的原型对象
    // 然后 我问 数组的原型对象  是不是对象?
    // 是  所以这个对象还有一个原型对象  就是 对象的原型对象
    // 那么 对象的原型对象 就是最上面了  再找就没了
    // 这种链式的寻找过程 我们称之为 原型链 然后 所有对象的顶点 就是Object

    // 那么 如果这个过程中 你到某一步找不到了
    console.log(arr.a);//undefined
    // 现在私有属性找 找不到 沿着__proto__找 也找不到.
    // 最后都没有找到 那就是undefined
    // 切记 这里不能加() 因为undefined不能加括号
</script> -->
<!-- 
<script>
    let arr=["a"];
    console.log(arr.hasOwnProperty("push"));//false 因为Push是公有属性
    // 判断push是不是私有属性 

    // 还有一个叫in
    console.log("push" in arr);//true
    // in就是判断有没有这个属性 不管是公有还是私有

</script> -->

<script>
    let arr1=new Array("a","b","c")
    let arr2=new Array("a","b","c")
    console.dir(arr1)
    console.dir(arr2)
    console.log(Array.prototype == arr1.__proto__);//true 指向的 都是Array的原型对象
    // 还记我今天早上说的 一个对象的产生 离不开构造器和原型对象
    // 构造器和原型对象 就像他的父母一样
</script>

函数

函数的四大角色

角色一:最普通的函数
角色二:方法
角色三:函数也是对象
角色四:函数也是一个构造器(类)

    // 角色一:最普通的函数
    function fn(){
        console.log("我就是一个函数");
    }
    function fn1(){

    }
    function fn2(){

    }
    // 角色二:方法
    let obj={
        name:"zhangsan",
        age:"16",
        say:function(){
            console.log("我会说话");
        }
        // 在这里面,我们say方法的本质 就是一个函数
    }
    // 角色三:函数也是对象
    function kn(){

    }
    kn.a=1;
    kn.b=2;
    kn.c=3;
    // 我们说 只有对象才能打点调用其身上的属性
    console.log(kn.a);
    console.log(kn.b);
    console.log(kn.c);

    // 角色四:函数也是一个构造器(类)
    // 什么是类:一类相同事物的集合
    // Number是类
    // 一般情况下,如果你要把函数当作一个构造器,函数名首字母需要大写,当然这个不是必须
    // 一般情况下,如果你函数的首字母大写了,那么后期通常将他作为一个构造器()去使用
    // 对象是属性的无序集合

    // 动物类  属性是用来描述对象的
    // 动物类都有什么? 品种,年龄,几条腿,叫声
    function Animal(name,age,num,say){
        this.name=name;
        this.age=age;
        this.num=num;
        this.say=say;
        // 这四个 就是对象身上的属性 
        // 我们通过Animal创造出来的对象 都有这四个属性
        // 我们之前创造对象的几种方式中 有一个就是new+构造器
    }
    // 我既然要创造实例化对象,那么肯定需要new 我通过new+构造器 得到我的对象
    let animal1=new Animal("dog","18","4","wangwang");
    console.log(animal1.name);
    console.log(animal1.age);
    console.log(animal1.num);
    console.log(animal1.say);
    console.log(animal1);
    // 以前我们用Array构造器的时候 是需要往里面传参的 let arr1=new Array(5)

call apply bind

call的使用

<!-- call 调用别人的方法 -->
<!-- 你去自动贩卖机 然后该扫码付钱了 扫脸  然后你突然低头 扫到后面的人了 -->
<!-- 用别人的钱给你买了东西 用别人的资源 做了你要做的事情 -->
<script>
    var name="qq";
    function say(){
        console.log("我是say函数");
        console.log(this.name);
    }

    let obj1={name:"wc"};
    let obj2={name:"xq"};

    // 我现在 想借用window上面的say方法  输出我这两个对象的名字
    say.call();

    // say函数目前有四个角色  1.普通函数 2.方法 3.对象 4.类
    // 如果一个函数后面.call() 会让函数执行
    // 但是如果不写参数 就是普通的让函数执行 没有其他意思

    say.call(obj1);
    say.call(obj2);
    // 以上表示 obj1和obj2借用了say函数 
    // 当call的参数只有一个对象的时候 那么表示这个对象借用call前面的函数
    // 那么这里面都做了什么? 1.让函数执行 2.改变了this的指向,让this指向参数的对象

    function f1(a,b){
        console.log("我是"+this.name+"的函数,传进来的参数是"+a+","+b);
    }
    f1(1,2)
    // 我现在既想用call去借用f1 又想传参
    f1.call(obj1,1,2)
    f1.call(obj2,3,4)
</script>

apply的使用

<script>
    //call方法的作用是什么  apply方法的作用就是啥
    //区别:传参的方式不同
    function f1(a,b){
        console.log("我是"+this.name+"的函数,传进来的参数是"+a+","+b);
    }
    let obj1={name:"wc"};
    let obj2={name:"xq"};
    // apply的使用
    f1.apply(obj1,[1,2])
    f1.apply(obj2,[3,4])
</script>

通过apply求数组最大值

<!-- 通过apply求一个数组中的最大值 -->
<!-- apply作用是啥? 借用  和call的区别在于参数的不同 -->

<!-- <script>
    // 首先 你想求一个数组的最大值 我们都是循环得到
    var arr=[2,3,10,2,89,100,48,101];
    let max=arr[0];
    arr.forEach(function(item){
        if(item>max){
            max=item
        }
    })
    console.log(max);
</script> -->

<script>
    var arr=[2,3,10,2,89,100,48,101];
    // Math 是JS中 单体内置对象  不需要new 可以直接使用
    console.dir(Math);
    // max 方法 就是求最大值
    console.log(Math.max(1,2,3,4,5));

    // 我们现在是不是要借用Math里面的max方法
    // 用apply
    // 因为我们这里没有用到this,但是apply第一个参数  就是要指向的对象 且将this指向这个对象
    // 我们没有用到this 但是参数又不能不写 所以需要一个空的东西来占位
    // 回一下apply的参数啥样的? []
    console.log(Math.max.apply({},arr));
    console.log(Math.max.apply(123,arr));
    console.log(Math.max.apply(null,arr));
</script>

bind的使用

<script>
    //call方法的作用是什么  apply方法的作用就是啥
    //区别:传参的方式不同
    function f1(){
        console.log("我是"+this.name+"的函数,传进来的参数是");
    }
    let obj1={name:"wc"};
    let obj2={name:"xq"};
    // bind的使用
    f1.bind(obj1)();
    f1.bind(obj2);
    // bind的返回值是一个改变过this指向的函数
    // 以上面这个为例 那么f1.bind(obj1)的返回值就是一个改变了this指向 让this指向obj1的f1函数
</script>

三者区别

//call方法的作用是什么 apply、bind方法的作用就是啥
//区别:传参的方式不同
// apply的使用
f1.apply(obj1,[1,2])
f1.apply(obj2,[3,4])
//call的使用
say.call(obj1);
say.call(obj2);
// bind的使用
f1.bind(obj1)();
f1.bind(obj2);

bind的返回值是一个改变过this指向的函数
call的作用1.让函数执行 2.改变了this的指向,让this指向参数的对象

继承

继承的概念

什么是继承?
有一个孩子 他爸是双眼皮 他妈妈是单眼皮 这个孩子是双眼皮
这孩子明显就是继承了他父亲的双眼皮
在我们的JS中 或者说 类中
私有属性 公有属性
那么 这个属性能否继承?
我们在这里 现在没有extend extend是Java里面的
那么 我如果让两个类 或者说两个构造器之间
产生一个继承的关系?
就比如 我现在父级类中有一个私有属性a
然后我现在需要另一个类 儿子类 创建的对象 也有私有属性a
但是这个a 还必须是从父级那里得到的 而不是子级里面自己写的

继承的方式

原型继承

<!-- <script>
    function Father(a){
        this.a=a
        // 如果说我把Father当作一个构造器的话  那么a就是他的私有属性
    }
    Father.prototype.getA=function(){
        console.log(this.a);
        // 如果你把方法或者属性写到了自己所对应的原型对象上 那么这个属性或者方法就是公有的
        // 为什么是公有 因为你每一个用Father生成的对象 都可以沿着__proto__找到你这个原型对象然后用
    }
    function Son(){

    }
    // 此时的Son和Father有没有关系?没有
    // 那么Son这个类中有没有a私有属性?没有
    // 我现在想让Son和Father扯上关系 然后Son把Father的私有属性a继承过来
    let son=new Son();
    console.log(son.a);//undefined
    console.log(son.getA());
</script> -->

<script>
    function Father(){
        this.a=100
        // 如果说我把Father当作一个构造器的话  那么a就是他的私有属性
    }
    Father.prototype.getA=function(){
        console.log(this.a);
        // 如果你把方法或者属性写到了自己所对应的原型对象上 那么这个属性或者方法就是公有的
        // 为什么是公有 因为你每一个用Father生成的对象 都可以沿着__proto__找到你这个原型对象然后用
    }
    function Son(){
    }
    Son.prototype=new Father();
    Son.prototype.constuctor=Son;

    let son=new Son();
    console.log(son.a);//
    son.getA()
    // 原形继承 实际上就是把父级的私有属性和公有属性 都继承为子级的公有属性
    // 他的核心 核心就是通过改变子级的原型对象 让子级的原型对象变成父级的实例化对象
    // 父级的私有属性都给了实例化对象 而把实例化对象作为子级的原型对象 那么这些属性就全部变成了子级的公有属性
</script>

call继承

<script>
    function Father(){
        this.a=110;
    }
    Father.prototype.getA=function(){
        console.log(this.a);
    }
    function Son(){
        // 如果你想用call继承
        // 那么你需要在你的子级里面写一个东西
        Father.call(this);
        // Father的角色  1.函数 2.他也是一个类 3.他还是一个对象
        // call两个作用 
        // 1.改变this的指向 2.执行函数

        // say.call(obj1)
    }
    let son=new Son();
    // new作用有哪些?
    // 1.开了一个新空间让this指向这个空间
    // 2.返回一个地址
    // 3.执行构造器函数

    // 当你生成对象的时候 你Son里面的this 指的是什么?
    // 说白了  你此时Son里面的 Father.call(this)  等价于 Father.call(son)
    // 然后执行了函数Father  Father里面的代码时 this.a=110;
    // son.a=110 你这里是不是就给son添加了私有属性 a
    console.log(son.a);
    son.getA()//报错
    // son.a结果是110

    // 通过call 把父级当作一个函数执行 改变里面的this指向 让this指向新的子对象
    // 然后给新的子对象添加上了父级一模一样的属性 并赋初值
    // 从而实现了继承 

    // call继承的特点  只能继承私有属性  并且 继承下来后 也是子级的私有属性
</script>

混合继承

<!-- 
    call继承
        1.只能继承私有属性
        2.继承过来后还是私有属性
    原形继承:
        1.私有属性和公有属性都能继承
        2.继承过来后 都是公有属性
    如何私有属性和公有属性都继承过来,并且私有属性还是私有属性,公有属性还是公有属性?
 -->
 <!-- 组合继承 -->
 <!-- 组合继承就是把两个继承的核心代码拿过来 -->
 <script>
    function Father(){
        this.a=110;
    }
    Father.prototype.getA=function(){
        console.log(this.a);
    }
    function Son(){
        // call继承的核心代码  
        Father.call(this)
    }
    // 然后是原形继承
    // 原形继承的核心代码
    // 1.先改变子类的原型对象 改成父级的一个实例化对象
    Son.prototype=new Father();
    // 2.让父级 的实例化对象中的constructor指向子级
    Son.prototype.constructor=Son;

    let son=new Son();
    console.log(son.a);
    // 可能有人就会问了 你这里的这个a 到底是你继承下来的 还是你去原形对象上找的?
    // 因为我们原形继承也会继承私有属性 只不过继承下来后变成了公有属性
    // call继承把父级的私有属性继承下来作为子级的私有属性
    // 也就是说 现在子级的私有属性上有一个a 公有属性上也有一个a
    // 我们找东西 先找自己的呀  所以这里先找的是 私有属性 在私有属性上找到了a
    // 所以 不会再去公有属性上找了
    // 此时  私有属性和公有属性都继承下来了  并且 私有属性还是私有属性
    son.getA();
 </script>

只继承公有属性


<script>
    function Father(){
        this.a=110;
    }
    Father.prototype.getA=function(){
        console.log("这是Father的公有属性");
        console.log(this.a);
    }
    function Son(){
    }

    var Fn=function(){}
    Fn.prototype=Father.prototype;

    // 你原本的Father有私有属性  所以继承下去 也会把私有属性继承
    // 而我现在只需要公有属性  所以我创建了一个新的函数(类 构造器)
    // 然后这里面没有私有属性 然后我改变他的原型对象为Father的原型对象
    // 那么 公有属性拿到了  私有属性 没有了
    Son.prototype=new Fn();
    let son=new Son();
    console.log(son.a);
    son.getA()
</script>

特点

call继承
1.只能继承私有属性
2.继承过来后还是私有属性
原形继承:
1.私有属性和公有属性都能继承
2.继承过来后 都是公有属性
混合继承:
私有属性和公有属性都继承下来了 并且 私有属性还是私有属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值