js面向對象

<!--
    Created by wangyang on 2019-05-16.
    [email protected]
    http://www.itwangyang.xyz
-->
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=Edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
	<meta name="apple-mobile-web-app-capable" content="yes">
	<meta name="apple-mobile-web-app-status-bar-style" content="black">
	<meta name="format-detection" content="telephone=no">
	<title>Title</title>
	<meta name="description" content="">
	<meta name="keywords" content="">
	<!--所有的IE都起作用:-->
	<!--[if IE]>
	<link rel="stylesheet" type="text/css" href="all-ie-only.css/> <![endif]-->

</head>
<body>

<script>
  /**
   * 數據類型,----值類型:數字、字符串、布爾值、null、undefined
   * 複雜數據類型:引用類型,對象:數組、函數、正則表達式、Date
   */

  //變量沒有聲明過,--》如果獲取變量的值,是會產生語法錯誤的    console.log(a);

  /**
   * undefined場景:
   * 1.一個變量聲明瞭,但是沒有賦值    var a; console.log(a);
   * 2.一個變量聲明瞭,並且賦值了一個undefined的值,var b = undefined;  console.log(b);
   * 3.一個對象中,獲取某個不存在的屬性的值  var c ={};console.log(c.name);
   */


  /**
   * 面向對象:
   * 對象是鍵值對的集合:對象是由屬性和方法構成的;
   */

  /**
   * 對象屬性的操作:
   * 1.  第一種方式:.語法
   * student.name
   * 缺陷:後面不能使用js中的關鍵字、保留字(class/this/function/)
   *      後面不能使用數字的
   *
   *
   * 2.  第二種:使用更廣,推薦
   * student["name"];  等價於  student.name
   *
   *設置屬性:
   * student ["gender"] = "男";
   *
   *
   * 刪除屬性:
   * delete student["gender"]
   *
   */

    //1.創建對象
  const Person = {
      gender: '男',
      height: 185,
      toShanghai: function () {
        console.log('做🚄去上海');
      }
    };
  //2.獲取屬性
  console.log(Person.gender);
  console.log(Person['height']);
  //3.設置屬性
  Person.address = "上海市閘北區";
  Person.height = 180;
  //4.刪除屬性
  delete Person.gender;
  delete Person.a;
  //5.清空對象
  Person = {};  //Person對象不再具有任何屬性
  Person = null; //表示將Person變量的值賦爲null,從此以後person不再是一個對象了



</script>
</body>
</html>

1.構造函數

<!--
    Created by wangyang on 2019-05-16.
    [email protected]
    http://www.itwangyang.xyz
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="format-detection" content="telephone=no">
  <title>Title</title>
  <meta name="description" content="">
  <meta name="keywords" content="">
  <!--所有的IE都起作用:-->
  <!--[if IE]>
  <link rel="stylesheet" type="text/css" href="all-ie-only.css/> <![endif]-->

</head>
<body>


<script>
  function Person(age, gender) {
    this.age = age;
    this.gender = gender;

    //此時的內存依然浪費了---》原型
    this.syaHello = function () {

    }
  }

  //Person是p1的構造函數
  let p1 = new Person(5, '女');
  let p2 = new Person(51, '女');
  let p3 = new Person(52, '女');
  let p4 = new Person(15, '女');
  let p5 = new Person(25, '女');
  console.log(p1, p2, p3, p4, p5);
</script>
</body>
</html>

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>
</head>
<body>
    
</body>
<script>
    //1、
    function _fn(){
        return 5;
    }
    var s=new _fn();
    //s是_fn構造函數的實例

    //2、
    function _fn3(){}
    var q1=new _fn3();
    //q1是_fn3構造函數的實例

    //3、
    function fn3(){
        return [1,3,5]; 
    }
    var qw=new fn3();//qw=[1,3,5]
    console.log(qw);
    //qw不是fn3構造函數的實例

    //如何判斷一個數據是否是複雜數據類型?
    //使用排除法:
    //a、看它的值是不是:數字、字符串、布爾值、null、undefined,
    //b、如果不是以上5種值,那就是複雜數據類型

    //  舉例:
    // [1,3,5]
    //  /abc/
    //  function(){}
    //  new Object();


    //爲什麼要理解構造函數的返回值?
    //String是一個內置函數
    //a、String()
    //b、new String()
    
    //結論:一個函數通過new調用,或者不通過new調用,很多時候會有截然不同的返回值

    //我如何分辨出一個對象到底是不是某個構造函數的實例?
    //a、var isTrue=xxx instanceof Person

    function Person(){

    }
    var p1=new Person();
    console.log(p1 instanceof Person);//true,就是Person的實例

    function Student(){
        return 100;
    }
    var s1=new Student();
    console.log(s1 instanceof Student);//true,就是Student的實例

    function Programmer(){
        return [1,3,5]
    }
    var pro=new Programmer();//pro並不是Programmer的實例
    console.log(pro instanceof Programmer);//false
    
    console.log("是數組的實例嗎?",pro instanceof Array);//true

    //小技巧:如何通過肉眼識別xxx對象時哪個構造函數的實例?
    //xxx.__proto__屬性,也是對象,該對象中一般都會有一個constructor屬性,這個只想PPP函數,那麼就可以認爲:xxx是PPP構造函數的實例


    //typeof運算符,只能判斷:數字、字符串、布爾值、undefined、函數

    //切記:千萬不能使用typeof運算符來判斷對象的構造函數

    //typeof null === "object"
    //typeof {}  === "object"
    //typeof []  === "object"
    //typeof function(){} === "function"
    //typeof /abc/     === "object"

</script>
</html>

3.爲什麼要繼承? 

<!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>
</head>
<body>
    
</body>
<script>
    function Person(name,age){
        this.name=name;
        this.age=age;
        this.say=function(){}
    }
    var p1=new Person();
    var p2=new Person();
    
    //p1對象和p2對象的say方法是否是同一個方法:false
    console.log(p1.say===p2.say);

    //由於say方法可能功能相似,但是不是同一個方法(沒有指向同一塊內存,會造成內存浪費)
    //解決方案:把say方法寫在他們共同的(父對象)中
    //其實他們共同的父對象,就可以通過:Person.prototype來獲取

    //-->只要把say方法寫在Person.prototype中,那麼say方法就是同一個方法
    Person.prototype.run=function(){
        console.log('時速500KM');
    }
    //此時p1和p2都可以訪問到run方法
    p1.run();
    p2.run();
    //驗證p1.run和p2.run是否是同一個方法?
    console.log(p1.run == p2.run); //指向同一個方法,這種方法避免了內存的浪費
    
    console.log(p1.run == Person.prototype.run);
    //true

    var p3=new Person();
    console.log(p3.run == p1.run); //true
    console.log(p3.run === p1.run);//true
    //結論:只要往某個構造函數的prototype對象中添加某個屬性、方法,那麼這樣的屬性、方法都可以被所有的構造函數的實例所共享
    //==>這裏的【構造函數的prototype對象】稱之爲原型對象
    //  Person.prototype是 p1 p2 p3 的原型對象
    //  Person.prototype是Person構造函數的【實例】的原型對象

    //猜猜看?
    //  Person的原型對象是誰呢?
    //  -->首先要知道Person的構造函數:-->Function
    //  -->所以Person的原型對象是:Function.prototype

    //  p1的原型對象是誰呢?
    //  -->首先要知道p1是誰創建的?    -->Person
    //  -->所以p1的原型對象時:     Person.prototype

</script>
</html>

 

4.繼承

<!--
    Created by wangyang on 2019-05-17.
    [email protected]
    http://www.itwangyang.xyz
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="format-detection" content="telephone=no">
  <title>Title</title>
  <meta name="description" content="">
  <meta name="keywords" content="">
  <!--所有的IE都起作用:-->
  <!--[if IE]>  <link rel="stylesheet" type="text/css" href="all-ie-only.css/> <![endif]-->

</head>
<body>



<script>
  function Person(name,age) {
    this.name = name;
    this.age = age;
    this.sayHello = function () {

    };
  }
  let p1 = new Person();
  let p2 = new Person();
  console.log(p1.sayHello === p2.sayHello);//false
  //沒有指向同一塊內存,會造成內存浪費;解決方案:吧sayHello寫在他們共同的(父對象)中

  //其實他們共同的父對象,就可以通過Person.prototype來獲取,就可以指向同一塊內存中

  Person.prototype.run = function () {
    console.log('hha');
  };
  p1.run();
  p2.run();
  console.log(p1.run === p2.run);//true

  //結論:只要往某個構造函數的prototype對象中添加某個屬性,方法,那麼這樣的屬性,方法都可以被所有的構造函數的實例所共享

  //----》在這裏,【構造函數的prototype對象】稱爲原型對象

  /**
   * Person的原型對象是誰?
   * ---》手寫要知道Person的構造函數:-----》Function
   * --->所有Person的原型對象是:Function.prototype
   *
   */




  //繼承第一種方式
  function Tiger() {
    
  }
  
  Tiger.prototype = {
    sayHello:function () {

    },
    eat:function () {
      
    }
  };
  let tiger = new Tiger();
  console.log(tiger.sayHello);
  console.log(tiger.eat);
  //缺點:添加1,2個無所謂,但是如果太多了,內存浪費

 


   //繼承的第一種方式:原型鏈繼承1
  /**  
   *  Person.prototype.say=function(){
   *    console.log("你好")
   * }
   缺點:添加1、2個方法無所謂,但是如果方法很多會導致過多的代碼冗餘
  
  
  
  // 繼承的第二種方式:原型鏈繼承2

   Person.prototype={
    constructor:Person,
    say:function(){
      console.log("你好");
    },
    run:function(){
      console.log("正在進行百米衝刺");
    }
  }
   注意點:
   a、一般情況下,應該先改變原型對象,再創建對象
   b、一般情況下,對於新原型,會添加一個constructor屬性,從而不破壞原有的原型對象的結構
   
   
   //繼承的第三種方式:拷貝繼承(混入繼承)

   場景:有時候想使用某個對象中的屬性,但是又不能直接修改它,於是就可以創建一個該對象的拷貝
   實現1: js var source={name:"李白",age:15} var target={}; target.name=source.name target.age=source.age;

   上面的方式很明顯無法重用,實際代碼編寫過程中,很多時候都會使用拷貝繼承的方式,所以爲了重用,可以編寫一個函數把他們封裝起來: js function extend(target,source){ for(key in source){ target[key]=source[key]; } return target; } extend(target,source)

   由於拷貝繼承在實際開發中使用場景非常多,所以很多庫都對此有了實現

   jquery:$.extend
   es6中有了對象擴展運算符彷彿就是專門爲了拷貝繼承而生: js var source={name:"李白",age:15} var target={ ...source }

   //繼承的第四種方式:原型式繼承

   場景:
   創建一個純潔的對象
   創建一個繼承自某個父對象的子對象
   使用方式:
   - 空對象:Object.create(null)
   var o1={ say:function(){} }
   var o2=Object.create(o1);
   繼承的第五種方式:借用構造函數實現繼承

   場景:適用於2種構造函數之間邏輯有相似的情況
   function Animal(name){ this.name=name; } function Person(name,age){ this.name=name; this.age=age; }

   以上代碼用借用構造函數實現 js function Animal(name,age){ this.name=name; this.age=age; } function Person(name,age,address){ Animal.call(this,name); //this.name=name; //this.age=age; this.address=address; }


   * 
   */
 





</script>
</body>
</html>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章