JavaScript--面向?qū)ο?/h1>

面向?qū)ο蠡靖拍?/h2>
  • 面向?qū)ο?Object Oriented,OO)是軟件開發(fā)方法
  • 面向?qū)ο笫且环N對(duì)現(xiàn)實(shí)世界抽象的理解,是計(jì)算機(jī)編程技術(shù)發(fā)展到一定階段后的產(chǎn)物
  • Object Oriented Programming-OOP ——面向?qū)ο缶幊?/p>


面向?qū)ο蠛兔嫦蜻^(guò)程區(qū)別

  • 面向過(guò)程
    • 強(qiáng)調(diào)的是功能行為
    • 關(guān)注的是解決問(wèn)題需要哪些步驟
  • 回想下前面我們完成一個(gè)需求的步驟:
    • 首先搞清楚我們要做什么
    • 然后分析怎么做
    • 最后用代碼體現(xiàn)
    • 一步一步去實(shí)現(xiàn),而具體的每一步都需要我們?nèi)?shí)現(xiàn)和操作
  • 在上面每一個(gè)具體步驟中我們都是參與者担神, 并且需要面對(duì)具體的每一個(gè)步驟和過(guò)程, 這就是面向過(guò)程最直接的體現(xiàn)

  • 面向?qū)ο笫腔诿嫦蜻^(guò)程而言

  • 面向?qū)ο蠛兔嫦蜻^(guò)程都是一種思想

  • 面向?qū)ο?/strong>

    • 將功能封裝進(jìn)對(duì)象喂饥,強(qiáng)調(diào)具備了功能的對(duì)象
    • 關(guān)注的是解決問(wèn)題需要哪些對(duì)象
  • 當(dāng)需求單一消约, 或者簡(jiǎn)單時(shí)肠鲫, 我們一步一步去操作沒問(wèn)題, 并且效率也挺高或粮。 可隨著需求的更改导饲, 功能的增加, 發(fā)現(xiàn)需要面對(duì)每一個(gè)步驟非常麻煩氯材, 這時(shí)就開始思索渣锦, 能不能把這些步驟和功能再進(jìn)行封裝, 封裝時(shí)根據(jù)不同的功能氢哮,進(jìn)行不同的封裝袋毙,功能類似的封裝在一起。這樣結(jié)構(gòu)就清晰多了冗尤, 用的時(shí)候听盖, 找到對(duì)應(yīng)的類就可以了胀溺, 這就是面向?qū)ο笏枷?/p>


  • 示例
  • 買電腦
    • 面向過(guò)程
      • 了解電腦
      • 了解自己的需求
      • 對(duì)比參數(shù)
      • 去電腦城
      • 砍價(jià),付錢
      • 買回電腦
      • 被坑
    • 面向?qū)ο?
      • 找班長(zhǎng)
      • 描述需求
      • 班長(zhǎng)把電腦買回來(lái)

  • 吃飯
    • 面向過(guò)程
      • 買菜
      • 摘菜
      • 洗菜
      • 切菜
      • 炒菜
      • 盛菜
    • 面向?qū)ο?
      • 去飯店
      • 點(diǎn)菜

  • 洗衣服
    • 面向過(guò)程
      • 脫衣服
      • 放進(jìn)盆里
      • 放洗衣液
      • 加水
      • 放衣服
      • 搓一搓
      • 清一清
      • 擰一擰
      • 曬起來(lái)
    • 面向?qū)ο?
      • 脫衣服
      • 打開洗衣機(jī)
      • 丟進(jìn)去
      • 一鍵洗衣烘干
    • 終極面向?qū)ο?
      • 買電腦/吃飯/洗衣服
      • 找個(gè)對(duì)象

面向?qū)ο蟮奶攸c(diǎn)

  • 是一種符合人們思考習(xí)慣的思想
  • 可以將復(fù)雜的事情簡(jiǎn)單化
  • 將程序員從執(zhí)行者轉(zhuǎn)換成了指揮者
  • 完成需求時(shí):
    • 先要去找具有所需的功能的對(duì)象來(lái)用
    • 如果該對(duì)象不存在皆看,那么創(chuàng)建一個(gè)具有所需功能的對(duì)象
    • 這樣簡(jiǎn)化開發(fā)并提高復(fù)用

類與對(duì)象的關(guān)系

  • 面向?qū)ο蟮暮诵木褪菍?duì)象,那怎么創(chuàng)建對(duì)象?
    • 現(xiàn)實(shí)生活中可以根據(jù)模板創(chuàng)建對(duì)象,編程語(yǔ)言也一樣,也必須先有一個(gè)模板,在這個(gè)模板中說(shuō)清楚將來(lái)創(chuàng)建出來(lái)的對(duì)象有哪些屬性行為
  • JavaScript中的類相當(dāng)于圖紙仓坞,用來(lái)描述一類事物。
  • JavaScript中可以自定義類, 但是也提供了一個(gè)默認(rèn)的類叫做Object

使用默認(rèn)類創(chuàng)建對(duì)象

  • 通過(guò) new Object() 創(chuàng)建對(duì)象
<script>
    // 1.使用默認(rèn)類創(chuàng)建一個(gè)空對(duì)象
    var obj = new Object()
    // 2.動(dòng)態(tài)給空對(duì)象新增屬性
    obj.name = "lnj";
    obj.age = 33;
    // 3.動(dòng)態(tài)給空對(duì)象新增方法
    obj.say = function () {
        console.log("hello");
    }
    // 4.使用對(duì)象的屬性和方法
    console.log(obj.name);
    console.log(obj.age);
    obj.say();
</script>
  • 通過(guò)字面量創(chuàng)建對(duì)象
<script>
    /*
    // 1.使用字面量創(chuàng)建對(duì)象
    var obj = {}; // 相當(dāng)于var obj = new Object()
    // 2.動(dòng)態(tài)給空對(duì)象新增屬性
    obj.name = "lnj";
    obj.age = 33;
    // 3.動(dòng)態(tài)給空對(duì)象新增方法
    obj.say = function () {
        console.log("hello");
    }
    */

    // 1.使用字面量創(chuàng)建對(duì)象
    var obj = {
        name : 'lnj',
        age: 33,
        say : function () {
            console.log("hello");
        }
    }
    // 2.使用對(duì)象的屬性和方法
    console.log(obj.name);
    console.log(obj.age);
    obj.say();
</script>
  • 使用工廠函數(shù)創(chuàng)建對(duì)象
    • 上面的創(chuàng)建方式, 沒多創(chuàng)建一個(gè)人都需要將代碼再寫一遍, 冗余代碼太多, 所以我們可以創(chuàng)建創(chuàng)建對(duì)象的代碼封裝到一個(gè)函數(shù)中
    • 專門用于創(chuàng)建對(duì)象的函數(shù)我們稱之為工廠函數(shù)
<script>
    function createPerson(name, age) {
        var obj = new Object();
        obj.name = name;
        obj.age = age;
        obj.say = function () {
            console.log("hello");
        }
        return obj;
    }
    var obj1 = createPerson("lnj", 33);
    var obj2 = createPerson("zq", 18);
    console.log(obj1);
    console.log(obj2);
</script>
<script>
    function createPerson(name, age) {
        var obj = {
            name: name,
            age: age,
            say: function () {
                console.log("hello");
            }
        }
        return obj;
    }
    var obj1 = createPerson("lnj", 33);
    var obj2 = createPerson("zq", 18);
    console.log(obj1);
    console.log(obj2);
</script>

函數(shù)中的this關(guān)鍵字

  • 每個(gè)函數(shù)中都有一個(gè)this關(guān)鍵字, 誰(shuí)調(diào)用當(dāng)前函數(shù), this關(guān)鍵字就是誰(shuí)
<script>
    function test() {
        console.log(this);
    }
    // 默認(rèn)情況下直接調(diào)用的函數(shù)都是由window調(diào)用的
    // 所以test函數(shù)中的this是window
    test(); // 相當(dāng)于window.test();
    
    var obj = new Object()
    obj.name = "lnj";
    obj.age = 33;
    obj.say = function () {
        console.log(this.name, this.age);
    }

    // 這里的say是一個(gè)方法, 方法必須通過(guò)對(duì)象來(lái)調(diào)用
    // 所以這里的say方法是由obj對(duì)象調(diào)用的, 所以say方法中的this是obj對(duì)象
    obj.say();
</script>

如何設(shè)計(jì)一個(gè)類

  • 生活中描述事物無(wú)非就是描述事物的屬性行為腰吟。
    • 如:人有身高无埃,體重等屬性,有說(shuō)話毛雇,打架等行為嫉称。
事物名稱(類名):人(Person)
屬性:身高(height)、年齡(age)
行為(功能):跑(run)禾乘、打架(fight)
  • JavaScript中用類來(lái)描述事物也是如此
    • 屬性:對(duì)應(yīng)類中的成員變量澎埠。
    • 行為:對(duì)應(yīng)類中的成員方法。
  • 定義類其實(shí)在定義類中的成員(成員變量和成員方法)
  • 擁有相同或者類似屬性(狀態(tài)特征)和行為(能干什么事)的對(duì)象都可以抽像成為一個(gè)類

如何分析一個(gè)類

  • 一般名詞都是類(名詞提煉法)

如何定義一個(gè)類

  • 在JavaScript中可以通過(guò)構(gòu)造函數(shù)來(lái)定義一個(gè)類
  • 構(gòu)造函數(shù)也是一個(gè)函數(shù), 只不過(guò)函數(shù)的名稱必須大寫(帕斯卡命名)
  • 構(gòu)造函數(shù)也是一個(gè)函數(shù), 只不過(guò)調(diào)用時(shí)必須通過(guò)new來(lái)調(diào)用
<script>
    // 通過(guò)構(gòu)造函數(shù)定義一個(gè)類
    // 在構(gòu)造函數(shù)中描述該類事物共性的屬性和行為
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.say = function () {
            console.log(this.name, this.age);
        }
    }
</script>

如何通過(guò)類創(chuàng)建一個(gè)對(duì)象

  • 不過(guò)就是創(chuàng)建結(jié)構(gòu)體的時(shí)候, 根據(jù)每個(gè)對(duì)象的特征賦值不同的屬性罷了
<script>
    // 通過(guò)構(gòu)造函數(shù)定義一個(gè)類
    // 在構(gòu)造函數(shù)中描述該類事物共性的屬性和行為
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.say = function () {
            console.log(this.name, this.age);
        }
    }
    // 通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象
    // new 的執(zhí)行過(guò)程
    // 1 在內(nèi)存中創(chuàng)建了一個(gè)空的對(duì)象
    // 2 讓構(gòu)造函數(shù)中的this指向剛剛創(chuàng)建的對(duì)象
    // 3 執(zhí)行構(gòu)造函數(shù)始藕,在構(gòu)造函數(shù)中設(shè)置屬性和方法(當(dāng)然也可以做其它事情)
    // 4 返回了當(dāng)前對(duì)象
    var p1 = new Person("lnj", 33);
    var p2 = new Person("zq", 18);
    p1.say();
    p2.say();
</script>
  • 與工廠函數(shù)的不同之處
    • 不用我們自己手動(dòng)創(chuàng)建對(duì)象
    • 不用我們手動(dòng)返回創(chuàng)建好的對(duì)象
    • 構(gòu)造函數(shù)名稱首字母必須大寫
    • 構(gòu)造函數(shù)必須通過(guò)new調(diào)用

構(gòu)造函數(shù)作用

  • 使用構(gòu)造函數(shù)的好處不僅僅在于代碼的簡(jiǎn)潔性蒲稳,更重要的是我們可以識(shí)別對(duì)象的具體類型了
  • 每個(gè)對(duì)象都可以訪問(wèn)一個(gè)名稱叫做constructor的屬性, 屬性指向創(chuàng)建該實(shí)例的構(gòu)造函數(shù)
<script>
    var obj = new Object();
    obj.name = "lnj";
    console.log(obj); // Object

    function Person(name) {
       this.name = name;
    }
    var p = new Person("zs");
    console.log(p); // Person
    // 可以通過(guò)對(duì)象的constructor屬性判斷某個(gè)對(duì)象是否是某個(gè)類的實(shí)例
    console.log(p.constructor === Person); // true
    // 也可以通過(guò)instanceof判斷某個(gè)對(duì)象是否是某個(gè)類的實(shí)例
    // 企業(yè)開發(fā)推薦
    console.log(p instanceof Person);
</script>

構(gòu)造函數(shù)的內(nèi)存優(yōu)化問(wèn)題

  • 每當(dāng)通過(guò)一個(gè)構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象, 就會(huì)在內(nèi)存中開辟一塊存儲(chǔ)空間, 該存儲(chǔ)空間中保存了對(duì)象的所有屬性和方法
  • 如果構(gòu)造函數(shù)中的某些屬性或方法是需要變化的, 那么每份存儲(chǔ)空間中都應(yīng)該保存一份獨(dú)有, 但是如果某些屬性和方法是不變的, 那么每份存儲(chǔ)空間中都保存一份則造成了內(nèi)存浪費(fèi)
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.type = "人";
        this.say = function () {
            console.log(this.name, this.age, this.type);
        }
    }
    // 所有對(duì)象的type屬性和say方法都是一樣的
   // 但是還會(huì)在每個(gè)對(duì)象的存儲(chǔ)空間中都存儲(chǔ)一份
    var p1 = new Person("lnj", 33);
    var p2 = new Person("zs", 33);
    console.log(p1.say === p2.say); // false
</script>
  • 對(duì)于這種問(wèn)題我們可以把需要共享的函數(shù)定義到構(gòu)造函數(shù)外部
<script>
    function say() {
        console.log(this.name, this.age, this.type);
    }
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.type = "人";
        this.say = say;
    }
    var p1 = new Person("lnj", 33);
    var p2 = new Person("zs", 33);
    console.log(p1.say === p2.say); // true
</script>
  • 但是如果有多個(gè)需要共享的函數(shù), 就會(huì)造成全局命名空間沖突的問(wèn)題(同一作用域不能出現(xiàn)同名的標(biāo)識(shí)符)
<script>
    // 將共享函數(shù)封裝到一個(gè)對(duì)象中, 與外界隔絕, 這樣就不會(huì)污染全局命名空間了
    var fns = {
        say: function () {
            console.log(this.name, this.age, this.type);
        },
        setName: function (name) {
            this.name = name;
        },
        setAge: function (age) {
            this.age = age;
        }
    }
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.type = "人";
        this.say = fns.say;
        this.setName = fns.setName;
        this.setAge = fns.setAge;
    }
    var p1 = new Person("lnj", 33);
    var p2 = new Person("zs", 33);
    console.log(p1.say === p2.say); // true
</script>

構(gòu)造函數(shù)的內(nèi)存優(yōu)化問(wèn)題(更好的方案)

  • JavaScript 規(guī)定,每一個(gè)構(gòu)造函數(shù)都有一個(gè) prototype 屬性伍派,指向另一個(gè)對(duì)象江耀。
  • 這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)的所擁有
  • 也就意味著诉植,我們可以把所有對(duì)象實(shí)例需要共享的屬性和方法直接定義在 prototype 對(duì)象上
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    // 給原型對(duì)象添加新的屬性和方法
    Person.prototype.say = function () {
        console.log(this.name, this.age, this.type);
    };
    Person.prototype.type = "人";

    var p1 = new Person("lnj", 33);
    var p2 = new Person("zs", 33);
    console.log(p1.say === p2.say); // true
    console.log(p1.type === p2.type); // true

    // 當(dāng)調(diào)用對(duì)象的屬性或者方法的時(shí)候祥国,先去找對(duì)象本身的屬性/方法
    // 如果對(duì)象沒有該屬性或者方法。此時(shí)去原型中查找對(duì)應(yīng)的屬性/方法
    // 如果對(duì)象本身沒有該屬性/方法晾腔,原型中也沒有該屬性或者方法舌稀,此時(shí)會(huì)報(bào)錯(cuò)
    p1.say();
    console.log(p1.type);
</script>
  • 任何函數(shù)都具有一個(gè) prototype 屬性,該屬性是一個(gè)對(duì)象
  • 構(gòu)造函數(shù)的 prototype 對(duì)象默認(rèn)都有一個(gè) constructor 屬性灼擂,指向 prototype 對(duì)象所在函數(shù)
  • 通過(guò)構(gòu)造函數(shù)得到的實(shí)例對(duì)象內(nèi)部會(huì)包含一個(gè)指向構(gòu)造函數(shù)的 prototype 對(duì)象的指針 proto

原型鏈

<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.type = "超人";
    }
    // 給原型對(duì)象添加新的屬性和方法
    Person.prototype.say = function () {
        console.log(this.name, this.age, this.type);
    };
    Person.prototype.type = "人";
    var p = new Person("lnj", 33);
    console.log(p.type);

    console.log(p.__proto__);
    console.log(p.__proto__.__proto__);
    console.log(p.__proto__.__proto__.__proto__);
</script>
  • 方法查找規(guī)則
    • 先查找當(dāng)前對(duì)象, 當(dāng)前對(duì)象有就使用當(dāng)前對(duì)象的方法
    • 當(dāng)前對(duì)象沒有再逐層在原型鏈上查找, 最先找到那個(gè)就使用哪個(gè)
    • 如果找到null都沒找到就報(bào)錯(cuò)
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
        // this.say = function () {
        //     console.log("自己的", this.name, this.age, this.type);
        // };
    }
    // 給原型對(duì)象添加新方法
    // Person.prototype.say = function () {
    //     console.log("原型鏈上的", this.name, this.age, this.type);
    // };
    var p = new Person("lnj", 33);
    // 自己有嗎? 有, 使用自己的
    // 自己有嗎? 沒有, 去原型鏈查找
    // 都沒有嗎? 報(bào)錯(cuò)
    p.say();
</script>
  • 屬性查找規(guī)則
    • 先查找當(dāng)前對(duì)象, 當(dāng)前對(duì)象有就使用當(dāng)前對(duì)象的方法
    • 當(dāng)前對(duì)象沒有再逐層在原型鏈上查找, 最先找到那個(gè)就使用哪個(gè)
    • 如果找到null都沒找到就輸出undefined
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
        // this.type = "超人";
    }
    // 給原型對(duì)象添加新的屬性和方法
    // Person.prototype.type = "人";
    var p = new Person("lnj", 33);
    // 自己有嗎? 有, 使用自己的
    // 自己有嗎? 沒有, 去原型鏈查找
    // 都沒有嗎? undefined
    console.log(p.type);
</script>
  • 屬性的設(shè)置規(guī)則
    • 不會(huì)修改原型鏈上的屬性, 會(huì)給當(dāng)前對(duì)象新增一個(gè)屬性
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    // 給原型對(duì)象添加新的屬性和方法
    Person.prototype.type = "人";
    var p = new Person("lnj", 33);
    console.log(p.__proto__.type);
    // p.__proto__.type = "超人";
    // 自己有這個(gè)屬性嗎? 沒有, 新增
    p.type = "超人";
    console.log(p.__proto__.type);
</script>

自定義原型對(duì)象

  • 原型對(duì)象是構(gòu)造函數(shù)的一個(gè)屬性, 所以我們可以通過(guò)修改屬性值的方式來(lái)自定義原型對(duì)象
  • 需要注意的是, 自定義原型對(duì)象不能破壞原有的三角戀關(guān)系
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    Person.prototype = {
        constructor: Person, // 手動(dòng)還原constructor屬性, 保持三角戀關(guān)系
        type: "人",
        say: function () {
            console.log(this.name, this.age, this.type);
        }
    }
    var p = new Person("lnj", 33);
    p.say();
</script>

面向?qū)ο笕筇匦?/h2>
  • 封裝性
    • 封裝性就是隱藏實(shí)現(xiàn)細(xì)節(jié)壁查,僅對(duì)外公開接口


    • 類是數(shù)據(jù)與功能的封裝,數(shù)據(jù)就是成員變量剔应,功能就是方法
  • 為什么要封裝?
    • 不封裝的缺點(diǎn):當(dāng)一個(gè)類把自己的成員變量暴露給外部的時(shí)候,那么該類就失去對(duì)該成員變量的管理權(quán)睡腿,別人可以任意的修改你的成員變量
    • 封裝就是將數(shù)據(jù)隱藏起來(lái),只能用此類的方法才可以讀取或者設(shè)置數(shù)據(jù),不可被外部任意修改是面向?qū)ο笤O(shè)計(jì)本質(zhì)(將變化隔離)。這樣降低了數(shù)據(jù)被誤用的可能 (提高安全性靈活性)
<script>
    function Person(name) {
        this.name = name; // 公有屬性
        // 只能在內(nèi)部使用, 不能在外部使用
        var age = 666; // 私有屬性
        // 公有方法
        this.say = function () {
            console.log(this.name, age);
            test();
        }
        this.setAge = function (num) {
            age = num;
        }
        this.getAge = function () {
            return age;
        }
        // 私有方法
        function test() {
            console.log("test");
        }
    }
    var p = new Person("lnj");
    console.log(p.name);
    p.say();
    console.log(p.age); // undefined
    p.setAge(123);
    console.log(p.getAge());
    p.test(); // 報(bào)錯(cuò)
</script>
  • 封裝原則
    • 將不需要對(duì)外提供的內(nèi)容都隱藏起來(lái),把屬性都隱藏,提供公共的方法對(duì)其訪問(wèn)

實(shí)例屬性和實(shí)例方法/靜態(tài)屬性和靜態(tài)方法

  • 通過(guò)構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象訪問(wèn)的屬性和方法,我們稱之為實(shí)例屬性和實(shí)例方法
    • 實(shí)例屬性和實(shí)例方法都是綁定在構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象上的
<script>
    function Person(name) {
        this.name = name; // 實(shí)例屬性
        this.eat = function () { // 實(shí)例方法
            console.log("eat");
        }
    }
    Person.prototype.age = "0"; // 實(shí)例屬性
    Person.prototype.say = function () { // 實(shí)例方法
        console.log("hello");
    }
    var p = new Person("lnj");
    console.log(p.name); // 通過(guò)對(duì)象訪問(wèn)
    console.log(p.age); // 通過(guò)對(duì)象訪問(wèn)
    p.eat(); // 通過(guò)對(duì)象訪問(wèn)
    p.say(); // 通過(guò)對(duì)象訪問(wèn)
</script>
  • 通過(guò)構(gòu)造函數(shù)直接調(diào)用的屬性和方法,我們稱之為靜態(tài)屬性和靜態(tài)方法
    • 靜態(tài)屬性和靜態(tài)方法都是綁定在構(gòu)造函數(shù)上的
<script>
    function Person() {
        Person.name = "lnj"; // 靜態(tài)屬性
        Person.eat = function () { // 靜態(tài)方法
            console.log("eat");
        }
    }
    Person.count = 0; // 靜態(tài)屬性
    Person.say = function () { // 靜態(tài)方法
        console.log("hello");
    };

    console.log(Person.name); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
    console.log(Person.count); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
    Person.eat(); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
    Person.say(); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
</script>

  • 繼承性
    • 兒子繼承父親的物品就是繼承最好的體現(xiàn)
    • js中繼承目的: 把子類型中共同的屬性和方法提取到父類型中
    • 較少代碼的冗余度, 提升代碼的復(fù)用性




  • 借用原型鏈實(shí)現(xiàn)繼承
    • 直接將子類的原型對(duì)象修改為父類對(duì)象, 這樣就能使用原型鏈上的屬性和方法
<script>
    // 父類
    function Person() {
        this.name = "lnj";
        this.age = 33;
        this.gender = "male";
    }
    // 子類
    function Student(score) {
        this.score = score;
    }
    // 由于是直接將子類原型對(duì)象修改為了父類對(duì)象
    // 所以繼承的屬性值很難自定義
    Student.prototype = new Person();
    Student.prototype.constructor = Student;

    var stu1 = new Student(99);
    console.log(stu.name, stu.age, stu.gender, stu.score);
    var stu2 = new Student(66);
    console.log(stu.name, stu.age, stu.gender, stu.score);
</script>
  • 借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承
    • 在子類中調(diào)用父類構(gòu)造函數(shù), 并且將父類構(gòu)造函數(shù)的this修改為子類對(duì)象
<script>
    // 父類
    function Person(name, age, gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    // 借用構(gòu)造函數(shù)只是調(diào)用了父類的構(gòu)造函數(shù), 借用了構(gòu)造函數(shù)中的代碼
    // 相當(dāng)于動(dòng)態(tài)的給子類添加了許多屬性, 但是并沒有修改子類的原型
    // 所以子類無(wú)法繼承父類的方法
    Person.prototype.say = function () {
        console.log(this.name, this.age, this.gender);
    }
    // 子類
    function Student(score, name, age, gender) {
        Person.call(this, name, age, gender);
        this.score = score;
    }

    var stu1 = new Student(99, "lnj", 33, "male");
    var stu2 = new Student(66, "zq", 18, "female");
    console.log(stu1.name, stu1.age, stu1.gender, stu1.score);
    console.log(stu2.name, stu2.age, stu2.gender, stu2.score);
    stu1.say(); // 報(bào)錯(cuò)
    stu2.say(); // 報(bào)錯(cuò)
</script>
  • 借用構(gòu)造函數(shù)+借用原型鏈組合繼承
    • 通過(guò)借用構(gòu)造函數(shù)實(shí)現(xiàn)屬性繼承
    • 通過(guò)借用原型鏈實(shí)現(xiàn)方法繼承
<script>
    // 父類
    function Person(name, age, gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    Person.prototype.say = function () {
        console.log(this.name, this.age, this.gender);
    }
    // 子類
    function Student(score, name, age, gender) {
        Person.call(this, name, age, gender);
        this.score = score;
    }
    Student.prototype = Person.prototype;
    Student.prototype.constructor = Student;
    // 由于子類的原型指向了父類的原型, 所以操作的都是同一個(gè)原型對(duì)象
    // 給子類的原型新增方法或者屬性, 父類也會(huì)受到影響
    Student.prototype.study = function () {
        console.log("好好學(xué)習(xí)天天向上");
    };
    Student.prototype.type = "學(xué)生";

    var stu = new Student(99, "lnj", 33, "male");
    stu.say();
    stu.study();
    console.log(stu.type);

    var p = new Person("zq", 18, "female");
    p.say();
    p.study();
    console.log(p.type);
</script>
  • 終極方案
<script>
    // 父類
    function Person(name, age, gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    Person.prototype.say = function () {
        console.log(this.name, this.age, this.gender);
    }
    // 子類
    function Student(score, name, age, gender) {
        Person.call(this, name, age, gender);
        this.score = score;
    }
    Student.prototype = new Person();
    Student.prototype.constructor = Student;
    // 由于子類的原型指向一個(gè)全新的對(duì)象
    // 所以給子類的原型新增方法或者屬性, 父類不會(huì)受到影響
    Student.prototype.study = function () {
        console.log("好好學(xué)習(xí)天天向上");
    };
    Student.prototype.type = "學(xué)生";

    var stu = new Student(99, "lnj", 33, "male");
    stu.say();
    stu.study();
    console.log(stu.type);

    var p = new Person("zq", 18, "female");
    p.say();
    p.study(); // 報(bào)錯(cuò)
    console.log(p.type); // 報(bào)錯(cuò)
</script>

對(duì)象的增刪改查

<script>
    // 1.定義構(gòu)造函數(shù)
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    Person.prototype.type = "人";

    // 2.利用構(gòu)造函數(shù)創(chuàng)建對(duì)象
    var p1 = new Person("lnj", 33);
    var p2 = new Person("zq", 18);
    // 3.動(dòng)態(tài)增加屬性
    p1.score = 99;
    console.log(p1);
    console.log(p2);
    // 4.刪除某個(gè)屬性
    delete p1.name;
    console.log(p1);
    console.log(p2);
    // 5.修改某個(gè)屬性的值
    p1.age = 66;
    console.log(p1);
    console.log(p2);
    // 6.查詢是否包含某個(gè)屬性
    // in 會(huì)到原型對(duì)象里面查找
    console.log("type" in p1);
    // hasOwnProperty: 只在對(duì)象自身查找
    console.log(p1.hasOwnProperty("type"));
</script>

Object對(duì)象的bind-call-apply方法

  • 默認(rèn)情況下所有對(duì)象都有bind-call-apply方法
  • 這三個(gè)方法的作用是用于修改指定函數(shù)中this的指向
<script>
    function sum(a, b) {
        // 默認(rèn)情況下函數(shù)中的this誰(shuí)調(diào)用就是誰(shuí)
        console.log(this);
        console.log(a, b);
    }
    // test();
    var obj = {
        name: "zq"
    };
    // bind方法可以修改指定函數(shù)中this的指向
    // 但是bind方法并不會(huì)調(diào)用函數(shù), 如果需要手動(dòng)調(diào)用
    // sum.bind(obj)(10, 20);

    // call方法也可以修改指定函數(shù)中this的指向
    // 并且call方法會(huì)自動(dòng)調(diào)用函數(shù), 不用手動(dòng)調(diào)用
    // sum.call(obj, 10, 20);
    
    // apply方法也可以修改指定函數(shù)中this的指向
    // 并且apply方法會(huì)自動(dòng)調(diào)用函數(shù), 不用手動(dòng)調(diào)用
    // 和call方法的區(qū)別是傳遞參數(shù)的形式不同而已
    sum.apply(obj,[10, 20]);
</script>

對(duì)象的拷貝

  • 淺拷貝
    • 對(duì)于基本類型屬性無(wú)論是深淺拷貝,都會(huì)復(fù)制一份
    • 對(duì)于引用類型屬性淺拷貝拷貝的是引用類型的地址
    • 簡(jiǎn)而言之, 淺拷貝修改引用類型屬性, 拷貝前拷貝后的對(duì)象都會(huì)受到影響
<script>
    var obj1 = {
        name: "lnj", // 基本類型屬性
        age: 33,     // 基本類型屬性
        dog: {       // 引用類型屬性
            name: "wc",
            age: 1,
        }
    };

    var obj2 = {};
    /*
    for(var key in obj1){
        obj2[key] = obj1[key];
    }
    */
    function copy(o1, o2){
        for(var key in o1){
            o2[key] = o1[key];
        }
    }
    copy(obj1, obj2);
    console.log(obj1);
    console.log(obj2);

    // 修改基本類型屬性, 不會(huì)影響原有對(duì)象
    obj2.name = "zs";
    console.log(obj1.name);
    console.log(obj2.name);

    // 修改引用類型屬性, 會(huì)影響原有對(duì)象
    obj2.dog.name = "xq";
    console.log(obj1.dog.name);
    console.log(obj2.dog.name);
</script>
  • 深拷貝
    • 對(duì)于基本類型屬性無(wú)論是深淺拷貝,都會(huì)復(fù)制一份
    • 對(duì)于引用類型屬性深拷貝會(huì)將引用類型復(fù)制一份
    • 簡(jiǎn)而言之, 深拷貝修改引用類型屬性, 只會(huì)影響當(dāng)前修改對(duì)象
<script>
    var obj1 = {
        name: "lnj", // 基本類型屬性
        age: 33,     // 基本類型屬性
        dog: {       // 引用類型屬性
            name: "wc",
            age: 1,
        }
    };
    var obj2 = {};

    function copy(o1, o2){
        for(var key in o1){
            var item = o1[key];
            // 判斷當(dāng)前屬性是否是引用類型
            if(item instanceof Object){
                // 創(chuàng)建一個(gè)新引用類型屬性
                var o = {};
                o2[key] = o;
                // 進(jìn)一步拷貝引用類型屬性
                copy(item, o);
            }
            // 判斷當(dāng)前屬性是否數(shù)組
            else if(item instanceof Array){
                // 創(chuàng)建一個(gè)數(shù)組屬性
                var arr = [];
                o2[key] = arr;
                // 進(jìn)一步拷貝數(shù)組屬性
                copy(item, arr);
            }
            // 如果不是引用類型, 直接拷貝即可
            else{
                o2[key] = item;
            }
        }
    }
    copy(obj1, obj2);
    console.log(obj1);
    console.log(obj2);

    // 修改基本類型屬性, 不會(huì)影響原有對(duì)象
    obj2.name = "zs";
    console.log(obj1.name);
    console.log(obj2.name);

    // 修改引用類型屬性, 會(huì)影響原有對(duì)象
    obj2.dog.name = "xq";
    console.log(obj1.dog.name);
    console.log(obj2.dog.name);
</script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末峻贮,一起剝皮案震驚了整個(gè)濱河市席怪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纤控,老刑警劉巖挂捻,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異船万,居然都是意外死亡刻撒,警方通過(guò)查閱死者的電腦和手機(jī)惜辑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)疫赎,“玉大人盛撑,你說(shuō)我怎么就攤上這事∨醺悖” “怎么了抵卫?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)胎撇。 經(jīng)常有香客問(wèn)我介粘,道長(zhǎng),這世上最難降的妖魔是什么晚树? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任姻采,我火速辦了婚禮,結(jié)果婚禮上爵憎,老公的妹妹穿的比我還像新娘慨亲。我一直安慰自己,他們只是感情好宝鼓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布刑棵。 她就那樣靜靜地躺著,像睡著了一般愚铡。 火紅的嫁衣襯著肌膚如雪蛉签。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天沥寥,我揣著相機(jī)與錄音碍舍,去河邊找鬼。 笑死邑雅,一個(gè)胖子當(dāng)著我的面吹牛片橡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蒂阱,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼锻全,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼狂塘!你這毒婦竟也來(lái)了录煤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤荞胡,失蹤者是張志新(化名)和其女友劉穎妈踊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泪漂,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廊营,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年歪泳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片露筒。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呐伞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慎式,到底是詐尸還是另有隱情伶氢,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布瘪吏,位于F島的核電站癣防,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏掌眠。R本人自食惡果不足惜蕾盯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蓝丙。 院中可真熱鬧级遭,春花似錦、人聲如沸渺尘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沧烈。三九已至掠兄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锌雀,已是汗流浹背蚂夕。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腋逆,地道東北人婿牍。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像惩歉,于是被迫代替她去往敵國(guó)和親等脂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348