js面向對象編程讀書筆記

面向對象編程OOP object oriented object

對象:
實質上的事物、可以是任何的東西 包含屬性和方法

把部分對象的的共同特征的抽象
封裝
封裝的內容一般有:1、相關的數據 2、基於這些數據的方法
封裝的結果就是暴露給其他對象一個操作這些數據的方法
聚合
將一個復雜的對象分解為多個簡單的對象
繼承
優雅的實現代碼的重用,但是js中沒有具體的類,所以有繼承關系的隻能是對象之間
多態
worker繼承瞭person的全部的方法,這就意味這在這個類中實現瞭不同的talk()方法,當各自類生成的對象調用這個talk方法的時候,會實現各自的功能,這個現象就是多態。

js的基本5大數據類型

數字、字符串、佈爾值、undefined、null
undefined和null的區別
js試圖訪問某個不存在的或者是沒有經過賦值的變量,就會得到一個undefined
1+undefined undefined
1+null 1

函數

什麼是函數:代碼的一種分組形式。
參數:傳遞給函數多餘的參數,函數會自動省略掉沒有用的參數,可以利用函數的滋生的屬性arguments這個數組來訪問參數
預定義函數
js中已經預定瞭一些函數,parseInt()、parseFloat()、idNaN()。。。。。
isNaN() 判斷一個值是否是可以參與運算的數字
encodeURI
eval()
變量作用域
js中不能為變量定義特定的塊級作用域,但是可以為其定制特定的函數域
如果變量是定義在某個函數中,在函數之外這個變量是不可見的
如水果是定義在if或者for語句中,這個變量在外界是可見的
匿名函數
匿名函數的作用:
1、可以將匿名函數作為參數傳遞給其他的函數,接收方就能利用我們所傳遞的函數來完成某些事情
2、定義某個匿名函數來完成某些一次性的事情
回調函數
將函數作為參數傳遞給另一個函數,可以理解為一個函數調用操作交給瞭另一個函數
自調函數
在一個函數後邊加一個()就會自己調用一邊
內部函數
想要函數A裡邊有一個函數B當調用函數A的時候B也會被調用,必須在函數A定義的時候B能夠被調用。
函數和函數對象的區別

    var aa=new function (){this.a="fun"};
     var cc=function (){this.a="ccFun"};
     console.log(typeof cc);
     console.log(typeof aa);

aa是一個函數 cc是一個函數對象 call方法切換的對象的上下文。
call方法和apply方法
call方法(obj,arg1,arg2)
這個方法可以代替另一對象obj調用一個方法。 將一個函數的對象上下文從初始的上下文改變為this.obj的上下文,

  var aa=new function (){this.a="fun"};
    function  bb(x) {
        var a="myfun";
        console.log(this.a);
        console.log(x);
    }
    bb.call(aa,3);    //fun   3

調用bb的時候函數的上下文被切換到aa中,所以a被賦值為fun
apply方法(obj,[arguments])和call的功能一樣但是傳遞的參數是不一樣的

    var aa={
         a:"12121"
     };
    function  bb(x) {
        var a="myfun";
        console.log(this.a);
        console.log(x);
    }
    bb.apply(aa,[3]);   //12121  3

調用call和apply以後將obj的所有的屬性和方法都給瞭調用的這個

 var base={
      member:'qq',
      xx:function(){
      console.log(111);
  }
  }
    function extend() {
        console.log(this.member);
        console.log(this.xx);
    }
    extend.call(base);

上面的程序和下面的程序的運行效果一致

   function base() {
        this.member='qq';
        this.xx=function(){
            console.log(111);
        }
    }
    function extend() {
        base.call(this);   //extend會繼承base所有的屬性和方法
        console.log(this.member);
        console.log(this.xx);
    }
    extend();

閉包

通過閉包可以突破js的函數作用域鏈:函數內部定義一個函數返回一個函數內部的變量 外部就可以訪問這個變量

 var n;
     var aa=function (){
         var b="cc";
         n=function() {
            return b;
        }
     }
    aa();   /aa必須先執行一遍
    console.log(n());   //將b升級成瞭全局的變量,突破瞭js的作用域鏈

一個函數需要在其父級函數之後留住對父級作用域鏈的話,此時就是用閉包。。
循環中的閉包

     var aa=function (){
        var a=[];
         var i;
         for(i=0;i<3;i++){
             a[i]=function () {
                 return i;
             }
         }
         return a;
     }
    var xx=aa();
    console.log(xx[0]());
    console.log(xx[1]());
    console.log(xx[2]());

沒有輸出對應的1 2 3
下面的這個程序是正確的

     var aa=function (){
        var a=[];
         var i;
         for(i=0;i<3;i++){
             a[i]=(function (x) {
                 return function(){
                     return x;
                 };
             })(i)
         }
         return a;
     }
    var xx=aa();
    console.log(xx[0]());
    console.log(xx[1]());
    console.log(xx[2]());

會輸出 0, 1 ,2
定義一個將變量本地化的函數MakeLocal()

 function makeLocal(x){
        return function (){
            return x;
        }
    }
     var aa=function (){
        var a=[];
         var i;
         for(i=0;i<3;i++){
             a[i]=makeLocal(i)
         }
         return a;
     }
    var xx=aa();
    console.log(xx[0]());
    console.log(xx[1]());
    console.log(xx[2]());

依然會輸出0,1,2
設置secret變量的set和get的操作,這樣secret變量就不可被直接訪問

  var getValue,setValue;
  (function(){
      var secret=0;
     getValue=function (){
          return secret;
      };
      setValue=function (v){
          secret=v;
      }
  })();
    setValue(3);
    console.log(getValue());   //3

練習:
1、編寫一個將16進制的字符串轉換為顏色 將0000FF轉換為0,0,255

function ge(value1,value2){
         var result=Math.pow(16,0)*value1+Math.pow(16,1)*value2;
         return result;
     }
     function getRGB(jinzhi){
         var zu=[];
         for(var i=0;i

對象

構造函數,通過constructor能夠得到構造器屬性

   function A(name){
       this.name=name;
   }
    var aa=new A('juan');
    console.log(aa.constructor)     //得到function  A這個函數

instanceof操作符
判斷某個對象是不是某個類的實例

   function A(name){
       this.name=name;
   }
    var aa=new A('juan');
    console.log(aa instanceof A);

傳遞對象
當我們拷貝某個對象或者將它傳遞給一個函數的時候,其實就是傳遞這個對象的引用,對這個對象的操作都會實質性的改變這個對象。
對象之間的比較
當我們對對象進行比較的時候,當且僅當兩個引用指向同一個對象時為true;

var A={name:1};
    var B={name:1};
    console.log(A==B)   //false

兩個對象的引用不同,所以輸出為false

內置對象

數據封裝類對象 Object Array Boolean Number String
工具類對象 Math Date RegExp
錯誤類對象
Object
所有對象的父級對象
屬性 constructor 返回構造屬性 所有的構造屬性都是隻讀的,不能被改變
方法 1、tostring() 2、valueOf()

    var A=[1,3,4]
    console.log(A.valueOf())  //[1, 3, 4]
    console.log(A.toString())  //1,3,4

Array
屬性 length
方法 sort ,reverse,join,slice,push,splice
Function()
屬性
length 參數的個數
caller 會返回一個調用該函數對象的外層函數的引用

  function A(){
      console.log(A.caller) ;
  }
    function B(){
        return A();
    }
   B();   //function B()

在B中執行A的結果就是返回A的caller(也就是B)

原型

每一個對象都有自己的原型

 function A(){
      console.log(1);
  }
   console.log(A.prototype)  //object

通過函數的prototype屬性來為函數添加屬性和方法

  function A(name){
      this.name=name;
      this.say=function () {
          console.log(this.name);
      }
  }
  var aa=new A('juanjuan');
    aa.say();
  A.prototype.www=function () {
      console.log(111)
  }
  var bb=new A('juanjuan');
  bb.www();

但是隻限於添加,並不能改變原來的function中的方法,想要改變原來的對象的方法,可以先將原來對象的方法刪除。再在原型上添加方法,這樣就實現瞭改變原來對象方法的功能

var yuan=function (){
        this.say=function (x) {
            console.log(x);
        }
    }
    yuan.prototype.say=function (x) {
        console.log(x+2)
    }
    var yuan1=new yuan();
    yuan1.say(2);
    yuan.prototype.say1=function (x) {
        console.log(x+2)
    }
    yuan1.say1(2);

而且原型可以隨時被修改,並且與之相關的對象都會隨之改變。
原型隻能向對象添加方法,但是不能改變原來的方法。這是因為如果遇上對的自身屬性與原型屬性同名的時候 自身屬性的優先級高於原型屬性 如果刪除瞭自身的同名屬性,原型上的這個同名屬性就會出現

  var yuan=function (){
        this.say=function (x) {
            console.log(x);
        }
    }
    var yuan1=new yuan();
    delete yuan1.say;
    yuan.prototype.say=function (x) {
        console.log(x+2)
    }
    yuan1.say(2);

自身屬性和原型屬性
通過hasOwnProperty來判斷

var yuan=function (name,age){
        this.name=name;
        this.age=age;
    }
    yuan.prototype={
        price:100,
        rating:3
    }
    var yuan1=new yuan("趙文娟",12);
    for(var cc in yuan1){
        console.log(cc+yuan1[cc]);  //誰輸出自身和原型的所有的屬性
    }
console.log(yuan1.hasOwnProperty("name"));   //true   自身有著個屬性
console.log(yuan1.hasOwnProperty("price"));   //false  自身沒有這個屬性

當前對象是否是另一個對象的原型
isPrototypeOf()函數

    var monkey=function (name,age){
        this.name=name;
        this.age=age;
    }
    var human=function(){}
    human.prototype=monkey;
    var hh=new human();
    console.log(monkey.isPrototypeOf(hh));   //monkey是人類這個對象的原型

神秘的proto連接
我們知道當訪問一個在當前對象中不存在屬性的時候,原型就會被納入查找的范圍。這個屬性就可以指向它的原型,

var monkey=function (){
        this.feeds='香蕉';
        this.breaths='空氣';
    }
    var human=function(){}
    var mon=new monkey();
    human.prototype=mon;
    var hh=new human();
    hh.name="juanjuan";
    console.log(hh.feeds);  //香蕉
    console.log(mon.isPrototypeOf(hh));   //true
 console.log(hh.__proto__);  //monkey {feeds: "香蕉", breaths: "空氣"}
    console.log(hh.prototype);   //undefined

繼承

js中每一個函數都有一個名為prototype的對象的屬性 new的時候會創建出一個一個對象,並且通過proto指針來連接原型,這樣就形成瞭原型鏈
這裡寫圖片描述
通過原型鏈實現js 中的繼承

  function  shape(){
             this.name='shape';
             this.toString=function (){
                 return this.name;
             }
         }
       function TwoDshape(){
           this.name='2Dshape'
       }
       function Trigle(side,height){
           this.name='Trigle';
           this.side=side;
           this.height=height;
           this.getArea=function(){
               return this.side*this.height/2;
           }
       }
         //對對象的prototype屬性重寫
         TwoDshape.prototype=new shape();
         Trigle.prototype=new TwoDshape();
//對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以
         TwoDshape.prototype.constructor=TwoDshape;
         Trigle.prototype.constructor=Trigle;
        var tt=new Trigle(3,4);
         console.log(tt.toString());    //Trigle
         console.log(tt.getArea())   //6

演示繼承的效果 一定要註意的是對prototype完全覆蓋以後,會對constructor有影響,所以需要

    TwoDshape.prototype.constructor=TwoDshape;
         Trigle.prototype.constructor=Trigle;

以上的實驗結果的分析:
遍歷tt對象中的所有屬性。沒有找到一個叫做toString 的方法。就查看proto所連接的實體,也就是回去TwoDshape的new出來的實體中去找,也沒有找到,就會去shape的new的實體找toString這個方法 。同時我們也在原型覆蓋之後重新定義瞭constructor這個屬性,所以TwoDshape和Trigle都是本身

    console.log(tt instanceof shape);   //true
      console.log(tt instanceof TwoDshape);   //true
       console.log(shape.prototype.isPrototypeOf(tt))  //true

將需要共享的屬性都遷移到原型中
當使用構造創建的屬性,每一次的new都會在實體中的多一個這樣的屬性。所以將共享的屬性和方法遷移到原型中是有必要的。

   function  shape(){}
         shape.prototype={
            name:'shape',
             toString:function (){
             return this.name;
         }
         }
       function TwoDshape(){
       }
         TwoDshape.prototype=new shape();
         TwoDshape.prototype.constructor=TwoDshape;
       function Trigle(side,height){
           this.side=side;      //因為這個屬性是每一個實體獨有的屬性
           this.height=height;   //因為這個屬性是每一個實體獨有的屬性
       }
         //對對象的prototype屬性重寫
         Trigle.prototype=new TwoDshape();
         //對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以
         Trigle.prototype.constructor=Trigle;
         Trigle.prototype.getArea=function(){
             return this.side*this.height/2
         }
        var tt=new Trigle(3,4);
         console.log(tt.toString());    //Trigle
         console.log(tt.getArea())   //6
      console.log(tt instanceof shape);   //true
      console.log(tt instanceof TwoDshape);   //true
       console.log(tt.hasOwnProperty('name'))  //false
       console.log(tt.hasOwnProperty('height'))  //true

值得註意的是擴展原型對象之前,一定要完成繼承關系的構建
隻繼承原型
盡可能的將重用的屬性和方法添加到原型中,這樣就通過原型鏈就實現瞭繼承,不再需要shape的實體來實現繼承關系。

   function  shape(){}
         shape.prototype={
            name:'shape',
             toString:function (){
             return this.name;
         }
         }
       function TwoDshape(){
       }
         TwoDshape.prototype=shape.prototype;
         TwoDshape.prototype.constructor=TwoDshape;
       function Trigle(side,height){
           this.side=side;      //因為這個屬性是每一個實體獨有的屬性
           this.height=height;   //因為這個屬性是每一個實體獨有的屬性
       }
         //對對象的prototype屬性重寫
         Trigle.prototype=TwoDshape.prototype;
         //對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以
         Trigle.prototype.constructor=Trigle;
         Trigle.prototype.getArea=function(){
             return this.side*this.height/2
         }
        var tt=new Trigle(3,4);
         console.log(tt.toString());    //Trigle
         console.log(tt.getArea())   //6
      console.log(tt instanceof shape);   //true
      console.log(tt instanceof TwoDshape);   //true
       console.log(tt.hasOwnProperty('name'))  //false
       console.log(tt.hasOwnProperty('height'))  //true

測試的結果和上面的一致,但是js在找toString的時候,直接找的是原型
子對象訪問父對象
js中本身沒有這樣的屬性,但是可以通過子類調用父類的同名方法來實現

   function  shape(){}
         shape.prototype={
            name:'shape',
             toString:function (){
               var result=[];
                 if(this.constructor.urber){
                     result[result.length]=this.constructor.urber.toString();
                 }
                 result[result.length]=this.name;
                 return result.join(',')+"11";
         }
         }
       function TwoDshape(){
       }
         function F(){};
         F.prototype=shape.prototype;
         TwoDshape.prototype=new F();
         TwoDshape.prototype.constructor=TwoDshape;
         TwoDshape.urber=shape.prototype;
         TwoDshape.prototype.name='2D圖形';
       function Trigle(side,height){
           this.side=side;      //因為這個屬性是每一個實體獨有的屬性
           this.height=height;   //因為這個屬性是每一個實體獨有的屬性
       }
         //對對象的prototype屬性重寫
         var F=function(){};
         F.prototype=TwoDshape.prototype;
         Trigle.prototype=new F();
         //對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以
         Trigle.prototype.constructor=Trigle;
         Trigle.urber=TwoDshape.prototype;
         Trigle.prototype.getArea=function(){
             return this.side*this.height/2
         }
         Trigle.prototype.name="三角形";
        var tt=new Trigle(3,4);
         console.log(tt.toString());    //shape11,2D圖形11,三角形11

改進瞭toString 用urber屬性指向瞭父類的原型
將繼承的部分封裝

   function extend(child,parent) {
        var F=function () {};
        F.prototype=parent.prototype;
        child.prototype=new F();
        child.prototype.constructor=child;
        child.uber=parent.prototype;   //實現子類繼承父類的屬性
    }
    function  shape(){}
         shape.prototype={
            name:'shape',
             toString:function (){
               var result=[];
                 if(this.constructor.uber){
                     result[result.length]=this.constructor.uber.toString();
                 }
                 result[result.length]=this.name;
                 return result.join(',')+"11";
         }
         }
       function TwoDshape(){}
         extend(TwoDshape,shape);
         TwoDshape.prototype.name='2D圖形';
       function Trigle(side,height){
           this.side=side;      //因為這個屬性是每一個實體獨有的屬性
           this.height=height;   //因為這個屬性是每一個實體獨有的屬性
       }
       extend(Trigle,TwoDshape);
         Trigle.prototype.getArea=function(){
             return this.side*this.height/2
         }
         Trigle.prototype.name="三角形";
        var tt=new Trigle(3,4);
         console.log(tt.toString());    //shape11,2D圖形11,三角形11

對象之間的繼承 淺拷貝
js中沒有類 丟掉構造函數來實現繼承 就是多個屬性的拷貝
使用多個屬性的拷貝實現瞭類之間的繼承,中間沒有使用函數,js就實現瞭類之間的繼承

  function extendCopy(p) {
          var c={};
          for(var i in p){
              c[i]=p[i];
          }
          c.uber=p;
          return c;
      }
         var shape={
            name:'圖形',
             toString:function (){
                return this.name;
             }
         }
       var twoDee=extendCopy(shape);
      twoDee.name='2D圖形';
      twoDee.toString=function(){
          return this.uber.toString()+this.name;
      }
       var trigle=extendCopy(twoDee);
      trigle.name="三角形";
      trigle.toString=function(){
          return this.uber.toString()+this.name;
      }
      trigle.side=3;
      trigle.height=4;
      trigle.getArea=function(){
          return this.height*this.side/2
      }
         console.log(trigle.toString());    //圖形2D圖形三角形
         console.log(trigle.getArea());    //6

深拷貝
之前的extendCopy實現 的是淺拷貝,要想實現深拷貝實際上遇到一個對象引用性的屬性的時候,我們需要再次調用深拷貝函數
實現深拷貝

  function deepCopy(p,c) {
        var c=c||{};
        for(var i in p){
            if(typeof p[i]=="object"){
                c[i]=(typeof p[i===Array])?[]:{};
                deepCopy(p[i],c[i])   //實際上是一個遞歸實現的深拷貝
            }
            else{
                c[i]=p[i];
            }
        }
        return c;
    }

比較深拷貝和淺拷貝

 function deepCopy(p,c) {
        var c=c||{};
        for(var i in p){
            if(typeof p[i]=="object"){
                c[i]=(typeof p[i===Array])?[]:{};
                deepCopy(p[i],c[i])   //實際上是一個遞歸實現的深拷貝
            }
            else{
                c[i]=p[i];
            }
        }
        return c;
    }
    function extendCopy(p) {
        var c={};
        for(var i in p){
            c[i]=p[i];
        }
        c.uber=p;
        return c;
    }
     var p={
         numbers:[1,2,3],
         letters:['a','b','c'],
         obj:{
             name:'zhaowenjuan'
         },
         boolean:true
     }
       var test=deepCopy(p);
       test.numbers.push(3,4);
       console.log(test.numbers);   //1,2,3,4
       console.log(p.numbers);   //1,2,3   發生的是深拷貝  備份與原來相互獨立
        var test2=extendCopy(p);
      test2.numbers.push(3,4);
    console.log(test2.numbers);   //1,2,3,4
console.log(p.numbers);     //1,2,3,4    發生的是淺拷貝    備份與原來是一個

發佈留言