面向对象二(原型、原型链、类)

84 篇文章 3 订阅
3 篇文章 0 订阅

目录

1.​主题:

2.拖拽的构造函数实现

2.1- 拖拽的面相过程写法

2.2- 拖拽的面相对象写法

3.构造函数继承

3.1构造函数继承:

4.原型的继承

5.原型的深拷贝继承

5.1JSON序列化继承原型

5.2自定义深拷贝函数:

5.3使用自定义深拷贝函数实现原型继承:

5.3组合继承

6.原型链

7.拖拽的边界需求 :通过继承来解决

8.ES6中的类

8.1类的写法

8.2静态方法和属性:实例不会继承的属性和方法,但是子类还是会继承父类的静态方法和属性

8.3类的继承:extends

8.4子类重写父类方法

8.5使用class继承实现拖拽

9.包装对象

10.常用方法

10.1hasOwnProperty():看是不是对象自身底下的属性

10.2contructor()查看对象的构造函数 可以用来做判断是属于哪个构造函数:

10.3instanceof():对象与构造函数是否在原型链上有关系。检测的是原型:

10.4typeof() 返回的是表达式的数据类型:

10.5toString()判断类型;


1.​主题:

  1. 拖拽案例
  2. ​构造函数继承
  3. ​原型的继承
  4. ​拖拽案例的继承改造
  5. ​es6中的类的用法

2.拖拽的构造函数实现

2.1- 拖拽的面相过程写法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #div1 {
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;
            top: 20px;
            left: 50px;
        }
        #div2 {
            width: 100px;
            height: 100px;
            background: blue;
            position: absolute;
            top: 200px;
            left: 400px;
        }
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<script>
{
    //需求:面向过程方法实现拖拽
    let div1 = document.querySelector("#div1");
    div1.addEventListener("mousedown",function(ev){
        //浏览器兼容
        let e = ev || window.event;

        //获取初始化鼠标坐标
        let startPos = {};
        startPos.x = e.clientX;
        startPos.y = e.clientY;

        //获取初始left和top
        let left = div1.offsetLeft;
        let top = div1.offsetTop;
        // console.log(startPos);
        function move(ev){
            let e = ev || window.event;
            let curPos = {};
            curPos.x = e.clientX;
            curPos.y = e.clientY;
            // console.log(curPos);
            console.log(curPos.x - startPos.x + div1.offsetLeft);
            console.log(curPos.y - startPos.y + div1.offsetTop);
            
            div1.style.left = curPos.x - startPos.x + left + 'px';
            div1.style.top = curPos.y - startPos.y + top + 'px';
        }
        document.addEventListener("mousemove",move);
        document.addEventListener("mouseup",function(){
            document.removeEventListener("mousemove",move);
        },{
            once:true
        });
        
    });
    
}
</script>
</body>
</html>

2.2- 拖拽的面相对象写法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #div1 {
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;
            top: 20px;
            left: 50px;
        }
        #div2 {
            width: 100px;
            height: 100px;
            background: blue;
            position: absolute;
            top: 200px;
            left: 400px;
        }
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<script>
{
    //需求:面向对象方法实现拖拽

    //构造函数一般只写属性
    function Drag(ele){
        this.ele = ele;
        this.mouseDown();
    }
    Drag.prototype.mouseDown = function(){
        //需要将this传递到事件里:赋值或者箭头函数
        this.ele.addEventListener("mousedown",ev=>{
            //浏览器兼容
            let e = ev || window.event;

            //获取初始化鼠标坐标,直接求出初始化时鼠标坐标和left/top的值,以免要传递多个参数
            let x = e.clientX - this.ele.offsetLeft;
            let y = e.clientY - this.ele.offsetTop;

            this.mouseMove(x,y);
            this.mouseUp(x,y);
        });
    }
    //由于move方法也需要共用,所以也写在原型上
    Drag.prototype.setStyle = function(l,t){
        this.ele.style.left = l + 'px';
        this.ele.style.top = t + 'px';
    }
    Drag.prototype.mouseMove = function(x,y){
        //如果将onmousemove加在document上并使用监听和匿名函数,则在move方法中的this会指向document,所以需要加在this.ele
        //使用监听时不好传递参数
        document.onmousemove = ev=>{
            let e = ev || window.event;
            let left = e.clientX - x;
            let top = e.clientY - y;

            this.setStyle(left,top);
        };
    }
    Drag.prototype.mouseUp = function(x,y){
        document.onmouseup = ev=>{
            //清除鼠标移动事件可以直接使this.ele.onmousemove = "";
            document.onmousemove = "";
        };
    }
    let div1 = document.querySelector("#div1");
    let div2 = document.querySelector("#div2");
    let drag1 = new Drag(div1);
    let drag2 = new Drag(div2);
    
}
</script>
</body>
</html>

3.构造函数继承

  1. 继承:子类继承父类所有属性和行为,父类不受影响。
  2. 目的:找到类之间的共性精简代码
  3. ES5中,使用call()/apply()/bind()修改this指向来实现继承
  4. 只会继承父类的构造函数,而不会继承父类的原型
  5. 解决原型不会继承继承问题:直接将父类原型赋值给子类,JOSN序列化拷贝对象,自定义深拷贝方法,组合继承
  6. 简单原型继承(直接将父类原型赋值给子类),当子类修改原型时会影响父类的情况(传址给子类);
  7. JOSN序列化原型继承:当对象中存在undefined和函数时,拷贝的对象会忽略

3.1构造函数继承:

通过call()/apply()/bind()修改this指向来实现,子类继承父类所有属性和行为

    //继承:子类继承父类的所有属性和行为,父类不受影响
    function Parent(name){
        this.eyes = 2;
        this.name = name;
        this.legs = 2;
    } 
    //写子类,通过call()/apply()/bind()方法修改子类的this指向,并传入参数即可
    function Son(name){
        Parent.call(this,name);
        //并由自己的独特属性
        this.hobby = "打篮球";
    } 

    //发现子类继承父类后,拥有所有父类的属性和行为,父类不受影响
    let parent = new Parent("老张");
    let son = new Son("小明");
    console.log(parent);//Parent {eyes: 2, name: "老张", legs: 2} 
    console.log(son);//Son {eyes: 2, name: "小明", legs: 2, hobby: "打篮球"}

- 继承问题:继承只会继承父类的构造函数,而不会继承父类的原型

    //继承:只会继承父类的构造函数,而不会继承父类的原型
    Parent.prototype.job = function(){
        console.log("程序员");
    }
    let parent = new Parent("老张");
    let son = new Son("小明");
    parent.job();//程序员
    son.job();//报错:Uncaught TypeError: son.job is not a function

4.原型的继承

解决构造函数继承不能继承原型问题:直接将父类原型赋值给子类;JSON序列化;自定义深拷贝继承;组合继承

  • - 简单原型继承(直接将父类原型赋值给子类),出现影响父类的情况;

如下:直接将父类原型赋值给子类后,子类拥有了父类的原型,但是当子类修改了原型时,父类原型也收到了影响。所以这种方式不可用。解决:进行深拷贝,见下一节。

    //简单继承:直接将父类原型赋值给子类。问题:修改子类原型时会影响父类
    Son.prototype = Parent.prototype;
    let parent = new Parent("老张");
    let son = new Son("小明");
    parent.job();//程序员
    son.job();//程序员
    //当通过子类Son修改赋值得到的原型时,发现父类的原型也发生了改变
    Son.prototype.job = function(){
        console.log("销售");
    }
    parent.job();//销售
    son.job();//销售

5.原型的深拷贝继承

深拷贝继承(JSON序列化/自定义深拷贝函数);组合继承

Object.assign()方法只会拷贝一层;arr.flat()方法扁平化多维数组,都不适合多层的深拷贝。

- 传值和传址问题:

  1. - 基本数据类型传值:Number、String、Boolean、Null、Undefined、BigInt、Symbol
  2. - 复杂数据类型/引用数据类型传址:Array、Date、Math、RegExp、Object、Function

5.1JSON序列化继承原型

- JSON序列化的不足:

  • 如果拷贝对象包含函数,或者undefined等值,此方法就会出现问题,不会进行拷贝函数和undefined。

所以如果原型上有为undefined或者函数时,尽量不要使用JSON进行原型拷贝。

    //JSON序列化解决不能继承原型的问题。问题:当对象中有undefined或函数时,不会进行拷贝
    let parentProto = {
        name:"张三",
        age:20,
        test:undefined,
        fn:function(){
            console.log("fn...");
        }
    };
    let sonProto = JSON.parse(JSON.stringify(parentProto)); 

    console.log(parentProto);//{name: "张三", age: 20, test: undefined, fn: ƒ} 
    console.log(sonProto);//{name: "张三", age: 20}

5.2自定义深拷贝函数:

如果对象上有多层对象嵌套,可以进行深拷贝。

问题1:for...in循环会循环原型上的属性或方法,所以需要使用hasOwnProperty()方法判断,传入的对象自身的属性而不是原型上的属性,再进行深拷贝

问题2:但是如果有属性值为null,通过自定义深拷贝函数进行拷贝后,得到的对象中,null会转为一个空对象。

    //自定义深拷贝函数
    let parentProto = {
        name:"张三",
        age:20,
        gender:undefined,
        addr:null,
        fn:function(){
            console.log("fn...");
        }
    };

    function deepCopy(obj){
        //判断参数是对象还是数组,如果是数组就创建新数组进行拷贝,如果是对象就创建新对象进行拷贝
        let newObj = Array.isArray(obj)?[]:{};
        //通过for...in循环进行拷贝
        for(let key in obj){
            //hasOwnProperty():看是不是对象自身底下的属性
            //for...in循环会循环原型上的属性或方法,所以需要使用hasOwnProperty()方法判断,传入的对象自身的属性而不是原型上的属性,再进行深拷贝
            if(obj.hasOwnProperty(key)){
                //如果得到的属性仍然是一个对象,就继续进行循环
                if(typeof obj[key] == "object"){
                    newObj[key] = deepCopy(obj[key]);
                }else{
                    newObj[key] = obj[key];
                }
            }
        }
        return newObj;
    }
    let sonProto = deepCopy(parentProto);
    console.log(parentProto);//{name: "张三", age: 20, gender: undefined, addr: null, fn: ƒ}
    console.log(sonProto);//{name: "张三", age: 20, gender: undefined, addr: {…}, fn: ƒ}

5.3使用自定义深拷贝函数实现原型继承:

注意:必须在进行深拷贝时是直接将父类进行拷贝后的对象赋值给子类,会覆盖掉原有的原型对象上的constructor。这样当需要通过原型上的contructor 判断属于哪个构造函数时,会失效。所以,需要将原型中的构造函数进行追加Son.prototype.constructor = Son; 

    //继承:子类继承父类的所有属性和行为,父类不受影响
    function Parent(name){
        this.eyes = 2;
        this.name = name;
        this.legs = 2;
    } 
    function Son(name){
        Parent.call(this,name);
        this.hobby = "打篮球";
    } 

    //继承:只会继承父类的构造函数,而不会继承父类的原型
    Parent.prototype.job = function(){
        console.log("程序员");
    }
    //自定义深拷贝实现原型继承
    function deepCopy(obj){
        let newObj = Array.isArray(obj)?[]:{};
        for(let key in obj){
            if(obj.hasOwnProperty(key)){
                if(typeof obj[key] == "object"){
                    newObj[key] = deepCopy(obj[key]);
                }else{
                    newObj[key] = obj[key];
                }
            }
        }
        return newObj;
    }
    Son.prototype = deepCopy(Parent.prototype);
    //实例化后的对象有构造函数和原型构成,如果只拷贝原型会丢失构造函数,所以还需要将构造函数返回
    //有时候会通过constructor来判断实例化后的对象是属于哪个构造函数,如果Son.prototype.constructor = Son;不写就判断不了
    Son.prototype.constructor = Son;
    let parent = new Parent("老张");
    let son = new Son("小明");
    Son.prototype.job = function(){
        console.log("销售");
    }
    parent.job();//程序员
    son.job();//销售

 重新追加constructor :Son.prototype.constructor = Son; 

//如果不写Son.prototype.constructor = Son;时
    console.log(son.constructor);// Object() { [native code] }
    Son.prototype.constructor = Son;
    console.log(son.constructor);//Son(name){Parent.call(this,name);this.hobby = "打篮球";}

需要追加constructor 的作用:可以通过constructor来判断,其属于哪个构造器

    //需要追加constructor的作用:可以通过constructor来判断,其属于哪个构造器
    console.log(son.constructor === Son);//true
    let str = 'abc';
    console.log(str.constructor === String);//true

5.3组合继承

  • 写一个空的构造函数;
  • 把父级原型赋值给这个空的构造函数;
  • 再把实例化这个空构造函数并赋值给自己的原型
    function Parent(name){
        this.eyes = 2;
        this.name = name;
        this.legs = 2;
    } 
    function Son(name){
        Parent.call(this,name);
    } 
    Parent.prototype.job = function(){
        console.log("程序员");
    }

    //写一个空的构造函数;
    // 把父级原型赋值给这个空的构造函数;
    // 再把实例化这个空构造函数并赋值给自己的原型
    let Link = function(){};
    Link.prototype = Parent.prototype;
    Son.prototype = new Link();
    //无论哪种方式实现继承都需要设置会子类的构造函数
    Son.prototype.constructor = Son;

    let parent = new Parent("张三");
    let son = new Son("李四");
    Son.prototype.job = function(){
        console.log("销售");
    }
    parent.job();//程序员
    son.job();//销售

6.原型链

构造函数有构造函数自身和原型两部分组成。而原型本身也是一对对象,所以构造函数的原型又由其自身和原型组成。一直链条式进行。

查找顺序:先找自身构造函数->再找自身的原型->Object底层的原型->最终找不到返回undefined

原型链是指对象在访问属性或方法时的查找方式。

1.当访问一个对象的属性或方法时,会先在对象自身上查找属性或方法是否存在,如果存在就使用对象自身的属性或方法。如果不存在就去创建对象的构造函数的原型对象中查找 ,依此类推,直到找到为止。如果到顶层对象中还找不到,则返回 undefined。

2.原型链最顶层为 Object 构造函数的 prototype 原型对象(Object的原型prototype不会再有原型,即Object.prototype再也没有原型了),给 Object.prototype 添加属性或方法可以被除 null 和 undefined 之外的所有数据类型对象使用。

如下图:此处:首先在实例化对象parent本身的构造函数Parent上查找name,如果没有找到,会到实例化对象parent的原型上即Parent.prototype上查找,还是没有找到就到底层Object.prototype上查找,最终都没有找到就返回undefined

    //原型链
    function Parent(){
        this.name = "张三";
    }
    Parent.prototype.name = "李四";
    Object.prototype.name = "王五";
    let parent = new Parent();
    //此处:首先在实例化对象parent本身的构造函数Parent上查找name,如果没有找到,会到实例化对象parent的原型上即Parent.prototype上查找,还是没有找到就到底层Object.prototype上查找,最终都没有找到就返回undefined
    console.log(parent.name);

7.拖拽的边界需求 :通过继承来解决

需求:2个div实现拖拽 其中第二个需要设置边界不能拖出可视区域

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #div1 {
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;
            top: 20px;
            left: 50px;
        }
        #div2 {
            width: 100px;
            height: 100px;
            background: blue;
            position: absolute;
            top: 200px;
            left: 400px;
        }
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<script>
{
    //需求:使用继承实现第二个div不超出边界

    //构造函数一般只写属性
    function Drag(ele){
        this.ele = ele;
        this.mouseDown();
    }
    Drag.prototype.mouseDown = function(){
        //需要将this传递到事件里:赋值或者箭头函数
        this.ele.addEventListener("mousedown",ev=>{
            //浏览器兼容
            let e = ev || window.event;

            //获取初始化鼠标坐标,直接求出初始化时鼠标坐标和left/top的值,以免要传递多个参数
            let x = e.clientX - this.ele.offsetLeft;
            let y = e.clientY - this.ele.offsetTop;

            this.mouseMove(x,y);
            this.mouseUp(x,y);
        });
    }
    //由于move方法也需要共用,所以也写在原型上
    Drag.prototype.setStyle = function(l,t){
        this.ele.style.left = l + 'px';
        this.ele.style.top = t + 'px';
    }
    Drag.prototype.mouseMove = function(x,y){
        //如果将onmousemove加在document上并使用监听和匿名函数,则在move方法中的this会指向document,所以需要加在this.ele
        //使用监听时不好传递参数
        document.onmousemove = ev=>{
            let e = ev || window.event;
            let left = e.clientX - x;
            let top = e.clientY - y;

            this.setStyle(left,top);
        };
    }
    Drag.prototype.mouseUp = function(x,y){
        document.onmouseup = ev=>{
            //清除鼠标移动事件可以直接使this.ele.onmousemove = "";
            document.onmousemove = "";
        };
    }
    let div1 = document.querySelector("#div1");
    let drag1 = new Drag(div1);
    
    //给第二个div创建子类继承父类
    function Div2Drag(ele){
        //继承的同时要把所有参数传递过去
        Drag.call(this,ele);
    }
    //声明新的Temp空构造,将父级原型赋值给这个空构造的原型
    let Temp = function(){};
    Temp.prototype = Drag.prototype;
    //将示例化后的Temp赋值给子级的原型
    Div2Drag.prototype = new Temp();
    //还原子级原有的构造方法
    Div2Drag.prototype.constructor = Div2Drag;

    //重写Div2Drag的鼠标移动方法
    Div2Drag.prototype.mouseMove = function(x,y){
        document.onmousemove = ev=>{
            let e = ev || window.event;
            let left = e.clientX - x;
            let top = e.clientY - y;
            //限定边界
            left = Math.max(0,left);
            top = Math.max(0,top);
            left = Math.min(document.documentElement.clientWidth - this.ele.offsetWidth,left);
            top = Math.min(document.documentElement.clientHeight - this.ele.offsetHeight,top);
            this.setStyle(left,top);
        };
    }

    let div2Drag = new Div2Drag(div2);
    
}
</script>
</body>
</html>

8.ES6中的类

ES6中的方法会自动加到原型中;

父类的静态属性和方法,不能通过实例化后的对象获取;

子类可以继承父类的静态属性和方法,但是子类实例化后的对象不能获取到父类的静态属性和方法;

8.1类的写法

class Person{
  	height="178cm";
    constructor(name,age){
        //属性
        this.name = name;
        this.age = age;
    }
    //方法
    getName(){
        console.log("姓名是:"+this.name);
    }
}
let student = new Person("张三",20);
student.getName();

8.2静态方法和属性:实例不会继承的属性和方法,但是子类还是会继承父类的静态方法和属性

 class Person{
      //静态方法
      static hobby(){
          console.log("喜欢篮球");
      }
  }
  //静态属性
  Person.height = "178cm";
  //通过类来调用
  Person.hobby();
  console.log(Person.height);

8.3类的继承:extends

  class Dad{
      name = "张三";
      age = 40;
      constructor(height){
          this.height = height;
      }
      hobby(){
          console.log("喜欢篮球");
      }
  }
  class Son extends Dad{
      constructor(height){
          //表示父类的构造函数
          super(height);
      }
  }
  let son1 = new Son("178cm");
  son1.hobby();
  console.log(son1.height);

8.4子类重写父类方法

  class Dad{
      name = "张三";
      age = 40;
      constructor(height){
          this.height = height;
      }
      hobby(){
          console.log("喜欢篮球");
      }
  }
  class Son extends Dad{
      constructor(height){
          //表示父类的构造函数
          super(height);
      }
      hobby(){
          super.hobby();
          console.log("也喜欢足球");
      }
  }
  let son1 = new Son("178cm");
  son1.hobby();//喜欢篮球 也喜欢足球
  console.log(son1.height);//178cm

8.5使用class继承实现拖拽

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #div1 {
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;
            top: 20px;
            left: 50px;
        }

        #div2 {
            width: 100px;
            height: 100px;
            background: blue;
            position: absolute;
            top: 200px;
            left: 400px;
        }
    </style>
</head>

<body>
    <div id="div1"></div>
    <div id="div2"></div>
    <script>
        {
            //需求:ES6的继承,class实现继承没有原型的浅拷贝问题
            class Drag {
                constructor(ele) {
                    this.ele = ele;
                    this.mouseDown();
                }
                mouseDown() {
                    this.ele.addEventListener("mousedown", ev => {
                        //浏览器兼容
                        let e = ev || window.event;

                        //获取初始化鼠标坐标,直接求出初始化时鼠标坐标和left/top的值,以免要传递多个参数
                        let x = e.clientX - this.ele.offsetLeft;
                        let y = e.clientY - this.ele.offsetTop;

                        this.mouseMove(x, y);
                        this.mouseUp();
                    });
                }
                setStyle(l, t) {
                    this.ele.style.left = l + 'px';
                    this.ele.style.top = t + 'px';
                }
                mouseMove(x, y) {
                    document.onmousemove = ev => {
                        let e = ev || window.event;
                        let left = e.clientX - x;
                        let top = e.clientY - y;
                        this.setStyle(left, top);
                    };
                }
                mouseUp() {
                    document.onmouseup = function () {
                        document.onmousemove = "";
                    }
                }
            }
            let div1 = document.querySelector("#div1");
            let drag1 = new Drag(div1);

            //给第二个div创建子类继承父类
            class Div2Drag extends Drag {
                constructor(ele) {
                    super(ele);
                }
                mouseMove = function (x, y) {
                    document.onmousemove = ev => {
                        let e = ev || window.event;
                        let left = e.clientX - x;
                        let top = e.clientY - y;
                        //限定边界
                        left = Math.max(0, left);
                        top = Math.max(0, top);
                        left = Math.min(document.documentElement.clientWidth - this.ele.offsetWidth, left);
                        top = Math.min(document.documentElement.clientHeight - this.ele.offsetHeight, top);
                        this.setStyle(left, top);
                    };
                }
            }
            
            let div2Drag = new Div2Drag(div2);
        }
    </script>
</body>

</html>

9.包装对象

- 除过null,undefined,基本类型都有自己对应的包装对象:String Number Boolean    

- 包装对象把所有的属性和方法给了基本类型,然后包装对象消失

当基本数据类型需要用到包装类型上的方法时,对自动生成包装对象。

原理分析:

    //包装对象:String Number Boolean    
    let str = "a b c";

    //基本数据类型调用包装对象的方法时,原理分析
    function mySplit(str,method,arg){
        let temp = new String(str);
        //最终包装完后系统会销毁包装对象
        return temp[method](arg);
    }

    let res = mySplit(str,"split"," ");
    console.log(res);//(3) ["a", "b", "c"]

10.常用方法

javascript中判断数据类型的四种方法及typeof、instanceof、constructor、toString的详细讲解参考链接:https://blog.csdn.net/liwenfei123/article/details/77978027

  1. - hasOwnProperty():看是不是对象自身底下的属性(而不是属于原型的)。如自定义深拷贝
  2. - contructor查()看对象的构造函数 可以用来做判断是属于哪个构造函数
  3. - instanceof():对象与构造函数是否在原型链上有关系
  4. typeof():typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。而对于Date,Array,RegExp等都是返回原型链最顶端的Object。
  5. - toString()判断类型; 转换字符串 进制转换

10.1hasOwnProperty():看是不是对象自身底下的属性

function deepCopy(obj){
        //判断参数是对象还是数组,如果是数组就创建新数组进行拷贝,如果是对象就创建新对象进行拷贝
        let newObj = Array.isArray(obj)?[]:{};
        //通过for...in循环进行拷贝
        for(let key in obj){
            //hasOwnProperty():看是不是对象自身底下的属性,而不是原型链
            //for...in循环会循环原型上的属性或方法,所以需要使用hasOwnProperty()方法判断,传入的对象自身的属性而不是原型上的属性,再进行深拷贝
            if(obj.hasOwnProperty(key)){
                //如果得到的属性仍然是一个对象,就继续进行循环
                if(typeof obj[key] == "object"){
                    newObj[key] = deepCopy(obj[key]);
                }else{
                    newObj[key] = obj[key];
                }
            }
        }
        return newObj;
    }

10.2contructor()查看对象的构造函数 可以用来做判断是属于哪个构造函数:

    let str = 'abc';
    console.log(str.constructor === String);//true

10.3instanceof():对象与构造函数是否在原型链上有关系。检测的是原型:

    //instanceof函数:instanceof 检测的是原型
    let arr = [];
    let obj = {};

    console.log(arr instanceof Array);//true
    console.log(obj instanceof Object);//true
    //因为instanceof判断的是原型链上的关系,而数组本身也是复杂数据类型-对象。所以不能使用instanceof来判断一个数组是否是Array
    console.log(arr instanceof Object);//true

    //因为字符串str是基本数据类型,没有原型,而instanceof判断的是原型,所以会返回false
    let str = "abc";
    let str1 = new String("sdf");
    console.log(str instanceof String);//false
    console.log(str1 instanceof String);//true
    console.log(str);
    console.log(str1);//String {"sdf"}

10.4typeof() 返回的是表达式的数据类型:

   console.log(typeof arr);//object
   console.log(typeof obj);//object

10.5toString()判断类型;

转换字符串 进制转换:用Object.prototype.toString.call(arr)方法判断的类型最为准确。但是必须通过 call 或 apply 来调用,而不能直接调用 toString()

    console.log(Object.prototype.toString.call(arr));//[object Array]
    console.log(Object.prototype.toString.call(obj));//[object Object]
    console.log(Object.prototype.toString.call(str));//[object String]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值