JavaScript函數原型鏈知識記錄

1 構造函數web

構造函數是專門用於建立對象的
function Person(myName, myAge) {
            // let obj = new Object();  // 系統自動添加的
            // let this = obj; // 系統自動添加的
            this.name = myName;
            this.age = myAge;
            this.say = function () {
                console.log("hello world");
            }
            // return this; // 系統自動添加的
        }
1.當咱們new Person("lnj", 34);系統作了什麼事情
1.1會在構造函數中自動建立一個對象
1.2會自動將剛纔建立的對象賦值給this
1.3會在構造函數的最後自動添加return this;
        let obj1 = new Person("lnj", 34);
        let obj2 = new Person("zs", 44);
        console.log(obj1);
        console.log(obj2);

構造函數的優化:編程

上面構造函數的定義有一個弊端,以下數組

let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
因爲兩個對象中的say方法的實現都是同樣的, 可是保存到了不一樣的存儲空間中
因此有性能問題
console.log(obj1.say === obj2.say); // false

經過三個等號來判斷兩個函數名稱, 表示判斷兩個函數是否都存儲在同一塊內存中

優化方式1:安全

function mySay() {
            console.log("hello world");
        }
        function Person(myName, myAge) {
            // let obj = new Object();  // 系統自動添加的
            // let this = obj; // 系統自動添加的
            this.name = myName;
            this.age = myAge;
            this.say = mySay;
            // return this; // 系統自動添加的
        }
        let obj1 = new Person("lnj", 34);
        let obj2 = new Person("zs", 44);
        console.log(obj1.say === obj2.say); // true

可是這種方式也是有弊端的,app

1.1閱讀性下降了
1.2污染了全局的命名空間

優化方式2:webstorm

function Person(myName, myAge) {
            // let obj = new Object();  // 系統自動添加的
            // let this = obj; // 系統自動添加的
            this.name = myName;
            this.age = myAge;
            // this.say = fns.mySay;
            // return this; // 系統自動添加的
        }
        Person.prototype = {
            say: function () {
                console.log("hello world");
            }
        }
        let obj1 = new Person("lnj", 34);
        obj1.say();
        let obj2 = new Person("zs", 44);
        obj2.say();
        console.log(obj1.say === obj2.say); // true

經過改寫構造函數的 原型對象,讓方法say變得公用編程語言

3 prototype特色:函數

1.1存儲在prototype中的方法能夠被對應構造函數建立出來的全部對象共享
1.2prototype中除了能夠存儲方法之外, 還能夠存儲屬性
1.3prototype若是出現了和構造函數中同名的屬性或者方法, 對象在訪問的時候, 訪問到的是構造函中的數據

2.prototype應用場景

prototype中通常狀況下用於存儲全部對象都相同的一些屬性以及方法
若是是對象特有的屬性或者方法, 咱們會存儲到構造函數中
function Person(myName, myAge) {
            this.name = myName;
            this.age = myAge;
            this.currentType = "構造函數中的type";
            this.say = function () {
                console.log("構造函數中的say");
            }
        }
        Person.prototype = {
            currentType: "人",
            say: function () {
                console.log("hello world");
            }
        }
        let obj1 = new Person("lnj", 34);
        obj1.say();
        console.log(obj1.currentType);
        let obj2 = new Person("zs", 44);
        obj2.say();
        console.log(obj2.currentType);

4  prototype,constructor, __proto__的三角戀關係性能

1.每一個"構造函數"中都有一個默認的屬性, 叫作prototype
prototype屬性保存着一個對象, 這個對象咱們稱之爲"原型對象"
2.每一個"原型對象"中都有一個默認的屬性, 叫作constructor
constructor指向當前原型對象對應的那個"構造函數"
3.經過構造函數建立出來的對象咱們稱之爲"實例對象"
每一個"實例對象"中都有一個默認的屬性, 叫作__proto__
__proto__指向建立它的那個構造函數的"原型對象"
function Person(myName, myAge) {
            this.name = myName;
            this.age = myAge;
        }
        let obj1 = new Person("lnj", 34);

        console.log(Person.prototype);
        console.log(Person.prototype.constructor);
        console.log(obj1.__proto__);

5 Function函數優化

1.JavaScript中函數是引用類型(對象類型), 既然是對象,
因此也是經過構造函數建立出來的,"全部函數"都是經過Function構造函數建立出來的對象

2.JavaScript中只要是"函數"就有prototype屬性
"Function函數"的prototype屬性指向"Function原型對象"

3.JavaScript中只要"原型對象"就有constructor屬性
"Function原型對象"的constructor指向它對應的構造函數

4.Person構造函數是Function構造函數的實例對象, 因此也有__proto__屬性
Person構造函數的__proto__屬性指向"Function原型對象"

5 Object函數

1. JavaScript函數是引用類型(對象類型), 因此Function函數也是對象

2."Function構造函數"也是一個對象, 因此也有__proto__屬性
"Function構造函數"__proto__屬性指向"Function原型對象"

3. JavaScript中還有一個系統提供的構造函數叫作Object
只要是函數都是"Function構造函數"的實例對象

4.只要是對象就有__proto__屬性, 因此"Object構造函數"也有__proto__屬性
"Object構造函數"的__proto__屬性指向建立它那個構造函數的"原型對象"

5.只要是構造函數都有一個默認的屬性, 叫作prototype
prototype屬性保存着一個對象, 這個對象咱們稱之爲"原型對象"

6.只要是原型對象都有一個默認的屬性, 叫作constructor
constructor指向當前原型對象對應的那個"構造函數"

function Person(myName, myAge) {
            this.name = myName;
            this.age = myAge;
        }
        let obj1 = new Person("lnj", 34);
         console.log(Function.__proto__);
         console.log(Function.__proto__ === Function.prototype); // true

         console.log(Object);
         console.log(Object.__proto__);
         console.log(Object.__proto__ === Function.prototype); // true
         console.log(Object.prototype);
         console.log(Object.prototype.constructor);

         console.log(Object.prototype.constructor === Object); // true
         console.log(Object.prototype.__proto__); // null

6 函數對象關係

1.全部的構造函數都有一個prototype屬性, 全部prototype屬性都指向本身的原型對象
2,全部的原型對象都有一個constructor屬性, 全部constructor屬性都指向本身的構造函數
3.全部函數都是Function構造函數的實例對象
4.全部函數都是對象, 包括Function構造函數
5.全部對象都有__proto__屬性
6.普通對象的__proto__屬性指向建立它的那個構造函數對應的"原型對象"

7.全部對象的__proto__屬性最終都會指向"Object原型對象"

8."Object原型對象"的__proto__屬性指向NULL
Object ,Function,實例對象的總圖:
function Person(myName, myAge) {
            this.name = myName;
            this.age = myAge;
        }
        let obj1 = new Person("lnj", 34);

        console.log(Function.prototype.__proto__);
        console.log(Person.prototype.__proto__);
        console.log(Function.prototype.__proto__ === Person.prototype.__proto__);//true
        console.log(Function.prototype.__proto__ === Object.prototype);//true
        console.log(Person.prototype.__proto__ === Object.prototype);//true

7 原型鏈

1.對象中__proto__組成的鏈條咱們稱之爲原型鏈
2.對象在查找屬性和方法的時候, 會先在當前對象查找
若是當前對象中找不到想要的, 會依次去上一級原型對象中查找
若是找到Object原型對象都沒有找到, 就會報錯
function Person(myName, myAge) {
            this.name = myName;
            this.age = myAge;
            // this.currentType = "構造函數中的type";
            // this.say = function () {
            //     console.log("構造函數中的say");
            // }
        }

        Person.prototype = {
            // 注意點: 爲了避免破壞原有的關係, 在給prototype賦值的時候, 須要在自定義的對象中手動的添加constructor屬性, 手動的指定須要指向誰
            constructor: Person,
            // currentType: "人",
            // say: function () {
            //     console.log("hello world");
            // }
        }
        let obj1 = new Person("lnj", 34);
        // obj1.say();
        console.log(obj1.currentType);
        // console.log(Person.prototype.constructor);

8 屬性注意點

 在給一個對象不存在的屬性設置值的時候, 不會去原型對象中查找, 若是當前對象沒有就會給當前對象新增一個不存在的屬性
function Person(myName, myAge) {
            this.name = myName;
            this.age = myAge;
        }
        Person.prototype = {
            constructor: Person,
            currentType: "人",
            say: function () {
                console.log("hello world");
            }
        }
        let obj = new Person("lnj", 34);
        // console.log(obj.currentType); // "人"
        // console.log(obj.__proto__.currentType); // "人"

        // 注意點: 在給一個對象不存在的屬性設置值的時候, 不會去原型對象中查找, 若是當前對象沒有就會給當前對象新增一個不存在的屬性
        obj.currentType = "新設置的值";
        console.log(obj.currentType); // 新設置的值
        console.log(obj.__proto__.currentType); // "人"

9 js三大特性之一-封裝性

1.局部變量和局部函數
不管是ES6以前仍是ES6, 只要定義一個函數就會開啓一個新的做用域
只要在這個新的做用域中, 經過let/var定義的變量就是局部變量
只要在這個新的做用域中, 定義的函數就是局部函數

2.什麼是對象的私有變量和函數
默認狀況下對象中的屬性和方法都是公有的, 只要拿到對象就能操做對象的屬性和方法
外界不能直接訪問的變量和函數就是私有變量和是有函數
構造函數的本質也是一個函數, 因此也會開啓一個新的做用域, 因此在構造函數中定義的變量和函數就是私有和函數
*/
/*
3.什麼是封裝?
封裝性就是隱藏實現細節,僅對外公開接口

4.爲何要封裝?
4.1不封裝的缺點:當一個類把本身的成員變量暴露給外部的時候,那麼該類就失去對屬性的管理權,別人能夠任意的修改你的屬性
4.2封裝就是將數據隱藏起來,只能用此類的方法才能夠讀取或者設置數據,不可被外部任意修改. 封裝是面向對象設計本質(將變化隔離)。這樣下降了數據被誤用的可能 (提升安全性和靈活性)
function Person() {
            this.name = "lnj";
            // this.age = 34;
            let age = 34;
            this.setAge = function (myAge) {
                if(myAge >= 0){
                    age = myAge;
                }
            }
            this.getAge = function () {
                return age;
            }
            this.say = function () {
                console.log("hello world");
            }
            /*
            // 因爲構造函數也是一個函數, 因此也會開啓一個新的做用域
            // 因此在構造函數中經過var/let定義的變量也是局部變量
            // 因此在構造函數中定義的函數也是局部函數
            var num = 123;
            let value = 456;
            function test() {
                console.log("test");
            }
            */
        }
        let obj = new Person();
        // 結論: 默認狀況下對象的屬性和方法都是公開的, 只要拿到對象就能夠操做對象的屬性和方法
        // console.log(obj.name);
        // obj.age = -3;
        // console.log(obj.age);
        // obj.say();

        // console.log(age);
        obj.setAge(-3);
        console.log(obj.getAge());

10 私有屬性注意點

 在給一個對象不存在的屬性設置值的時候, 不會去原型對象中查找, 若是當前對象沒有就會給當前對象新增一個不存在的屬性
因爲私有屬性的本質就是一個局部變量, 並非真正的屬性, 因此若是經過 對象.xxx的方式是找不到私有屬性的, 因此會給當前對象

11 屬性方法分類

1.在JavaScript中屬性和方法分類兩類
1.1實例屬性/實例方法
在企業開發中經過實例對象訪問的屬性, 咱們就稱之爲實例屬性
在企業開發中經過實例對象調用的方法, 咱們就稱之爲實例方法
1.2靜態屬性/靜態方法
在企業開發中經過構造函數訪問的屬性, 咱們就稱之爲靜態屬性
在企業開發中經過構造函數調用的方法, 咱們就稱之爲靜態方法
 function Person() {
            this.name = "lnj";
            this.say = function () {
                console.log("hello world");
            }
        }
         
        // 經過構造函數建立的對象, 咱們稱之爲"實例對象"
        let obj = new Person();
        console.log(obj.name);
        obj.say();

        obj.age = 34;
        console.log(obj.age);
        obj.eat = function () {
            console.log("eat");
        }
        obj.eat();
      

        // 構造函數也是一個"對象", 因此咱們也能夠給構造函數動態添加屬性和方法
        Person.num = 666;
        Person.run = function () {
            console.log("run");
        }
        console.log(Person.num);
        Person.run();

12 bind call apply

1.this是什麼?
誰調用當前函數或者方法, this就是誰
*/

/*
2.這三個方法的做用是什麼?
這三個方法都是用於修改函數或者方法中的this的
2.1.bind方法做用
修改函數或者方法中的this爲指定的對象, 而且會返回一個修改以後的新函數給咱們
注意點: bind方法除了能夠修改this之外, 還能夠傳遞參數, 只不過參數必須寫在this對象的後面
2.2.call方法做用
修改函數或者方法中的this爲指定的對象, 而且會當即調用修改以後的函數
注意點: call方法除了能夠修改this之外, 還能夠傳遞參數, 只不過參數必須寫在this對象的後面
2.3.apply方法做用
修改函數或者方法中的this爲指定的對象, 而且會當即調用修改以後的函數
注意點: apply方法除了能夠修改this之外, 還能夠傳遞參數, 只不過參數必須經過數組的方式傳遞
let obj = {
            name: "zs"
        }

         function test(a, b) {
             console.log(a, b);
             console.log(this);
         }
         test(10, 20);
         window.test();
         let fn = test.bind(obj, 10, 20);
         fn();

         test.call(obj, 10, 20);

         test.apply(obj, [10, 20]);


        function Person() {
            this.name = "lnj";
            this.say = function () {
                console.log(this);
            }
        }
        let p = new Person();
         p.say();
         let fn = p.say.bind(obj);
         fn();
         p.say.call(obj);
        p.say.apply(obj);

 

13 js三大特性之繼承性

方式一:

function Person() {
            this.name = null;
            this.age = 0;
            this.say = function () {
                console.log(this.name, this.age);
            }
        }
        let per = new Person();
        per.name = "lnj";
        per.age = 34;
        per.say();

        // 在企業開發中若是構造函數和構造函數之間的關係是is a關係, 那麼就可使用繼承來優化代碼, 來減小代碼的冗餘度
        // 學生 is a 人 , 學生是一我的
        function Student() {
            // this.name = null;
            // this.age = 0;
            // this.say = function () {
            //     console.log(this.name, this.age);
            // }
            this.score = 0;
            this.study = function () {
                console.log("day day up");
            }
        }
        Student.prototype = new Person();
        Student.prototype.constructor = Student;

        let stu = new Student();
        stu.name = "zs";
        stu.age = 18;
        stu.score = 99;
        stu.say();
        stu.study();

弊端:

調用子類構造函數建立對象的時候,沒法訪問父類的屬性

 function Person(myName, myAge) {
            this.name = myName;
            this.age = myAge;
            this.say = function () {
                console.log(this.name, this.age);
            }
        }
       // let per = new Person("lnj", 34);
       //  per.say();

        // 學生 is a 人 , 學生是一我的
        function Student(myName, myAge, myScore) {
          //沒法訪問到父類的屬性
            this.score = myScore;
            this.study = function () {
                console.log("day day up");
            }
        }

        // let stu = new Student();
        // stu.name = "zs";
        // stu.age = 18;
        // stu.score = 99;
        // stu.say();
        // stu.study();

方式二:

function Person(myName, myAge) {
            // let per = new Object();
            // let this = per;
            // this = stu;
            this.name = myName; // stu.name = myName;
            this.age = myAge; // stu.age = myAge;
            this.say = function () { // stu.say = function () {}
                console.log(this.name, this.age);
            }
            // return this;
        }
        function Student(myName, myAge, myScore) {
            // let stu = new Object();
            // let this = stu;
            Person.call(this, myName, myAge); //  Person.call(stu);
            this.score = myScore;
            this.study = function () {
                console.log("day day up");
            }
            // return this;
        }
        let stu = new Student("ww", 19, 99);
        // stu.name = "zs";
        // stu.age = 18;
        // stu.score = 99;
        console.log(stu.score);
        stu.say();
        stu.study();

弊端:訪問不到 父類 原型對象 中的屬性和方法

方式三:

 function Person(myName, myAge) {
            // let per = new Object();
            // let this = per;
            // this = stu;
            this.name = myName; // stu.name = myName;
            this.age = myAge; // stu.age = myAge;
            this.sex = 1;
            // this.say = function () { // stu.say = function () {}
            //     console.log(this.name, this.age);
            // }
            // return this;
        }
        Person.prototype.say = function () {
            console.log(this.name, this.age);
        }

        function Student(myName, myAge, myScore) {
            Person.call(this, myName, myAge);
            this.score = myScore;
            this.study = function () {
                console.log("day day up");
            }
        }
        // 注意點: 要想使用Person原型對象中的屬性和方法, 那麼就必須將Student的原型對象改成Person的原型對象才能夠
        Student.prototype = Person.prototype;
        Student.prototype.constructor = Student;

        let stu = new Student("ww", 19, 99);
        console.log(stu.score);
        stu.say();
        stu.study();

弊端:讓子類的原型對象和父類的原型對象是一個,若是在其中一個 修改原型對象,會影響另外一個

方式四:終極方案

1.js中繼承的終極方法
1.1在子類的構造函數中經過call藉助父類的構造函數
1.2將子類的原型對象修改成父類的實例對象
function Person(myName, myAge) {
            // let per = new Object();
            // let this = per;
            // this = stu;
            this.name = myName; // stu.name = myName;
            this.age = myAge; // stu.age = myAge;
            // return this;
        }
        Person.prototype.say = function () {
            console.log(this.name, this.age);
        }
        function Student(myName, myAge, myScore) {
            Person.call(this, myName, myAge);
            this.score = myScore;
            this.study = function () {
                console.log("day day up");
            }
        }
        /*
        弊端:
        1.因爲修改了Person原型對象的constructor屬性, 因此破壞了Person的三角戀關係
        2.因爲Person和Student的原型對象是同一個, 因此給Student的元素添加方法, Person也會新增方法
         */
        // Student.prototype = Person.prototype;


        Student.prototype = new Person();
        Student.prototype.constructor = Student;
        Student.prototype.run = function(){
            console.log("run");
        }

        let per = new Person();
        per.run();

14 js三大特性之多態性

 /*
        1.什麼是強類型語言, 什麼是是弱類型語言
        1.1什麼是強類型語言:
        通常編譯型語言都是強類型語言,
        強類型語言,要求變量的使用要嚴格符合定義
        例如定義 int num; 那麼num中未來就只可以存儲整型數據
        1.2什麼是弱類型語言:
        通常解釋型語言都是弱類型語言,
        弱類型語言, 不會要求變量的使用要嚴格符合定義
        例如定義 let num; num中既能夠存儲整型, 也能夠存儲布爾類型等
        1.3因爲js語言是弱類型的語言, 因此咱們不用關注多態

        2.什麼是多態?
        多態是指事物的多種狀態
        例如:
        按下 F1 鍵這個動做,
        若是當前在 webstorm 界面下彈出的就是 webstorm 的幫助文檔;
        若是當前在 Word 下彈出的就是 Word 幫助;
        同一個事件發生在不一樣的對象上會產生不一樣的結果。

        3.多態在編程語言中的體現
        父類型變量保存子類型對象, 父類型變量當前保存的對象不一樣, 產生的結果也不一樣
        */

        // function Animal(myName) {
        //     this.name = myName;
        //     this.eat = function () {
        //         console.log(this.name + " 動物吃東西");
        //     }
        // }
        function Dog() {
            // Animal.call(this, myName);
            this.eat = function () {
                console.log(" 狗吃東西");
            }
        }
        // Dog.prototype = new Animal();
        // Dog.prototype.constructor = Dog;

        function Cat() {
            // Animal.call(this, myName);
            this.eat = function () {
                console.log(" 貓吃東西");
            }
        }
        // Cat.prototype = new Animal();
        // Cat.prototype.constructor = Cat;

        // function feed(Dog animal) {
        //     animal.eat(); // 狗吃東西
        // }
        // function feed(Cat animal) {
        //     animal.eat(); // 貓吃東西
        // }
        // function feed(Animal animal) {
        //     animal.eat(); // 狗吃東西
        // }
        function feed(animal){
            animal.eat();
        }
        let dog = new Dog();
        feed(dog);

        let cat = new Cat();
        feed(cat);
相關文章
相關標籤/搜索