亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

js回顧:原型鏈

call_me_R / 2586人閱讀

摘要:原型鏈原型鏈實際上是中的實現(xiàn)繼承的機(jī)制,在搞懂原型鏈之前首先要搞懂幾個概念,普通對象和函數(shù)對象,構(gòu)造函數(shù),對于很多人來說是混雜不清的概念,但是想要弄清楚原型鏈我們必須了解什么是首先,只能在存在與函數(shù)中其次,其實是當(dāng)前函數(shù)所在上下文環(huán)境,再簡

原型鏈

原型鏈實際上是JavaScript中的實現(xiàn)繼承的機(jī)制,在搞懂原型鏈之前首先要搞懂幾個概念:this,普通對象和函數(shù)對象,構(gòu)造函數(shù),new

this

this對于很多人來說是混雜不清的概念,但是想要弄清楚原型鏈我們必須了解什么是this

首先,this只能在存在與函數(shù)中

其次,this其實是當(dāng)前函數(shù)所在上下文環(huán)境,再簡單一點也可以理解為this返回一個當(dāng)前函數(shù)所在的對象,也就是說想要知道this是什么我們只需要關(guān)注是誰調(diào)用了this所在的函數(shù)就可以了

如下邊的代碼zhao.sayName()是zhao調(diào)用的sayName函數(shù)所以sayName中的this自然指的就是zhao這個對象,而下方 var liName = zhao.sayName語句是將sayName這個函數(shù)賦值給liName,調(diào)用liName就相當(dāng)于在最頂層也就是window下直接調(diào)用sayName,this的指向自然就是window這個最頂層對象

    var name = "Li"
    var zhao = {
        name: "Zhao",
        sayName: function () {
            console.log(this.name);
        }
    }
    zhao.sayName() // Zhao
    var liName = zhao.sayName;
    liName() // Li
普通對象與函數(shù)對象

JavaScript中一切都可以看作對象,但是實際上對象也是有區(qū)別的,對象分為普通對象函數(shù)對象

    // 普通對象
    var o1 = {}
    var o2 = new Object()
    var o3 = new f1()
    // 函數(shù)對象
    function f1(){}
    var f2 = function(){}
    var f3 = new Function()

    console.log(typeof f1); //function
    console.log(f1.prototype); //true
    console.log(typeof f2); //function
    console.log(f2.prototype); //true
    console.log(typeof f3); //function
    console.log(f3.prototype); //true

    console.log(typeof o1); //object
    console.log(o1.prototype); //undefined
    console.log(typeof o2); //object
    console.log(o2.prototype); //undefined
    console.log(typeof o3); //object
    console.log(o3.prototype); //undefined

凡是通過function構(gòu)建的對象都是函數(shù)對象,并且只有函數(shù)對象才有prototype屬性,普通對象沒有

prototype 原型

prototype又是什么呢?

當(dāng)我們創(chuàng)建函數(shù)的時候,編譯器會自動為該函數(shù)創(chuàng)建一個prototype屬性,這和屬性指向一個包含constructor屬性的對象,而這個屬性又默認(rèn)指回原函數(shù),讀起來有點繞對吧,大概是這樣的

    function Person() {
    //    prototype = {
    //        constructor: Person,
    //    }
    }

每個函數(shù)對象都有一個prototype(原型)屬性,在我看來prototype屬性的意義:

創(chuàng)建對象的模板

公開的共享空間

這兩點等學(xué)習(xí)了下邊new命令你就會明白了

constructor 構(gòu)造函數(shù)

函數(shù)對象的一種用法就是構(gòu)造函數(shù),通過構(gòu)造函數(shù)可以構(gòu)建一個函數(shù)對象的實例(普通對象)

    function Person(name, age ){
        this.name = name;
        this.age = age;
        this.sayHello = function(){
            console.log(`Hello! my name is ${this.name}`);
        };
    }

    var person1 = new Person("kidder", 28);
    person1.sayHello(); // Hello! my name is kidder
    console.log(person1.constructor); //[Function:Person]

按照慣例,構(gòu)造函數(shù)的命名以大寫字母開頭,非構(gòu)造函數(shù)以小寫字母開頭,通過構(gòu)造函數(shù)構(gòu)造的普通對象都會有一個constructor(構(gòu)造函數(shù))屬性,該屬性指向構(gòu)造該對象的構(gòu)造函數(shù)

new命令 new命令的工作機(jī)制

創(chuàng)建一個空對象作為要返回對象的實例

將這個空對象的原型(__ proto __)指向構(gòu)造函數(shù)的prototype屬性

將這個空對象賦值給構(gòu)造函數(shù)內(nèi)部的this

執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼

原型鏈

下面我們來看看構(gòu)造函數(shù)構(gòu)建一個普通對象的時候發(fā)生了什么

    var Person = function (name) {
        this.name = name;
        this.age = 18;
    };
    Person.prototype.sayHello = function(){
        console.log(`Hello! my name is ${this.name}`);
    };
    var li = new Person("Li");
    console.log(li.name); // Li
    console.log(li.age); // 18
    li.sayHello(); // Hello! my name is Li

創(chuàng)建一個空對象作為要返回對象的實例

    {}

將這個空對象的原型(__ proto __)指向構(gòu)造函數(shù)的prototype屬性

    {
        __proto__:Person.prototype;
    }

將這個空對象賦值給構(gòu)造函數(shù)內(nèi)部的this

    this = {
        __proto__:Person.prototype;
    }

執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼

   this = {
        __proto__:Person.prototype;
       name: "Li";
       age: 18;
   }

所以li這個對象中只有name和age兩個屬性,為什么li.sayHello()會輸出Hello! my name is Li呢?

這就是原型鏈,當(dāng)給定的屬性在當(dāng)前對象中找不到的情況下,會沿著__proto__這個屬性一直向?qū)ο蟮纳嫌稳ふ?,直到__proto__這個屬性指向null為止,如果找到指定屬性,查找就會被截斷,停止

上面這張圖是整個JavaScript的原型鏈體系,為了讓這張圖更直觀所以我將構(gòu)造函數(shù)的prototype屬性多帶帶提了出來,恩,其實畫在構(gòu)造函數(shù)內(nèi)部也可,但同時因為對象是引用類型,所以這樣畫也沒毛病吧

_ proto _ 和 prototype

這兩個屬性經(jīng)常會被我們混淆,那么我們回過頭再來總結(jié)一下

prototype:只有函數(shù)對象才具有的屬性,它用來存放的是構(gòu)造函數(shù)希望構(gòu)建的實例具有的共享的屬性和方法,主要用于構(gòu)造函數(shù)的實例化

_proto_ : 所有對象都具有的屬性,它指向的是當(dāng)前對象在原型鏈上的上級對象,主要作用是讓編譯器在由__proto__這個屬性構(gòu)成的原型鏈上查找特定的屬性和方法

補(bǔ)充 prototype的共享屬性
    var Person = function (name) {
        this.name = name;
        this.age = 18;
    };
    Person.prototype.sayHello = function(){
        console.log(`Hello! my name is ${this.name}`);
    };
    var li = new Person("Li");

    var Person1 = function () {
    };
    Person.prototype.name = "Li"
    Person.prototype.age = 18
    Person.prototype.sayHello = function(){
        console.log(`Hello! my name is ${this.name}`);
    };

    var Li = new Person1();

關(guān)于Person和Person1兩種構(gòu)造函數(shù)的寫法有什么不同呢?

一般來說寫在prototype原型對象中的屬性和方法都是公用的,也就是說寫在構(gòu)造函數(shù)中的屬性在構(gòu)建普通對象的時候,都會在新對象中重新定義,也就是從內(nèi)存的角度來說又會多占用一些內(nèi)存空間,所以我們將構(gòu)造函數(shù)的所有屬性和方法都寫在prototype原型中不好嗎?

但是原型函數(shù)也是有缺點的:

不夠靈活

    var Person = function () {
    };
    Person.prototype.name = "Li"
    Person.prototype.age = 18
    Person.prototype.sayHello = function(){
        console.log(`Hello! my name is ${this.name}`);
    };

    var li = new Person();
    var zhao = new Person();

這種方式構(gòu)造的所有對象都是一個模板,雖然我們也可以在當(dāng)前對象下進(jìn)行修改,但這樣一點也不優(yōu)雅,不規(guī)整,而且從某種意義上來說也是對內(nèi)存的浪費

對于引用類型的修改會被全部共享

    var Person = function () {
    };
    Person.prototype.name = "Li"
    Person.prototype.age = 18
    Person.prototype.friends = ["ZhangSan", "LiSi"]
    Person.prototype.sayHello = function(){
        console.log(`Hello! my name is ${this.name}`);
    };

    var li = new Person();
    var zhao = new Person();
     li.friends.push("WangWu");
    console.log(zhao.friends); // [ "ZhangSan", "LiSi", "WangWu" ]

在JavaScript中,基本類型的修改可以明確的通過創(chuàng)建或修改在當(dāng)前對象下的屬性對原型鏈進(jìn)行截斷,但是像數(shù)組,對象這種引用類型的值雖然也可以通過在當(dāng)前對象中創(chuàng)建該屬性來對原型鏈進(jìn)行截斷,但是一不注意就可能會出現(xiàn)上面這種情況直接對原型進(jìn)行了修改

構(gòu)造函數(shù)與原型相結(jié)合

所以,用構(gòu)造函數(shù)來定義實例屬性,用原型定義方法和共享的屬性,這樣寫就比較優(yōu)雅了

    function Person(name, age){
        this.name = name;
        this.age = age;
        this.friends = ["ZhangSan", "LiSi"];
    }
    Person.prototype.sayHello = function(){
        console.log(`Hello! my name is ${this.name},${this.age}歲了`);
    };
    var li = new Person("li", 18);
    var zhao = new Person("zhao", 16);
    li.sayHello();
    // Hello! my name is li, 18歲了
    zhao.sayHello();
    // Hello! my name is zhao,16歲了
    li.friends.push("WangWu");
    console.log(zhao.friends);
    // [ "ZhangSan", "LiSi" ]
創(chuàng)建對象的幾種方式

構(gòu)造函數(shù)方式

法一用構(gòu)造函數(shù)構(gòu)造一個新對象

    var A = function () { };
    var a = new A();
    console.log(a.constructor); // [Function:A]
    console.log(a.__proto__ === A.prototype); //true

字面量方式

法二的本質(zhì)來說和法一是一樣的,就是隱式調(diào)用原生構(gòu)造函數(shù)Object來構(gòu)造新對象

    var a = {};
    // var a = new Object();
    console.log(a.constructor); // [Function:Object]
    console.log(a.__proto__ === Object.prototype); //true

create方式

法三Object.create是以一個普通對象為模板創(chuàng)建一個新對象

    var a1 = {a:1}
    var a2 = Object.create(a1);
    console.log(a2.constructor); // [Function:Object]
    console.log(a2.__proto__ === a1);// true
    console.log(a2.__proto__ === a1.prototype); //false

所以除了Object.create創(chuàng)建對象的方式,可以說:__ proto __ === constructor.prototype;

constructor

前面我們說道prototype的時候進(jìn)行原型屬性的賦值的時候,采用的是逐項賦值,那么當(dāng)我直接將對象賦值給prototype屬性的時候會發(fā)生什么呢?

    function Person() { }
    Person.prototype = {
        name : "Li",
        age : 18,
        sayHello : function () {
            console.log(`Hello! my name is ${this.name},${this.age}歲了`);
        }
    };
    var li = new Person();
    console.log(li instanceof Object);        // true
    console.log(li instanceof Person);        // true
    console.log(li.constructor === Person);    // false
    console.log(li.constructor === Object);    // true
    console.log(Person.prototype.constructor);  // Object

這時候我們就發(fā)現(xiàn)我們構(gòu)建的li對象的constructor不再指向它的構(gòu)造函數(shù)Person,而是指向了Object,并且Person原型Person.prototype的constructor指向也指向了Object,這是什么原因呢?

其實,根源出現(xiàn)在Person.prototype上,上邊我們提到過,其實我們在寫構(gòu)造函數(shù)的時候?qū)嶋H上是這樣的

    function Person() {
    // prototype = {
    //    constructor : Person
    // }
    }

當(dāng)我們構(gòu)建Person構(gòu)造函數(shù)的時候,編譯器會自動生成一個帶有指向Person的constructor屬性的對象,并把這個對象賦值給Person.prototype,我們又知道js中對象是引用類型,當(dāng)我們使用Person.prototype.name=...的時候?qū)嶋H上是對這個對象的修改,而使用Person.prototype={...}實際上是將這個屬性原本的指針指向了另一個新創(chuàng)建的對象而不是原來編譯器自動創(chuàng)建的那個:

而li的constructor屬性自然是繼承自Person.prototype,所以constructor自然也就跟著改變了,如果在編程的過程中constructor這個屬性很重要的話可以通過下面的方式

    function Person() { }
    Person.prototype = {
        constructor:Person
        name : "Li",
        age : 18,
        sayHello : function () {
            console.log(`Hello! my name is ${this.name},${this.age}歲了`);
        }
    };
    
    var li = new Person();
    console.log(li instanceof Object);        // true
    console.log(li instanceof Person);        // true
    console.log(li.constructor === Person);    // true
    console.log(li.constructor === Object);    // false
    console.log(Person.prototype.constructor);  // Person
結(jié)語:

參考:《JavaScript高級程序設(shè)計》

這是我對JS原型鏈部分的總結(jié)與思考,也是我寫的第一篇這么正式的技術(shù)文檔,如有紕漏之處,歡迎大家批評指正

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/102330.html

相關(guān)文章

  • 關(guān)于javascript的原型原型,看我就夠了(一)

    摘要:要用作原型的對象。函數(shù)對象可以創(chuàng)建普通對象,這個我們上面講過了回顧一下這是一個自定義構(gòu)造函數(shù)普通對象沒法創(chuàng)建函數(shù)對象,凡是通過創(chuàng)建的對象都是函數(shù)對象,其他都是普通對象通常通過創(chuàng)建,可以通過來判斷。 關(guān)于js的原型和原型鏈,有人覺得這是很頭疼的一塊知識點,其實不然,它很基礎(chǔ),不信,往下看要了解原型和原型鏈,我們得先從對象說起 創(chuàng)建對象 創(chuàng)建對象的三種方式: 對象直接量 通過對象直接量創(chuàng)建...

    MoAir 評論0 收藏0
  • 前端面試回顧(1)---javascript的面向?qū)ο?/b>

    摘要:每個類有三部分構(gòu)成第一部分是構(gòu)造函數(shù)內(nèi),供實例對象化復(fù)制用。第二部分是構(gòu)造函數(shù)外,直接通過點語法添加,供類使用,實例化對象訪問不到。組合繼承還有一個要注意的地方在代碼處,將子類原型的屬性指向子類的構(gòu)造函數(shù)。 前言 前一陣面試,過程中發(fā)現(xiàn)問到一些很基礎(chǔ)的問題時候,自己并不能很流暢的回答出來。或者遇到一些基礎(chǔ)知識的應(yīng)用,由于對這些點理解的不是很深入,拿著筆居然什么都寫不出來,于是有了回顧一...

    animabear 評論0 收藏0
  • JS面向?qū)ο蟮某绦蛟O(shè)計之繼承的實現(xiàn) - 原型

    摘要:簡單回顧一下構(gòu)造函數(shù)原型和實例對象之間的關(guān)系每個構(gòu)造函數(shù)都有一個原型對象。找到生成構(gòu)造函數(shù)的原型對象的構(gòu)造函數(shù),搜索其原型對象,找到了。 JS面向?qū)ο蟮某绦蛟O(shè)計之繼承的實現(xiàn) - 原型鏈 前言:最近在細(xì)讀Javascript高級程序設(shè)計,對于我而言,中文版,書中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內(nèi)容引用自《JavaS...

    zhaochunqi 評論0 收藏0
  • js溫故而知新7(面向?qū)ο缶幊?——學(xué)習(xí)廖雪峰的js教程

    摘要:不區(qū)分類和實例的概念,而是通過原型來實現(xiàn)面向?qū)ο缶幊?。新?chuàng)建的的原型鏈?zhǔn)且簿褪钦f,的原型指向函數(shù)的原型。最后,創(chuàng)建一個對象代碼和前面章節(jié)完全一樣小明繼承用定義對象的另一個巨大的好處是繼承更方便了。 JavaScript不區(qū)分類和實例的概念,而是通過原型(prototype)來實現(xiàn)面向?qū)ο缶幊獭?原型是指當(dāng)我們想要創(chuàng)建xiaoming這個具體的學(xué)生時,我們并沒有一個Student類型可用...

    Jaden 評論0 收藏0
  • 關(guān)于原型中constructor、prototype及__proto__的問題

    摘要:最近看到一個關(guān)于原型鏈的問題,回顧一下原型鏈的知識點。說說為什么為什么是。首先不是自身的屬性,而是原型鏈上的,即的原型中。類似通過這樣來找到的值。,不是復(fù)制了對象,而是把指向了,所以對的修改會影響到的值。再看看這張圖一切都明朗了。 最近看到一個關(guān)于原型鏈的問題,回顧一下原型鏈的知識點。 function person(name) { this.name = name; ...

    alanoddsoff 評論0 收藏0

發(fā)表評論

0條評論

call_me_R

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<