五大模式(原型模式)

單例模式

  • 把描述同一個事物特徵的信息進行分類歸組,放到同一個命名空間下(減少全局變量的污染)
        var name = "邢";
        var age = 21;
        var sex = "男";
        
        var name = "劉";
        var age = 21;
        var sex = "男";
        
		//
        var xing = {
            name: "邢",
            age: 21,
            sex: "男"
        };
        var liu = {
            name: "劉",
            age: 21,
            sex: "男"
        };
        //描述同一個對象的所有屬性放在對象中,避免了全局變量的干擾,"單例模式"
        var timer = 1;
        var xing = {
            bar: function () {

            },
            fn: function () {

            }
        };
        // var timer = 1;
        var liu = {
            bar: function () {

            }
        };  
-----------------------------------------------------
			 var name = 'erYa';
       var age = 18;

       var name = 'jinYu';
       var age = 22;

       let person1 = {
           name: 'erYa',
           age:18
       };
       let person2 = {
           name: 'jinYu',
           age: 22
       }

高級單例模式

        //高級單例模式
        //jquery的源碼的外層是採用了高級單例模式
        var utils = (function () {
            var timer = 1;
            var num = 100;
            return {
                bar: function () {

                },
                fn: function () {

                }
            }
        })()
        //與單例模式相比,想使用同一變量,可以避免污染全局
--------------------------------------------------
		let person1 = (function(){
        let fn = function(){};
        let name= 'erYa';
        return {
            name:name,
            age:18,
            fn:fn
        }
    })()


        let person2 = (function(){
        let fn = function(){};
        let name= 'jinYu';
        let age = 18;
        return {
            name:name,
   
            fn:fn
        }
    })()

    let person3 = function(){
        let name= 'jinYu';
        let age = 26
        return {
            name:name,
            fn:fn
        }
    }
    console.log(person3) // 這是一個函數,不是person3的個人信息

工廠模式

  • 如果用單例模式去寫很多個person就會變得很麻煩,就有了工廠模式 特點:批量生產
  • 把實現相同功能的代碼封裝到函數裏,以後想運行這個方法,就直接執行這個函數就好了
  • 高內聚:提高代碼的複用率
  • 低耦合:減少頁面的重複代碼
        //工廠模式: 函數的封裝
        function createStudent(name, age) {
            var obj = {}
            obj.name = name;
            obj.age = age;
            return obj;
        };
        var xing = createStudent('邢', 21);
        var liu = createStudent('劉', 21);

對象前言

javaScript
面向對象: 基於對象
對象: 萬物皆對象
類: 指具有相同特徵的一類事物
實例:具體的一個事物:

  • 內置類 : Number String Boolean Null Undefined Object Array RegExp Date …
  • 類一定是個函數,但函數不一定是類;

面向對象

  • 把抽象的對象按照特點進行分類(大類/小類),把類的公共特徵進行提取和封裝,放到對應的類別中
  • 類就是對對象的一種細分,和公共部分的抽取
  • 在類中具體派生出來的具體事物就是類的實例,而且實例擁有自己私有的特徵,還擁有所屬類上的特徵
  • 我們研究面向對象,其實就是研究對象、類、實例之間的關係和各自的知識點

在這裏插入圖片描述

構造函數模式(構造自定義類)

構造函數解決了實例的私有屬性

  • var f=new Fn();
  • new 操作符——把 new 放在函數的前面,函數執行
  • 構造函數就是讓我們創建自定義類
  • new 函數執行 叫做構造函數運行模式,此時的Fn就是Fn類(構造函數),函數執行之後的返回結果就是一個對象,叫做實例對象(f就是Fn的實例)
  • 類就是函數數據類型的
  • 實例是對象數據類型
  • 構造函數中的this指向當前實例
    • 如果這個函數需要參數,那麼這個需要有小括號,如果不需要參數,那麼小括號可以省略
  • new後面的函數就是構造函數,也叫類;那麼通過new函數得到的返回值就叫實例;實例是構造函數new出來的;

> 鉤子函數:在初始化實例時會默認調用原型上的一些方法,那麼這些方法就是鉤子函數

運行原理

  • 1、在代碼執行之前,函數中會首先默認創建一個空對象 {}
  • 2、讓當前函數裏的this指向這個對象
  • 3、代碼執行
  • 4、默認return 這個對象

let f1 = new Fn(‘erYa’, 18);
//原型三句話
//f1就是Fn的實例
//fn就是函數,也是自定義(Fn)類
//f1就是實例化的對象

//let f3 = new Fn;
如果構造函數不傳實參,可以省略執行小括號
new 函數執行 叫做構造函數運行模式,此時的Fn就是Fn類(構造函數),函數執行之後的返回結果就是一個對象,叫做實例對象(f就是Fn的實例)

          function fn() {
            // var obj = {};
            // this = obj;
            this.name = 100;
            // return this;

        };
        var f = new fn;
        fn();
        console.log(f);

		/* 
       剛纔那些都是js的內置類,但是我們也可以自己去自定義一些類
       */

        //   構造函數(構造自定義類)

         function Fn(name, age) {
            /* 
            形成私有作用域
            形參賦值
            變量提升
            1、默認生成一個空對象 {}
            2、讓函數裏的this指向這個對象
            3、代碼執行
            4、默認return 這個對象
            */
            this.name = name; // 給this增加鍵值對
            this.age = age // 給this增加鍵值對
            this.say = function(){}
            return {}
        }

        // new: 他是js裏的關鍵字
        Fn() // 普通函數運行

        let f = new Fn()  // {}
        let f1 = new Fn('erYa', 18);
		//原型三句話
        //f1就是Fn的實例
    	//fn就是函數,也是自定義(Fn)類
		//f1就是實例化的對象
		
        let f2 = new Fn('jinYu', 18);
        let f3 = new Fn;//如果構造函數不傳實參,可以省略小括號
        let f4 = Fn;
        console.log(f)
        console.log(f1)
        console.log(f2)
        console.log(f1.age === f2.age)
        console.log(f1.say === f2.say)
        console.log(f3)
        console.log(f4)
        // new 函數執行 叫做構造函數運行模式,此時的Fn就是Fn類(構造函數),函數執行之後的返回結果就是一個對象,叫做實例對象(f就是Fn的實例)

構造函數和普通函數的不同

JS爲了區分構造函數和普通函數,一般將構造函數首字母大寫;

  • 運行上的不同
    • 普通函數–>形成私有作用域–>形參賦值–>變量提升–>代碼執行–>作用域是否銷燬
    • 構造函數–>形成私有作用域–>形參賦值–>變量提升–>默認生成一個對象–>把this指向這對象–>代碼執行–>默認把這個對象return出去–>作用域是否銷燬
  • 執行上的不同
    • 構造函數如果不傳實參,可以不加小括號
  • 構造函數如果手動return一個基本數據值,不能改變人家的返回值,但是手動return引用數據類型,可以改變構造函數的返回值,此時return的東西已經不是當前類的實例了【所以不要輕易修改構造函數的返回值】
        function Fn(n) {
            let m = 10;
            this.total = m + n;
            this.say = function () {
                console.log(this.total)
            }
        }
        let f1 = new Fn(10);
        let f2 = new Fn(20);
        console.log(f1.n)//undefined
        console.log(f2.m)//undefined
        console.log(f1.total)//20
        f2.say()//30
        console.log(f1 === f2)//false

創建實例的方式

  • 字面量創建實例的方式
		let num = 1;
		let str = 'w';
  • 構造函數創建實例的方式
        // 實例創建
        var num = new Number(1); 
        console.log(typeof num);// "object"
        console.log(num instanceof Number);//true
        console.log(num);//Number {1}
        console.log(num+1);//2
        console.log(num == 1 )//比較true,絕對比較就是false
        //  如果是數字並且只有一個參數,代表數組的length;如果大於等於兩個,那麼是數組的每一項;
        var  arr = new Array(100,200);
        console.log(arr);//[100, 200]
        console.log(typeof Array);// "function"
        var arr = new Array("a")
        console.log(arr);//["a"]  

------------------------------------------------------------------------
		let ss = new Number(1)
        console.log(ss)
        console.log((1).toFixed(2)) // '1.00'  把數字轉換爲字符串,保留指定位小數
        console.log(ss.toFixed(2)) // '1.00'
        let w = new String(3) // 創建一個字符串的實例
        console.log(w)
        console.log(w.substr) // f//.substr字符串裏的方法
        console.log(w instanceof String) // true

檢測當前實例是否屬於某個類

  • 使用instanceof是檢測當前實例是否屬於某個類
  • 實例 instanceof 類,如果實例是屬於這個類,那就返回true,反之就是false
  • 通過字面量方式創建的基本數據類型值不是一個標準的實例,不能使用instanceof 進行檢測;引用數據類型創建的就是一個標準的實例,可以使用instanceof來進行檢測;

侷限性

  • instanceof不能檢測基本數據類型,只能檢測引用數據類型的值
        // 字面量方式創建變量
		// var num = 1;// num 是Number的一個實例嗎?是
        // console.log(num  instanceof Number);// false
        // var  obj = {};
        // console.log(obj instanceof Object);// true
        // var ary = [];
        // console.log(ary instanceof Array);// true
--------------------------------------------
		function Fn(name, age){
            this.name = name;
            this.age = age;
        }
        let f1 = new Fn;
        console.log(f1 instanceof Fn) // true
        console.log(1 instanceof Fn) // false

給實例的類封裝公共方法需要注意的幾點

  • 你自己封裝的方法不能與人家內置的方法同名
  • 給你自己的方法加前綴
	//去重
   function myUnique(){
        console.log(this)
        let obj = {};
        for (var i = 0; i < this.length; i++) {
            if(obj[this[i]] !== undefined){
                this[i] = this[this.length-1];
                this.length--;
                i--;
                continue;
            }
            obj[this[i]] = this[i]
        }
    }
    Array.prototype.myUnique = myUnique;
    console.log(ary.myUnique().sort().reverse().slice(1,3))
    // 內置類的鏈式擴展:如果你想用鏈式調用,前提是你的方法的返回值必須是當前類的實例

原型模式

  • 原型模式: 構造函數解決了對象實例中私有屬性的問題,原有模式解決了對象實例中公有屬性的問題;如果將實例的私有屬性放到了公有屬性上,減少了堆內存的開闢;

原型解決了實例的公有屬性

  • 每一個函數(普通函數,構造函數)都天生自帶一個prototype屬性,屬性值是一個對象,它裏面存儲的是實例的公有屬性(原型)
  • 每一個原型都天生自帶一個constructor屬性,其屬性值指向當前原型所屬的類
  • 每一個對象都天生自帶一個__proto__屬性,其屬性值指向當前實例所屬類的原型
    在這裏插入圖片描述

原型鏈

  • 在對象裏查找-一個屬性,先看自己私有的有沒有,如果自己沒有,就通過__ proto__ 屬性找 到當前所屬類的原型上,如果原型上有,就直接用,如果沒有,繼續通過原型的__ proto__ 繼續往所屬類的原型上找,直到找到Object類的原型上找,如果還沒有,就是undefined, 這種級- 一級一級向上查找就會形成原型鏈
       function Fn(x, y) {
            this.x = x;
            this.y = y;
            this.getX = function () {
                console.log(this.x);
            }
        }

        Fn.prototype.getX = function () {
            console.log(this.x);
        }
        Fn.prototype.getY = function () {
            console.log(this.y);
        }
        var f1 = new Fn(100, 200);
        f1.__proto__.a = 300;
        var f2 = new Fn(100, 200);
        console.log(f1.getX == f2.getX);//false
        console.log(f1.getY == f2.getY);//true
        console.log(f1.x == f2.x);//true
        console.log(Fn.prototype.getX === f1.getX);//false
        console.log(Fn.prototype.getX === f1.__proto__.getX);//true
        f1.getX()//100
        Fn.prototype.getX();// undefined
        f2.__proto__.getX();//undefined
        Fn.prototype.getY();//undefined
        console.log(f1.a);//300
        console.log(f1.toString);//ƒ toString() { [native code] }

原型:

  • window 是全局作用域中一個大的對象;window是Object的一個實例;
  • Function是所有函數數據類型的基類(包括自己)
  • Object是所有對象的基類
  • 所有的函數都有prototype和__proto__屬性
  • Object的原型的__proto__執行的是自己,js認爲自己指向自己沒有意義,就規定爲null
  • 所有的函數數據類型(普通的函數、類【內置類,自定義類】)都是Function的一個實例,Function和Object都是Function的一個實例,那Object類的__proto__指向Function的原型
  • 所有函數都是Function的實例, 那Function也是函數, 那他的__proto__指向的是自己的原型。它的原型是一個匿名函數,但是也是對象,正常使用即可
    • 當函數作爲對象時,有length和name屬性
      • length:代表形參的個數,name:代表函數的名字
  • 如果一個對象你不知道誰構出來的, 那他的__proto__就指向Object內置類的原型( 所有類的原型都指向Object內置類的原型)
  • 函數的三種角色: 普通函數 構造函數 普通對象
  • 在正式場合函數是以函數的身份出場的( 這是人家的主角色)
  • 類的特點: 多態、 繼承和封裝
  • 給Fn的原型新增鍵值對;這就是原型擴展;

QQ圖片20191122182804.jpg

原型圖

  • 原型鏈:單向不可逆

原型圖.png

原型重定向

  • 內置類的原型不能被重定向
  • 可以覆蓋內置類原型上的方法
  • 用新的空間地址覆蓋Fn原有的空間地址;會導致constructor的丟失;
  • 重定向例子
        function Fn() {
            this.x = 100;
            this.y = 100;
        }
        Fn.prototype.getX = function () {
            console.log(this.x)
        }
        let f1 = new Fn;
        Fn.prototype = {
            getY: function () {
                console.log(this.y)
            }
        };
        let f2 = new Fn;
        
        console.log(f1.getX)
        console.log(f2.getX)
        console.log(f1.constructor)
        console.log(f2.constructor)
        console.log(Fn.prototype)

原型重定向.png

阿里面試題(原型鏈、運算符優先級)

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

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <script>
        function Foo() {
            getName = function () {
                console.log(1);
            };
            return this;
        }
        Foo.getName = function () {
            console.log(2);
        };
        Foo.prototype.getName = function () {
            console.log(3);
        };
        var getName = function () {
            console.log(4);
        };

        function getName() {
            console.log(5);
        }

        Foo.getName(); //2
        getName(); //5//4
        Foo().getName(); //2//1
        getName(); //4//1
        new Foo.getName(); //2
        new Foo().getName(); //1//3
        new new Foo().getName(); //1//3

        /* 
        1、成員訪問:尋找對象裏的屬性名所對應的屬性值就是成員訪問(19)
            Fn.aa
        2、new(帶參數列表):就是構造函數執行有括號(19)
        3、new(無參數列表):就是構造函數執行沒有括號(18)
        優先級一樣,從左到右運算
        
         */
    </script>
</body>

</html>

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